Просмотр исходного кода

Remove some C++17 workarounds now we build in C++20 mode. (#3653)

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Richard Smith 2 лет назад
Родитель
Сommit
3fa70de101

+ 3 - 5
toolchain/check/handle_binding_pattern.cpp

@@ -13,7 +13,6 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node,
                              bool is_generic) -> bool {
   auto [type_node, parsed_type_id] =
       context.node_stack().PopExprWithParseNode();
-  auto type_node_copy = type_node;
   auto cast_type_id = ExprAsType(context, type_node, parsed_type_id);
 
   // TODO: Handle `_` bindings.
@@ -22,8 +21,7 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node,
   auto [name_node, name_id] = context.node_stack().PopNameWithParseNode();
 
   // Create the appropriate kind of binding for this pattern.
-  auto make_bind_name = [&, name_node = name_node, name_id = name_id](
-                            SemIR::TypeId type_id,
+  auto make_bind_name = [&](SemIR::TypeId type_id,
                             SemIR::InstId value_id) -> SemIR::ParseNodeAndInst {
     // TODO: Eventually the name will need to support associations with other
     // scopes, but right now we don't support qualified names here.
@@ -73,7 +71,7 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node,
                           "{0} has incomplete type `{1}`.", llvm::StringLiteral,
                           std::string);
         return context.emitter().Build(
-            type_node_copy, IncompleteTypeInVarDecl,
+            type_node, IncompleteTypeInVarDecl,
             enclosing_class_decl ? llvm::StringLiteral("Field")
                                  : llvm::StringLiteral("Variable"),
             context.sem_ir().StringifyType(cast_type_id));
@@ -139,7 +137,7 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node,
                           "`let` binding has incomplete type `{0}`.",
                           std::string);
         return context.emitter().Build(
-            type_node_copy, IncompleteTypeInLetDecl,
+            type_node, IncompleteTypeInLetDecl,
             context.sem_ir().StringifyType(cast_type_id));
       });
       // Create the instruction, but don't add it to a block until after we've

+ 1 - 1
toolchain/sem_ir/formatter.cpp

@@ -832,7 +832,7 @@ class Formatter {
   template <typename InstT>
   auto FormatInstructionRHS(InstT inst) -> void {
     // By default, an instruction has a comma-separated argument list.
-    using Info = InstLikeTypeInfo<InstT>;
+    using Info = Internal::InstLikeTypeInfo<InstT>;
     if constexpr (Info::NumArgs == 2) {
       FormatArgs(Info::template Get<0>(inst), Info::template Get<1>(inst));
     } else if constexpr (Info::NumArgs == 1) {

+ 3 - 3
toolchain/sem_ir/inst.cpp

@@ -20,9 +20,9 @@ auto Inst::Print(llvm::raw_ostream& out) const -> void {
   };
 
   switch (kind_) {
-#define CARBON_SEM_IR_INST_KIND(Name)     \
-  case Name::Kind:                        \
-    print_args(InstLikeTypeInfo<Name>()); \
+#define CARBON_SEM_IR_INST_KIND(Name)               \
+  case Name::Kind:                                  \
+    print_args(Internal::InstLikeTypeInfo<Name>()); \
     break;
 #include "toolchain/sem_ir/inst_kind.def"
   }

+ 33 - 29
toolchain/sem_ir/inst.h

@@ -5,8 +5,8 @@
 #ifndef CARBON_TOOLCHAIN_SEM_IR_INST_H_
 #define CARBON_TOOLCHAIN_SEM_IR_INST_H_
 
+#include <concepts>
 #include <cstdint>
-#include <type_traits>
 
 #include "common/check.h"
 #include "common/ostream.h"
@@ -18,20 +18,18 @@
 
 namespace Carbon::SemIR {
 
+// InstLikeTypeInfo is an implementation detail, and not public API.
+namespace Internal {
+
 // Information about an instruction-like type, which is a type that an Inst can
 // be converted to and from. The `Enabled` parameter is used to check
 // requirements on the type in the specializations of this template.
-template <typename InstLikeType, bool Enabled = true>
+template <typename InstLikeType>
 struct InstLikeTypeInfo;
 
 // A helper base class for instruction-like types that are structs.
 template <typename InstLikeType>
 struct InstLikeTypeInfoBase {
-  // The derived class. Useful to allow SFINAE on whether a type is
-  // instruction-like: `typename InstLikeTypeInfo<T>::Self` is valid only if `T`
-  // is instruction-like.
-  using Self = InstLikeTypeInfo<InstLikeType>;
-
   // A corresponding std::tuple<...> type.
   using Tuple =
       decltype(StructReflection::AsTuple(std::declval<InstLikeType>()));
@@ -54,13 +52,10 @@ struct InstLikeTypeInfoBase {
 
 // A particular type of instruction is instruction-like.
 template <typename TypedInst>
-struct InstLikeTypeInfo<
-    TypedInst,
-    static_cast<bool>(
-        std::is_same_v<const InstKind::Definition<
-                           typename decltype(TypedInst::Kind)::TypedNodeId>,
-                       decltype(TypedInst::Kind)>)>
-    : InstLikeTypeInfoBase<TypedInst> {
+  requires std::same_as<const InstKind::Definition<
+                            typename decltype(TypedInst::Kind)::TypedNodeId>,
+                        decltype(TypedInst::Kind)>
+struct InstLikeTypeInfo<TypedInst> : InstLikeTypeInfoBase<TypedInst> {
   static_assert(!HasKindMemberAsField<TypedInst>,
                 "Instruction type should not have a kind field");
   static auto GetKind(TypedInst /*inst*/) -> InstKind {
@@ -73,10 +68,8 @@ struct InstLikeTypeInfo<
 
 // An instruction category is instruction-like.
 template <typename InstCat>
-struct InstLikeTypeInfo<
-    InstCat, static_cast<bool>(
-                 std::is_same_v<const InstKind&, decltype(InstCat::Kinds[0])>)>
-    : InstLikeTypeInfoBase<InstCat> {
+  requires std::same_as<const InstKind&, decltype(InstCat::Kinds[0])>
+struct InstLikeTypeInfo<InstCat> : InstLikeTypeInfoBase<InstCat> {
   static_assert(HasKindMemberAsField<InstCat>,
                 "Instruction category should have a kind field");
   static auto GetKind(InstCat cat) -> InstKind { return cat.kind; }
@@ -102,6 +95,12 @@ struct InstLikeTypeInfo<
   }
 };
 
+// A type is InstLike if InstLikeTypeInfo is defined for it.
+template <typename T>
+concept InstLikeType = requires { sizeof(InstLikeTypeInfo<T>); };
+
+}  // namespace Internal
+
 // A type-erased representation of a SemIR instruction, that may be constructed
 // from the specific kinds of instruction defined in `typed_insts.h`. This
 // provides access to common fields present on most or all kinds of
@@ -123,8 +122,8 @@ struct InstLikeTypeInfo<
 //   data where the instruction's kind is not known.
 class Inst : public Printable<Inst> {
  public:
-  template <typename TypedInst,
-            typename Info = typename InstLikeTypeInfo<TypedInst>::Self>
+  template <typename TypedInst>
+    requires Internal::InstLikeType<TypedInst>
   // NOLINTNEXTLINE(google-explicit-constructor)
   Inst(TypedInst typed_inst)
       // kind_ is always overwritten below.
@@ -132,14 +131,15 @@ class Inst : public Printable<Inst> {
         type_id_(TypeId::Invalid),
         arg0_(InstId::InvalidIndex),
         arg1_(InstId::InvalidIndex) {
-    if constexpr (HasKindMemberAsField<TypedInst>) {
+    if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
       kind_ = typed_inst.kind;
     } else {
       kind_ = TypedInst::Kind;
     }
-    if constexpr (HasTypeIdMember<TypedInst>) {
+    if constexpr (Internal::HasTypeIdMember<TypedInst>) {
       type_id_ = typed_inst.type_id;
     }
+    using Info = Internal::InstLikeTypeInfo<TypedInst>;
     if constexpr (Info::NumArgs > 0) {
       arg0_ = ToRaw(Info::template Get<0>(typed_inst));
     }
@@ -149,19 +149,22 @@ class Inst : public Printable<Inst> {
   }
 
   // Returns whether this instruction has the specified type.
-  template <typename TypedInst, typename Info = InstLikeTypeInfo<TypedInst>>
+  template <typename TypedInst>
+    requires Internal::InstLikeType<TypedInst>
   auto Is() const -> bool {
-    return Info::IsKind(kind());
+    return Internal::InstLikeTypeInfo<TypedInst>::IsKind(kind());
   }
 
   // Casts this instruction to the given typed instruction, which must match the
   // instruction's kind, and returns the typed instruction.
-  template <typename TypedInst, typename Info = InstLikeTypeInfo<TypedInst>>
+  template <typename TypedInst>
+    requires Internal::InstLikeType<TypedInst>
   auto As() const -> TypedInst {
+    using Info = Internal::InstLikeTypeInfo<TypedInst>;
     CARBON_CHECK(Is<TypedInst>()) << "Casting inst of kind " << kind()
                                   << " to wrong kind " << Info::DebugName();
     auto build_with_type_id_onwards = [&](auto... type_id_onwards) {
-      if constexpr (HasKindMemberAsField<TypedInst>) {
+      if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
         return TypedInst{kind(), type_id_onwards...};
       } else {
         return TypedInst{type_id_onwards...};
@@ -169,7 +172,7 @@ class Inst : public Printable<Inst> {
     };
 
     auto build_with_args = [&](auto... args) {
-      if constexpr (HasTypeIdMember<TypedInst>) {
+      if constexpr (Internal::HasTypeIdMember<TypedInst>) {
         return build_with_type_id_onwards(type_id(), args...);
       } else {
         return build_with_type_id_onwards(args...);
@@ -191,6 +194,7 @@ class Inst : public Printable<Inst> {
   // If this instruction is the given kind, returns a typed instruction,
   // otherwise returns nullopt.
   template <typename TypedInst>
+    requires Internal::InstLikeType<TypedInst>
   auto TryAs() const -> std::optional<TypedInst> {
     if (Is<TypedInst>()) {
       return As<TypedInst>();
@@ -253,8 +257,8 @@ class Inst : public Printable<Inst> {
 static_assert(sizeof(Inst) == 16, "Unexpected Inst size");
 
 // Instruction-like types can be printed by converting them to instructions.
-template <typename TypedInst,
-          typename = typename InstLikeTypeInfo<TypedInst>::Self>
+template <typename TypedInst>
+  requires Internal::InstLikeType<TypedInst>
 inline auto operator<<(llvm::raw_ostream& out, TypedInst inst)
     -> llvm::raw_ostream& {
   Inst(inst).Print(out);

+ 3 - 2
toolchain/sem_ir/inst_kind.cpp

@@ -23,8 +23,9 @@ auto InstKind::ir_name() const -> llvm::StringLiteral {
 
 auto InstKind::value_kind() const -> InstValueKind {
   static constexpr InstValueKind Table[] = {
-#define CARBON_SEM_IR_INST_KIND(Name) \
-  HasTypeIdMember<SemIR::Name> ? InstValueKind::Typed : InstValueKind::None,
+#define CARBON_SEM_IR_INST_KIND(Name)                           \
+  Internal::HasTypeIdMember<SemIR::Name> ? InstValueKind::Typed \
+                                         : InstValueKind::None,
 #include "toolchain/sem_ir/inst_kind.def"
   };
   return Table[AsInt()];

+ 2 - 2
toolchain/sem_ir/inst_profile.cpp

@@ -72,11 +72,11 @@ static auto RealProfileArgFunction(llvm::FoldingSetNodeID& id,
 // namely `ProfileArgs<DefaultProfileArgFunction, DefaultProfileArgFunction>`.
 template <typename InstT, int N>
 static constexpr auto SelectProfileArgFunction() -> ProfileArgFunction* {
-  if constexpr (N >= InstLikeTypeInfo<InstT>::NumArgs) {
+  if constexpr (N >= Internal::InstLikeTypeInfo<InstT>::NumArgs) {
     // This argument is not used by this instruction; don't profile it.
     return NullProfileArgFunction;
   } else {
-    using ArgT = typename InstLikeTypeInfo<InstT>::template ArgType<N>;
+    using ArgT = Internal::InstLikeTypeInfo<InstT>::template ArgType<N>;
     if constexpr (std::is_same_v<ArgT, InstBlockId>) {
       return InstBlockProfileArgFunction;
     } else if constexpr (std::is_same_v<ArgT, TypeBlockId>) {

+ 16 - 6
toolchain/sem_ir/typed_insts.h

@@ -736,18 +736,28 @@ struct VarStorage {
   NameId name_id;
 };
 
+// These concepts are an implementation detail of the library, not public API.
+namespace Internal {
+
+// HasParseNode is true if T has an associated parse node.
+template <typename T>
+concept HasParseNode = !std::same_as<typename decltype(T::Kind)::TypedNodeId,
+                                     Parse::InvalidNodeId>;
+
 // HasKindMemberAsField<T> is true if T has a `InstKind kind` field, as opposed
 // to a `static constexpr InstKind::Definition Kind` member or no kind at all.
-template <typename T, typename KindType = InstKind T::*>
-inline constexpr bool HasKindMemberAsField = false;
 template <typename T>
-inline constexpr bool HasKindMemberAsField<T, decltype(&T::kind)> = true;
+concept HasKindMemberAsField = requires {
+  { &T::kind } -> std::same_as<InstKind T::*>;
+};
 
 // HasTypeIdMember<T> is true if T has a `TypeId type_id` field.
-template <typename T, typename TypeIdType = TypeId T::*>
-inline constexpr bool HasTypeIdMember = false;
 template <typename T>
-inline constexpr bool HasTypeIdMember<T, decltype(&T::type_id)> = true;
+concept HasTypeIdMember = requires {
+  { &T::type_id } -> std::same_as<TypeId T::*>;
+};
+
+}  // namespace Internal
 
 }  // namespace Carbon::SemIR
 

+ 4 - 3
toolchain/sem_ir/typed_insts_test.cpp

@@ -42,7 +42,7 @@ template <typename TypedInst>
 auto CommonFieldOrder() -> void {
   Inst inst = MakeInstWithNumberedFields(TypedInst::Kind);
   auto typed = inst.As<TypedInst>();
-  if constexpr (HasTypeIdMember<TypedInst>) {
+  if constexpr (Internal::HasTypeIdMember<TypedInst>) {
     EXPECT_EQ(typed.type_id, TypeId(1));
   }
 }
@@ -70,7 +70,7 @@ auto RoundTrip() -> void {
   auto typed1 = inst1.As<TypedInst>();
   Inst inst2 = typed1;
 
-  ExpectEqInsts(inst1, inst2, HasTypeIdMember<TypedInst>);
+  ExpectEqInsts(inst1, inst2, Internal::HasTypeIdMember<TypedInst>);
 
   // If the typed instruction has no padding, we should get exactly the same
   // thing if we convert back from an instruction.
@@ -119,7 +119,8 @@ auto StructLayout() -> void {
   if constexpr (std::has_unique_object_representations_v<TypedInst>) {
     auto typed =
         MakeInstWithNumberedFields(TypedInst::Kind).template As<TypedInst>();
-    StructLayoutHelper(&typed, sizeof(typed), HasTypeIdMember<TypedInst>);
+    StructLayoutHelper(&typed, sizeof(typed),
+                       Internal::HasTypeIdMember<TypedInst>);
   }
 }
 

+ 6 - 9
toolchain/sem_ir/value_stores.h

@@ -29,19 +29,16 @@ struct ParseNodeAndInst {
 
   // For the common case, support construction as:
   //   context.AddInst({parse_node, SemIR::MyInst{...}});
-  template <typename InstT, typename std::enable_if_t<!std::is_same_v<
-                                typename decltype(InstT::Kind)::TypedNodeId,
-                                Parse::InvalidNodeId>>* = nullptr>
+  template <typename InstT>
+    requires(Internal::HasParseNode<InstT>)
   // NOLINTNEXTLINE(google-explicit-constructor)
-  ParseNodeAndInst(typename decltype(InstT::Kind)::TypedNodeId parse_node,
-                   InstT inst)
+  ParseNodeAndInst(decltype(InstT::Kind)::TypedNodeId parse_node, InstT inst)
       : parse_node(parse_node), inst(inst) {}
 
   // For cases with no parse node, support construction as:
   //   context.AddInst({SemIR::MyInst{...}});
-  template <typename InstT, typename std::enable_if_t<std::is_same_v<
-                                typename decltype(InstT::Kind)::TypedNodeId,
-                                Parse::InvalidNodeId>>* = nullptr>
+  template <typename InstT>
+    requires(!Internal::HasParseNode<InstT>)
   // NOLINTNEXTLINE(google-explicit-constructor)
   ParseNodeAndInst(InstT inst)
       : parse_node(Parse::NodeId::Invalid), inst(inst) {}
@@ -379,7 +376,7 @@ class NameScopeStore {
 template <typename IdT>
 class BlockValueStore : public Yaml::Printable<BlockValueStore<IdT>> {
  public:
-  using ElementType = typename IdT::ElementType;
+  using ElementType = IdT::ElementType;
 
   explicit BlockValueStore(llvm::BumpPtrAllocator& allocator)
       : allocator_(&allocator) {}