mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
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<T>` 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.
This commit is contained in:
+26
-22
@@ -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 <typename IdT>
|
||||
requires SemIR::Internal::IsIdKindType<IdT> &&
|
||||
SameAsOneOf<IdT, SemIR::IdAndKind::NoneType, SemIR::AbsoluteInstId,
|
||||
SemIR::CallParamIndex, SemIR::NameId>
|
||||
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 <typename IdT>
|
||||
requires SemIR::Internal::IsIdKindType<IdT>
|
||||
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<SemIR::AbsoluteInstId>:
|
||||
case SemIR::IdKind::For<SemIR::NameId>:
|
||||
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<SemIR::ConstantDependence>(
|
||||
[&](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<SemIR::MetaInstId>()) {
|
||||
auto inst = context.insts().Get(*inst_id);
|
||||
if (inst.Is<SemIR::SpliceInst>()) {
|
||||
|
||||
@@ -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<SemIR::ClassId>:
|
||||
|
||||
+76
-96
@@ -877,43 +877,25 @@ static constexpr bool HasGetConstantValueOverload = requires {
|
||||
Accept<auto (*)(EvalContext&, IdT, Phase*)->IdT>(GetConstantValue);
|
||||
};
|
||||
|
||||
using ArgHandlerFnT = auto(EvalContext& context, int32_t arg, Phase* phase)
|
||||
-> int32_t;
|
||||
|
||||
// Returns the arg handler for an `IdKind`.
|
||||
template <typename... Types>
|
||||
static auto GetArgHandlerFn(TypeEnum<Types...> id_kind) -> ArgHandlerFnT* {
|
||||
static constexpr std::array<ArgHandlerFnT*, SemIR::IdKind::NumValues> Table =
|
||||
{
|
||||
[](EvalContext& eval_context, int32_t arg, Phase* phase) -> int32_t {
|
||||
auto id = SemIR::Inst::FromRaw<Types>(arg);
|
||||
if constexpr (HasGetConstantValueOverload<Types>) {
|
||||
// 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<int32_t>([&]<typename IdT>(IdT id) -> int32_t {
|
||||
if constexpr (HasGetConstantValueOverload<IdT>) {
|
||||
// If we have a custom `GetConstantValue` overload, call it.
|
||||
return SemIR::ToRaw(GetConstantValue(eval_context, id, phase));
|
||||
} else if constexpr (std::same_as<IdT, SemIR::IdAndKind::InvalidType>) {
|
||||
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 <typename IdT>
|
||||
requires SemIR::Internal::IsIdKindType<IdT> &&
|
||||
SameAsOneOf<IdT, SemIR::IdAndKind::NoneType, SemIR::DestInstId,
|
||||
SemIR::EntityNameId, SemIR::InstBlockId, SemIR::InstId,
|
||||
SemIR::MetaInstId, SemIR::StructTypeFieldsId,
|
||||
SemIR::TypeInstId>
|
||||
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 <typename IdT>
|
||||
requires SemIR::Internal::IsIdKindType<IdT>
|
||||
static auto ResolveSpecificDeclForArg(EvalContext& /*eval_context*/, IdT /*id*/)
|
||||
-> void {
|
||||
if constexpr (HasGetConstantValueOverload<IdT>) {
|
||||
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<SemIR::DestInstId>:
|
||||
case SemIR::IdKind::For<SemIR::EntityNameId>:
|
||||
case SemIR::IdKind::For<SemIR::InstBlockId>:
|
||||
case SemIR::IdKind::For<SemIR::InstId>:
|
||||
case SemIR::IdKind::For<SemIR::MetaInstId>:
|
||||
case SemIR::IdKind::For<SemIR::StructTypeFieldsId>:
|
||||
case SemIR::IdKind::For<SemIR::TypeInstId>:
|
||||
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<void>(
|
||||
[&](auto id) { ResolveSpecificDeclForArg(eval_context, id); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<SemIR::InstId>()) {
|
||||
return *inst_id;
|
||||
}
|
||||
|
||||
@@ -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<void>([this]<typename IdT>(IdT arg) {
|
||||
if constexpr (requires { FormatArg(arg); }) {
|
||||
FormatArg(arg);
|
||||
} else if constexpr (std::is_same_v<IdT, IdAndKind::NoneType>) {
|
||||
// Do nothing
|
||||
} else {
|
||||
CARBON_FATAL("Missing FormatArg for {0}", typeid(IdT).name());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto Formatter::FormatInstRhs(Inst inst) -> void {
|
||||
|
||||
@@ -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 <typename... Types>
|
||||
static auto GetFormatArgFn(TypeEnum<Types...> 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 <typename... Types>
|
||||
auto Formatter::GetFormatArgFn(TypeEnum<Types...> id_kind) -> FormatArgFnT* {
|
||||
static constexpr std::array<FormatArgFnT*, IdKind::NumValues> Table = {
|
||||
[](Formatter& formatter, int32_t arg) -> void {
|
||||
auto typed_arg = Inst::FromRaw<Types>(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_
|
||||
|
||||
+108
-2
@@ -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 <typename T>
|
||||
requires IdKind::Contains<T>
|
||||
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<IntId>(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 <typename IdT>
|
||||
auto As() const -> IdT {
|
||||
CARBON_DCHECK(kind_ == IdKind::For<IdT>);
|
||||
return IdT(value_);
|
||||
}
|
||||
|
||||
// Converts to `IdT`, returning nullopt if the kind is incorrect.
|
||||
template <typename IdT>
|
||||
auto TryAs() const -> std::optional<IdT> {
|
||||
if (kind_ != IdKind::For<IdT>) {
|
||||
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<InvalidType> {
|
||||
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<NoneType> {
|
||||
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 <typename R, typename F>
|
||||
auto Dispatch(F&& f) const -> R {
|
||||
return GetDispatchFn<R, F>(kind_)(std::forward<F>(f), value_);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename R, typename F>
|
||||
using DispatchFnT = auto(F&& f, int32_t id) -> R;
|
||||
|
||||
template <typename R, typename F, typename... Ids>
|
||||
static auto GetDispatchFn(TypeEnum<Ids...> id_kind) -> DispatchFnT<R, F>* {
|
||||
static constexpr std::array<DispatchFnT<R, F>*, TypeEnum<Ids...>::NumValues>
|
||||
Table = {
|
||||
[](F&& f, int32_t id) -> R {
|
||||
return std::forward<F>(f)(SemIR::FromRaw<Ids>(id));
|
||||
}...,
|
||||
[](F&& f, int32_t /*id*/) -> R {
|
||||
return std::forward<F>(f)(InvalidType{});
|
||||
},
|
||||
[](F&& f, int32_t /*id*/) -> R {
|
||||
return std::forward<F>(f)(NoneType{});
|
||||
},
|
||||
};
|
||||
return Table[id_kind.ToIndex()];
|
||||
}
|
||||
|
||||
IdKind kind_;
|
||||
int32_t value_;
|
||||
};
|
||||
|
||||
namespace Internal {
|
||||
template <typename T>
|
||||
concept IsIdKindType =
|
||||
IdKind::Contains<std::remove_cvref_t<T>> ||
|
||||
SameAsOneOf<T, IdAndKind::NoneType, IdAndKind::InvalidType>;
|
||||
}
|
||||
|
||||
// Specialization for None.
|
||||
static constexpr auto ToRaw(IdAndKind::NoneType /*none*/) -> int32_t {
|
||||
return AnyIdBase::NoneIndex;
|
||||
}
|
||||
template <typename T>
|
||||
requires std::is_same_v<T, IdAndKind::NoneType>
|
||||
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_
|
||||
|
||||
+4
-49
@@ -187,36 +187,6 @@ concept InstLikeType = requires { sizeof(InstLikeTypeInfo<T>); };
|
||||
// data where the instruction's kind is not known.
|
||||
class Inst : public Printable<Inst> {
|
||||
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 <typename IdT>
|
||||
auto As() const -> IdT {
|
||||
CARBON_DCHECK(kind_ == IdKind::For<IdT>);
|
||||
return IdT(value_);
|
||||
}
|
||||
|
||||
// Converts to `IdT`, returning nullopt if the kind is incorrect.
|
||||
template <typename IdT>
|
||||
auto TryAs() const -> std::optional<IdT> {
|
||||
if (kind_ != IdKind::For<IdT>) {
|
||||
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<Inst> {
|
||||
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<Inst> {
|
||||
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 <typename T>
|
||||
requires IdKind::Contains<T>
|
||||
static constexpr auto FromRaw(int32_t raw) -> T {
|
||||
return T(raw);
|
||||
}
|
||||
template <>
|
||||
constexpr auto FromRaw<IntId>(int32_t raw) -> IntId {
|
||||
return IntId::MakeRaw(raw);
|
||||
}
|
||||
|
||||
auto Print(llvm::raw_ostream& out) const -> void;
|
||||
|
||||
friend auto operator==(Inst lhs, Inst rhs) -> bool {
|
||||
|
||||
@@ -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 <typename... Types>
|
||||
static auto GetAddFn(TypeEnum<Types...> id_kind) -> AddFnT* {
|
||||
static constexpr std::array<AddFnT*, IdKind::NumValues> Table = {
|
||||
[](Worklist& worklist, int32_t arg) {
|
||||
worklist.Add(Inst::FromRaw<Types>(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<void>([this](auto id) { Add(id); });
|
||||
}
|
||||
|
||||
// Ensure all the instructions on the todo list have fingerprints. To avoid a
|
||||
|
||||
Reference in New Issue
Block a user