diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index bae1802d8a53..30ad3ea98668 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/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 diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 14cd176c44de..f86145ed7482 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -832,7 +832,7 @@ class Formatter { template auto FormatInstructionRHS(InstT inst) -> void { // By default, an instruction has a comma-separated argument list. - using Info = InstLikeTypeInfo; + using Info = Internal::InstLikeTypeInfo; if constexpr (Info::NumArgs == 2) { FormatArgs(Info::template Get<0>(inst), Info::template Get<1>(inst)); } else if constexpr (Info::NumArgs == 1) { diff --git a/toolchain/sem_ir/inst.cpp b/toolchain/sem_ir/inst.cpp index b2512464d72e..6f25618bda25 100644 --- a/toolchain/sem_ir/inst.cpp +++ b/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()); \ +#define CARBON_SEM_IR_INST_KIND(Name) \ + case Name::Kind: \ + print_args(Internal::InstLikeTypeInfo()); \ break; #include "toolchain/sem_ir/inst_kind.def" } diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index 0d9759866f05..60f32d9091ce 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -5,8 +5,8 @@ #ifndef CARBON_TOOLCHAIN_SEM_IR_INST_H_ #define CARBON_TOOLCHAIN_SEM_IR_INST_H_ +#include #include -#include #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 +template struct InstLikeTypeInfo; // A helper base class for instruction-like types that are structs. template struct InstLikeTypeInfoBase { - // The derived class. Useful to allow SFINAE on whether a type is - // instruction-like: `typename InstLikeTypeInfo::Self` is valid only if `T` - // is instruction-like. - using Self = InstLikeTypeInfo; - // A corresponding std::tuple<...> type. using Tuple = decltype(StructReflection::AsTuple(std::declval())); @@ -54,13 +52,10 @@ struct InstLikeTypeInfoBase { // A particular type of instruction is instruction-like. template -struct InstLikeTypeInfo< - TypedInst, - static_cast( - std::is_same_v, - decltype(TypedInst::Kind)>)> - : InstLikeTypeInfoBase { + requires std::same_as, + decltype(TypedInst::Kind)> +struct InstLikeTypeInfo : InstLikeTypeInfoBase { static_assert(!HasKindMemberAsField, "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 -struct InstLikeTypeInfo< - InstCat, static_cast( - std::is_same_v)> - : InstLikeTypeInfoBase { + requires std::same_as +struct InstLikeTypeInfo : InstLikeTypeInfoBase { static_assert(HasKindMemberAsField, "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 +concept InstLikeType = requires { sizeof(InstLikeTypeInfo); }; + +} // 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 { public: - template ::Self> + template + requires Internal::InstLikeType // NOLINTNEXTLINE(google-explicit-constructor) Inst(TypedInst typed_inst) // kind_ is always overwritten below. @@ -132,14 +131,15 @@ class Inst : public Printable { type_id_(TypeId::Invalid), arg0_(InstId::InvalidIndex), arg1_(InstId::InvalidIndex) { - if constexpr (HasKindMemberAsField) { + if constexpr (Internal::HasKindMemberAsField) { kind_ = typed_inst.kind; } else { kind_ = TypedInst::Kind; } - if constexpr (HasTypeIdMember) { + if constexpr (Internal::HasTypeIdMember) { type_id_ = typed_inst.type_id; } + using Info = Internal::InstLikeTypeInfo; if constexpr (Info::NumArgs > 0) { arg0_ = ToRaw(Info::template Get<0>(typed_inst)); } @@ -149,19 +149,22 @@ class Inst : public Printable { } // Returns whether this instruction has the specified type. - template > + template + requires Internal::InstLikeType auto Is() const -> bool { - return Info::IsKind(kind()); + return Internal::InstLikeTypeInfo::IsKind(kind()); } // Casts this instruction to the given typed instruction, which must match the // instruction's kind, and returns the typed instruction. - template > + template + requires Internal::InstLikeType auto As() const -> TypedInst { + using Info = Internal::InstLikeTypeInfo; CARBON_CHECK(Is()) << "Casting inst of kind " << kind() << " to wrong kind " << Info::DebugName(); auto build_with_type_id_onwards = [&](auto... type_id_onwards) { - if constexpr (HasKindMemberAsField) { + if constexpr (Internal::HasKindMemberAsField) { return TypedInst{kind(), type_id_onwards...}; } else { return TypedInst{type_id_onwards...}; @@ -169,7 +172,7 @@ class Inst : public Printable { }; auto build_with_args = [&](auto... args) { - if constexpr (HasTypeIdMember) { + if constexpr (Internal::HasTypeIdMember) { 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 { // If this instruction is the given kind, returns a typed instruction, // otherwise returns nullopt. template + requires Internal::InstLikeType auto TryAs() const -> std::optional { if (Is()) { return As(); @@ -253,8 +257,8 @@ class Inst : public Printable { static_assert(sizeof(Inst) == 16, "Unexpected Inst size"); // Instruction-like types can be printed by converting them to instructions. -template ::Self> +template + requires Internal::InstLikeType inline auto operator<<(llvm::raw_ostream& out, TypedInst inst) -> llvm::raw_ostream& { Inst(inst).Print(out); diff --git a/toolchain/sem_ir/inst_kind.cpp b/toolchain/sem_ir/inst_kind.cpp index 0abebd114f84..6327a3efc892 100644 --- a/toolchain/sem_ir/inst_kind.cpp +++ b/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 ? InstValueKind::Typed : InstValueKind::None, +#define CARBON_SEM_IR_INST_KIND(Name) \ + Internal::HasTypeIdMember ? InstValueKind::Typed \ + : InstValueKind::None, #include "toolchain/sem_ir/inst_kind.def" }; return Table[AsInt()]; diff --git a/toolchain/sem_ir/inst_profile.cpp b/toolchain/sem_ir/inst_profile.cpp index 2ac034ed7a4f..a381a7ecfd24 100644 --- a/toolchain/sem_ir/inst_profile.cpp +++ b/toolchain/sem_ir/inst_profile.cpp @@ -72,11 +72,11 @@ static auto RealProfileArgFunction(llvm::FoldingSetNodeID& id, // namely `ProfileArgs`. template static constexpr auto SelectProfileArgFunction() -> ProfileArgFunction* { - if constexpr (N >= InstLikeTypeInfo::NumArgs) { + if constexpr (N >= Internal::InstLikeTypeInfo::NumArgs) { // This argument is not used by this instruction; don't profile it. return NullProfileArgFunction; } else { - using ArgT = typename InstLikeTypeInfo::template ArgType; + using ArgT = Internal::InstLikeTypeInfo::template ArgType; if constexpr (std::is_same_v) { return InstBlockProfileArgFunction; } else if constexpr (std::is_same_v) { diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index dfec41fc2aa1..67bc11221158 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/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 +concept HasParseNode = !std::same_as; + // HasKindMemberAsField 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 -inline constexpr bool HasKindMemberAsField = false; template -inline constexpr bool HasKindMemberAsField = true; +concept HasKindMemberAsField = requires { + { &T::kind } -> std::same_as; +}; // HasTypeIdMember is true if T has a `TypeId type_id` field. -template -inline constexpr bool HasTypeIdMember = false; template -inline constexpr bool HasTypeIdMember = true; +concept HasTypeIdMember = requires { + { &T::type_id } -> std::same_as; +}; + +} // namespace Internal } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/typed_insts_test.cpp b/toolchain/sem_ir/typed_insts_test.cpp index 45f10be127cd..bc2c5bf121f0 100644 --- a/toolchain/sem_ir/typed_insts_test.cpp +++ b/toolchain/sem_ir/typed_insts_test.cpp @@ -42,7 +42,7 @@ template auto CommonFieldOrder() -> void { Inst inst = MakeInstWithNumberedFields(TypedInst::Kind); auto typed = inst.As(); - if constexpr (HasTypeIdMember) { + if constexpr (Internal::HasTypeIdMember) { EXPECT_EQ(typed.type_id, TypeId(1)); } } @@ -70,7 +70,7 @@ auto RoundTrip() -> void { auto typed1 = inst1.As(); Inst inst2 = typed1; - ExpectEqInsts(inst1, inst2, HasTypeIdMember); + ExpectEqInsts(inst1, inst2, Internal::HasTypeIdMember); // 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) { auto typed = MakeInstWithNumberedFields(TypedInst::Kind).template As(); - StructLayoutHelper(&typed, sizeof(typed), HasTypeIdMember); + StructLayoutHelper(&typed, sizeof(typed), + Internal::HasTypeIdMember); } } diff --git a/toolchain/sem_ir/value_stores.h b/toolchain/sem_ir/value_stores.h index db909e03ae05..754528ee4c93 100644 --- a/toolchain/sem_ir/value_stores.h +++ b/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 >* = nullptr> + template + requires(Internal::HasParseNode) // 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 >* = nullptr> + template + requires(!Internal::HasParseNode) // NOLINTNEXTLINE(google-explicit-constructor) ParseNodeAndInst(InstT inst) : parse_node(Parse::NodeId::Invalid), inst(inst) {} @@ -379,7 +376,7 @@ class NameScopeStore { template class BlockValueStore : public Yaml::Printable> { public: - using ElementType = typename IdT::ElementType; + using ElementType = IdT::ElementType; explicit BlockValueStore(llvm::BumpPtrAllocator& allocator) : allocator_(&allocator) {}