From daebbf32fa49c7c0964dbfb446a71f0f2d3b2182 Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Wed, 6 May 2026 17:13:52 -0700 Subject: [PATCH] Restructure `ArgAndKind` as a type-safe generic ID. (#7172) The key changes here are: - Relocating and renaming it to align with `IdKind` (and relocating `ToRaw` and `FromRaw` to follow it). - Adding a `Dispatch` method that provides a generic overload-based API for expressing per-ID-kind dispatch, and rewriting existing code to use it. Note in particular that using overloads instead of switch cases makes it possible to generically handle all specializations of a templated ID type, e.g. `SomeIdType` for all `T`. We have no such templated ID types yet, but I'm introducing one in a follow-up PR that needs this capability. --- toolchain/check/action.cpp | 48 ++++--- toolchain/check/deduce.cpp | 2 +- toolchain/check/eval.cpp | 172 +++++++++++------------- toolchain/check/subst.cpp | 4 +- toolchain/sem_ir/expr_info.cpp | 2 +- toolchain/sem_ir/formatter.cpp | 12 +- toolchain/sem_ir/formatter.h | 29 +--- toolchain/sem_ir/id_kind.h | 110 ++++++++++++++- toolchain/sem_ir/inst.h | 53 +------- toolchain/sem_ir/inst_fingerprinter.cpp | 22 +-- 10 files changed, 236 insertions(+), 218 deletions(-) diff --git a/toolchain/check/action.cpp b/toolchain/check/action.cpp index 4a25a76d607e..94cd9f50e763 100644 --- a/toolchain/check/action.cpp +++ b/toolchain/check/action.cpp @@ -90,6 +90,13 @@ auto OperandDependence(Context& context, SemIR::InstId inst_id) OperandDependence(context, context.constant_values().Get(inst_id))); } +static auto OperandDependence(Context& context, SemIR::MetaInstId inst_id) + -> SemIR::ConstantDependence { + // A meta-instruction operand makes the instruction dependent if its type or + // constant value is dependent. + return OperandDependence(context, SemIR::InstId{inst_id}); +} + auto OperandDependence(Context& context, SemIR::TypeInstId inst_id) -> SemIR::ConstantDependence { // An instruction operand makes the instruction dependent if its type or @@ -98,30 +105,27 @@ auto OperandDependence(Context& context, SemIR::TypeInstId inst_id) return OperandDependence(context, context.constant_values().Get(inst_id)); } -static auto OperandDependence(Context& context, SemIR::Inst::ArgAndKind arg) +template + requires SemIR::Internal::IsIdKindType && + SameAsOneOf +static auto OperandDependence(Context& /*context*/, IdT /*id*/) -> SemIR::ConstantDependence { - CARBON_KIND_SWITCH(arg) { - case CARBON_KIND(SemIR::InstId inst_id): { - return OperandDependence(context, inst_id); - } + return SemIR::ConstantDependence::None; +} - case CARBON_KIND(SemIR::MetaInstId inst_id): { - return OperandDependence(context, inst_id); - } +template + requires SemIR::Internal::IsIdKindType +static auto OperandDependence(Context& /*context*/, IdT /*id*/) + -> SemIR::ConstantDependence { + // TODO: Properly handle different argument kinds. + CARBON_FATAL("Unexpected argument kind for action: {}", IdT::Label); +} - case CARBON_KIND(SemIR::TypeInstId inst_id): { - return OperandDependence(context, inst_id); - } - - case SemIR::IdKind::None: - case SemIR::IdKind::For: - case SemIR::IdKind::For: - return SemIR::ConstantDependence::None; - - default: - // TODO: Properly handle different argument kinds. - CARBON_FATAL("Unexpected argument kind for action"); - } +static auto OperandDependence(Context& context, SemIR::IdAndKind arg) + -> SemIR::ConstantDependence { + return arg.Dispatch( + [&](auto id) { return OperandDependence(context, id); }); } auto ActionIsPerformable(Context& context, SemIR::Inst action_inst) -> bool { @@ -208,7 +212,7 @@ static auto AddDependentActionSpliceImpl(Context& context, // their concrete values, so that the action doesn't need to know which specific // it is operating on. static auto RefineOperand(Context& context, SemIR::LocId loc_id, - SemIR::Inst::ArgAndKind arg) -> int32_t { + SemIR::IdAndKind arg) -> int32_t { if (auto inst_id = arg.TryAs()) { auto inst = context.insts().Get(*inst_id); if (inst.Is()) { diff --git a/toolchain/check/deduce.cpp b/toolchain/check/deduce.cpp index 3e42ddf5f52b..b0e2365f7f21 100644 --- a/toolchain/check/deduce.cpp +++ b/toolchain/check/deduce.cpp @@ -110,7 +110,7 @@ class DeductionWorklist { } // Adds a (param, arg) pair for an instruction argument, given its kind. - auto AddInstArg(SemIR::Inst::ArgAndKind param, int32_t arg) -> void { + auto AddInstArg(SemIR::IdAndKind param, int32_t arg) -> void { CARBON_KIND_SWITCH(param) { case SemIR::IdKind::None: case SemIR::IdKind::For: diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 69052e4a1123..8cc076ea43fc 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -877,43 +877,25 @@ static constexpr bool HasGetConstantValueOverload = requires { AcceptIdT>(GetConstantValue); }; -using ArgHandlerFnT = auto(EvalContext& context, int32_t arg, Phase* phase) - -> int32_t; - -// Returns the arg handler for an `IdKind`. -template -static auto GetArgHandlerFn(TypeEnum id_kind) -> ArgHandlerFnT* { - static constexpr std::array Table = - { - [](EvalContext& eval_context, int32_t arg, Phase* phase) -> int32_t { - auto id = SemIR::Inst::FromRaw(arg); - if constexpr (HasGetConstantValueOverload) { - // If we have a custom `GetConstantValue` overload, call it. - return SemIR::Inst::ToRaw( - GetConstantValue(eval_context, id, phase)); - } else { - // Otherwise, we assume the value is already constant. - return arg; - } - }..., - // Invalid and None handling (ordering-sensitive). - [](auto...) -> int32_t { CARBON_FATAL("Unexpected invalid IdKind"); }, - [](EvalContext& /*context*/, int32_t arg, - Phase* /*phase*/) -> int32_t { return arg; }, - }; - return Table[id_kind.ToIndex()]; -} - // Given the stored value `arg` of an instruction field and its corresponding // kind `kind`, returns the constant value to use for that field, if it has a // constant phase. `*phase` is updated to include the new constant value. If // the resulting phase is not constant, the returned value is not useful and // will typically be `NoneIndex`. static auto GetConstantValueForArg(EvalContext& eval_context, - SemIR::Inst::ArgAndKind arg_and_kind, - Phase* phase) -> int32_t { - return GetArgHandlerFn(arg_and_kind.kind())(eval_context, - arg_and_kind.value(), phase); + SemIR::IdAndKind arg_and_kind, Phase* phase) + -> int32_t { + return arg_and_kind.Dispatch([&](IdT id) -> int32_t { + if constexpr (HasGetConstantValueOverload) { + // If we have a custom `GetConstantValue` overload, call it. + return SemIR::ToRaw(GetConstantValue(eval_context, id, phase)); + } else if constexpr (std::same_as) { + CARBON_FATAL("Unexpected invalid IdKind"); + } else { + // Otherwise, we assume the value is already constant. + return SemIR::ToRaw(id); + } + }); } // Given an instruction, replaces its operands with their constant values from @@ -985,76 +967,74 @@ static auto ResolveSpecificDeclForSpecificId(EvalContext& eval_context, specific_id); } +static auto ResolveSpecificDeclForArg(EvalContext& eval_context, + SemIR::FacetTypeId facet_type_id) + -> void { + const auto& info = eval_context.context().facet_types().Get(facet_type_id); + for (const auto& interface : info.extend_constraints) { + ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id); + } + for (const auto& interface : info.self_impls_constraints) { + ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id); + } + for (const auto& constraint : info.extend_named_constraints) { + ResolveSpecificDeclForSpecificId(eval_context, constraint.specific_id); + } + for (const auto& constraint : info.self_impls_named_constraints) { + ResolveSpecificDeclForSpecificId(eval_context, constraint.specific_id); + } + for (const auto& type_impls : info.type_impls_interfaces) { + ResolveSpecificDeclForSpecificId(eval_context, + type_impls.specific_interface.specific_id); + } + for (const auto& type_impls : info.type_impls_named_constraints) { + ResolveSpecificDeclForSpecificId( + eval_context, type_impls.specific_named_constraint.specific_id); + } +} + +static auto ResolveSpecificDeclForArg(EvalContext& eval_context, + SemIR::SpecificId specific_id) -> void { + ResolveSpecificDeclForSpecificId(eval_context, specific_id); +} + +static auto ResolveSpecificDeclForArg( + EvalContext& eval_context, SemIR::SpecificInterfaceId specific_interface_id) + -> void { + ResolveSpecificDeclForSpecificId(eval_context, + eval_context.specific_interfaces() + .Get(specific_interface_id) + .specific_id); +} + +template + requires SemIR::Internal::IsIdKindType && + SameAsOneOf +static auto ResolveSpecificDeclForArg(EvalContext& /*eval_context*/, IdT /*id*/) + -> void { + // These id types have a GetConstantValue() overload but that overload + // does not canonicalize any SpecificId in the value type. +} + +template + requires SemIR::Internal::IsIdKindType +static auto ResolveSpecificDeclForArg(EvalContext& /*eval_context*/, IdT /*id*/) + -> void { + if constexpr (HasGetConstantValueOverload) { + CARBON_FATAL("Missing case for {0} which has a GetConstantValue() overload", + IdT::Label); + } +} // Resolves the specific declarations for a specific id in any field of the // `inst` instruction. static auto ResolveSpecificDeclForInst(EvalContext& eval_context, const SemIR::Inst& inst) -> void { for (auto arg_and_kind : {inst.arg0_and_kind(), inst.arg1_and_kind()}) { - // This switch must handle any field type that has a GetConstantValue() - // overload which canonicalizes a specific (and thus potentially forms a new - // specific) as part of forming its constant value. - CARBON_KIND_SWITCH(arg_and_kind) { - case CARBON_KIND(SemIR::FacetTypeId facet_type_id): { - const auto& info = - eval_context.context().facet_types().Get(facet_type_id); - for (const auto& interface : info.extend_constraints) { - ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id); - } - for (const auto& interface : info.self_impls_constraints) { - ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id); - } - for (const auto& constraint : info.extend_named_constraints) { - ResolveSpecificDeclForSpecificId(eval_context, - constraint.specific_id); - } - for (const auto& constraint : info.self_impls_named_constraints) { - ResolveSpecificDeclForSpecificId(eval_context, - constraint.specific_id); - } - for (const auto& type_impls : info.type_impls_interfaces) { - ResolveSpecificDeclForSpecificId( - eval_context, type_impls.specific_interface.specific_id); - } - for (const auto& type_impls : info.type_impls_named_constraints) { - ResolveSpecificDeclForSpecificId( - eval_context, type_impls.specific_named_constraint.specific_id); - } - break; - } - case CARBON_KIND(SemIR::SpecificId specific_id): { - ResolveSpecificDeclForSpecificId(eval_context, specific_id); - break; - } - case CARBON_KIND(SemIR::SpecificInterfaceId specific_interface_id): { - ResolveSpecificDeclForSpecificId(eval_context, - eval_context.specific_interfaces() - .Get(specific_interface_id) - .specific_id); - break; - } - - // These id types have a GetConstantValue() overload but that overload - // does not canonicalize any SpecificId in the value type. - case SemIR::IdKind::For: - case SemIR::IdKind::For: - case SemIR::IdKind::For: - case SemIR::IdKind::For: - case SemIR::IdKind::For: - case SemIR::IdKind::For: - case SemIR::IdKind::For: - break; - - case SemIR::IdKind::None: - // No arg. - break; - - default: - CARBON_CHECK( - !KindHasGetConstantValueOverload(arg_and_kind.kind()), - "Missing case for {0} which has a GetConstantValue() overload", - arg_and_kind.kind()); - break; - } + arg_and_kind.Dispatch( + [&](auto id) { ResolveSpecificDeclForArg(eval_context, id); }); } } diff --git a/toolchain/check/subst.cpp b/toolchain/check/subst.cpp index a6032f5a45f3..5e9101a86e35 100644 --- a/toolchain/check/subst.cpp +++ b/toolchain/check/subst.cpp @@ -87,7 +87,7 @@ class Worklist { // Pushes the specified operand onto the worklist. static auto PushOperand(Context& context, Worklist& worklist, - SemIR::Inst::ArgAndKind arg) -> void { + SemIR::IdAndKind arg) -> void { auto push_block = [&](SemIR::InstBlockId block_id) { for (auto inst_id : context.inst_blocks().Get(SemIR::InstBlockId(block_id))) { @@ -182,7 +182,7 @@ static auto ExpandOperands(Context& context, Worklist& worklist, // Pops the specified operand from the worklist and returns it. static auto PopOperand(Context& context, Worklist& worklist, - SemIR::Inst::ArgAndKind arg) -> int32_t { + SemIR::IdAndKind arg) -> int32_t { auto pop_block_id = [&](SemIR::InstBlockId old_inst_block_id) { auto size = context.inst_blocks().Get(old_inst_block_id).size(); SemIR::CopyOnWriteInstBlock new_inst_block(&context.sem_ir(), diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index f44634d6d0ff..2d1e8ed09247 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -15,7 +15,7 @@ namespace Carbon::SemIR { // Returns the InstId represented by an instruction operand. -static auto AsAnyInstId(Inst::ArgAndKind arg) -> InstId { +static auto AsAnyInstId(IdAndKind arg) -> InstId { if (auto inst_id = arg.TryAs()) { return *inst_id; } diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 278861c0d1ac..684ba9966b16 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -1052,8 +1052,16 @@ auto Formatter::FormatNameAndForm(InstId inst_id, Inst inst) -> void { } } -auto Formatter::FormatInstArgAndKind(Inst::ArgAndKind arg_and_kind) -> void { - GetFormatArgFn(arg_and_kind.kind())(*this, arg_and_kind.value()); +auto Formatter::FormatInstArgAndKind(IdAndKind arg_and_kind) -> void { + arg_and_kind.Dispatch([this](IdT arg) { + if constexpr (requires { FormatArg(arg); }) { + FormatArg(arg); + } else if constexpr (std::is_same_v) { + // Do nothing + } else { + CARBON_FATAL("Missing FormatArg for {0}", typeid(IdT).name()); + } + }); } auto Formatter::FormatInstRhs(Inst inst) -> void { diff --git a/toolchain/sem_ir/formatter.h b/toolchain/sem_ir/formatter.h index 71d286d0bc35..cf89df6bf66a 100644 --- a/toolchain/sem_ir/formatter.h +++ b/toolchain/sem_ir/formatter.h @@ -279,15 +279,8 @@ class Formatter { auto FormatArg(StringLiteralValueId id) -> void; auto FormatArg(ConstantId id) -> void { FormatConstant(id); } - // A `FormatArg` wrapper for `FormatInstArgAndKind`. - using FormatArgFnT = auto(Formatter& formatter, int32_t arg) -> void; - - // Returns the `FormatArgFnT` for the given `IdKind`. - template - static auto GetFormatArgFn(TypeEnum id_kind) -> FormatArgFnT*; - - // Calls `FormatArg` from an `ArgAndKind`. - auto FormatInstArgAndKind(Inst::ArgAndKind arg_and_kind) -> void; + // Calls `FormatArg` from an `IdAndKind`. + auto FormatInstArgAndKind(IdAndKind arg_and_kind) -> void; auto FormatReturnSlotArg(InstId dest_id) -> void; @@ -410,24 +403,6 @@ auto Formatter::FormatEntityStart(llvm::StringRef entity_kind, FormatEntityStart(entity_kind, entity.generic_id, entity_id); } -template -auto Formatter::GetFormatArgFn(TypeEnum id_kind) -> FormatArgFnT* { - static constexpr std::array Table = { - [](Formatter& formatter, int32_t arg) -> void { - auto typed_arg = Inst::FromRaw(arg); - if constexpr (requires { formatter.FormatArg(typed_arg); }) { - formatter.FormatArg(typed_arg); - } else { - CARBON_FATAL("Missing FormatArg for {0}", typeid(Types).name()); - } - }..., - // Invalid and None handling (ordering-sensitive). - [](auto...) -> void { CARBON_FATAL("Unexpected invalid IdKind"); }, - [](auto...) -> void {}, - }; - return Table[id_kind.ToIndex()]; -} - } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_SEM_IR_FORMATTER_H_ diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index 6b6711beca46..6da2d5ff5123 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -15,8 +15,8 @@ namespace Carbon::SemIR { // // As instruction operands, the types listed here can appear as fields of typed // instructions (`toolchain/sem_ir/typed_insts.h`) and must implement the -// `FromRaw` and `ToRaw` protocol in `Inst`. In most cases this is done by -// inheriting from `IdBase` or `IndexBase`. +// `FromRaw` and `ToRaw` protocol. In most cases this is done by inheriting from +// `IdBase` or `IndexBase`. // // clang-format off: We want one per line. using IdKind = TypeEnum< @@ -70,6 +70,112 @@ using IdKind = TypeEnum< VtableId>; // clang-format on +// Convert a field to its raw representation. +static constexpr auto ToRaw(AnyIdBase base) -> int32_t { return base.index; } + +// Convert a field from its raw representation. +template + requires IdKind::Contains +static constexpr auto FromRaw(int32_t raw) -> T { + return T(raw); +} + +// Specialization for IntId. +static constexpr auto ToRaw(IntId id) -> int32_t { return id.AsRaw(); } +template <> +constexpr auto FromRaw(int32_t raw) -> IntId { + return IntId::MakeRaw(raw); +} + +// A type-safe wrapper around any of the ID types in IdKind. +class IdAndKind { + public: + explicit IdAndKind(IdKind kind, int32_t value) : kind_(kind), value_(value) {} + + // Converts to `IdT`, validating the `kind` matches. + template + auto As() const -> IdT { + CARBON_DCHECK(kind_ == IdKind::For); + return IdT(value_); + } + + // Converts to `IdT`, returning nullopt if the kind is incorrect. + template + auto TryAs() const -> std::optional { + if (kind_ != IdKind::For) { + return std::nullopt; + } + return IdT(value_); + } + + auto kind() const -> IdKind { return kind_; } + auto value() const -> int32_t { return value_; } + + // Sentinel type that represents TypeEnum::Invalid in a Dispatch overload set. + // TODO: Consider moving these to TypeEnum. + struct InvalidType : public Printable { + static constexpr llvm::StringLiteral Label = "invalid"; + void Print(llvm::raw_ostream& out) const { out << Label; } + }; + + // Sentinel type that represents TypeEnum::None in a Dispatch overload set. + struct NoneType : public Printable { + static constexpr llvm::StringLiteral Label = "none"; + void Print(llvm::raw_ostream& out) const { out << Label; } + }; + + // Converts `*this` to the type corresponding to `kind()`, passes it to `f`, + // and returns the result. If `kind()` is `Invalid` or `None`, `f` is called + // with an `InvalidType` or `NoneType` argument. `f`'s return value must + // be convertible to `R`. + template + auto Dispatch(F&& f) const -> R { + return GetDispatchFn(kind_)(std::forward(f), value_); + } + + private: + template + using DispatchFnT = auto(F&& f, int32_t id) -> R; + + template + static auto GetDispatchFn(TypeEnum id_kind) -> DispatchFnT* { + static constexpr std::array*, TypeEnum::NumValues> + Table = { + [](F&& f, int32_t id) -> R { + return std::forward(f)(SemIR::FromRaw(id)); + }..., + [](F&& f, int32_t /*id*/) -> R { + return std::forward(f)(InvalidType{}); + }, + [](F&& f, int32_t /*id*/) -> R { + return std::forward(f)(NoneType{}); + }, + }; + return Table[id_kind.ToIndex()]; + } + + IdKind kind_; + int32_t value_; +}; + +namespace Internal { +template +concept IsIdKindType = + IdKind::Contains> || + SameAsOneOf; +} + +// Specialization for None. +static constexpr auto ToRaw(IdAndKind::NoneType /*none*/) -> int32_t { + return AnyIdBase::NoneIndex; +} +template + requires std::is_same_v +constexpr auto FromRaw(int32_t raw) -> IdAndKind::NoneType { + CARBON_CHECK(raw == AnyIdBase::NoneIndex); + return {}; +} + } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_SEM_IR_ID_KIND_H_ diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index 91728bdae7bb..af09c57f2133 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -187,36 +187,6 @@ concept InstLikeType = requires { sizeof(InstLikeTypeInfo); }; // data where the instruction's kind is not known. class Inst : public Printable { public: - // Associates an argument (arg0 or arg1) with its IdKind. - class ArgAndKind { - public: - explicit ArgAndKind(IdKind kind, int32_t value) - : kind_(kind), value_(value) {} - - // Converts to `IdT`, validating the `kind` matches. - template - auto As() const -> IdT { - CARBON_DCHECK(kind_ == IdKind::For); - return IdT(value_); - } - - // Converts to `IdT`, returning nullopt if the kind is incorrect. - template - auto TryAs() const -> std::optional { - if (kind_ != IdKind::For) { - return std::nullopt; - } - return IdT(value_); - } - - auto kind() const -> IdKind { return kind_; } - auto value() const -> int32_t { return value_; } - - private: - IdKind kind_; - int32_t value_; - }; - // Makes an instruction for a singleton. This exists to support simple // construction of all singletons by File. static auto MakeSingleton(InstKind kind) -> Inst { @@ -330,11 +300,11 @@ class Inst : public Printable { auto arg1() const -> int32_t { return arg1_; } // Returns arguments with their IdKind. - auto arg0_and_kind() const -> ArgAndKind { - return ArgAndKind(ArgKindTable[kind_].first, arg0_); + auto arg0_and_kind() const -> IdAndKind { + return IdAndKind(ArgKindTable[kind_].first, arg0_); } - auto arg1_and_kind() const -> ArgAndKind { - return ArgAndKind(ArgKindTable[kind_].second, arg1_); + auto arg1_and_kind() const -> IdAndKind { + return IdAndKind(ArgKindTable[kind_].second, arg1_); } // Sets the type of this instruction. @@ -346,21 +316,6 @@ class Inst : public Printable { arg1_ = arg1; } - // Convert a field to its raw representation, used as `arg0_` / `arg1_`. - static constexpr auto ToRaw(AnyIdBase base) -> int32_t { return base.index; } - static constexpr auto ToRaw(IntId id) -> int32_t { return id.AsRaw(); } - - // Convert a field from its raw representation. - template - requires IdKind::Contains - static constexpr auto FromRaw(int32_t raw) -> T { - return T(raw); - } - template <> - constexpr auto FromRaw(int32_t raw) -> IntId { - return IntId::MakeRaw(raw); - } - auto Print(llvm::raw_ostream& out) const -> void; friend auto operator==(Inst lhs, Inst rhs) -> bool { diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index 4c9f604ecdd8..cddd18a1a0bb 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/toolchain/sem_ir/inst_fingerprinter.cpp @@ -406,25 +406,15 @@ struct Worklist { CARBON_FATAL("Unexpected instruction operand kind {0}", typeid(T).name()); } - using AddFnT = auto(Worklist& worklist, int32_t arg) -> void; - - // Returns the arg handler for an `IdKind`. - template - static auto GetAddFn(TypeEnum id_kind) -> AddFnT* { - static constexpr std::array Table = { - [](Worklist& worklist, int32_t arg) { - worklist.Add(Inst::FromRaw(arg)); - }..., - // Invalid and None handling (ordering-sensitive). - [](auto...) { CARBON_FATAL("Unexpected invalid IdKind"); }, - [](auto...) {}, - }; - return Table[id_kind.ToIndex()]; + auto Add(IdAndKind::InvalidType /*invalid*/) -> void { + CARBON_FATAL("Unexpected invalid IdKind"); } + auto Add(IdAndKind::NoneType /*none*/) -> void {} + // Add an instruction argument to the contents of the current instruction. - auto AddWithKind(Inst::ArgAndKind arg) -> void { - GetAddFn(arg.kind())(*this, arg.value()); + auto AddWithKind(IdAndKind arg) -> void { + arg.Dispatch([this](auto id) { Add(id); }); } // Ensure all the instructions on the todo list have fingerprints. To avoid a