mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Remove ArgKinds to encourage safer coding patterns (#5212)
#5171 ran into an issue where the wrong kind was associated with an arg (`auto arg1 = RefineOperand(context, loc_id, arg0_kind, action.arg1());`). This PR is trying to reduce risk of similar errors by replaced `ArgKinds()` with instead an `ArgAndKind` structure and corresponding accessors. A couple things I considered and discarded were: - Adding `CARBON_KIND_SWITCH` support (in this PR -- see #5216). - The particular way that `ForCase` works would need to change, and I was hesitant to do that here. - But this is why I did add `As` to `ArgAndKind`, because it had me thinking in that direction. - Trying to make wrapper functions like `MutateArgs(callback_fn);`. This kind of approach gets a little messy due to some of the conditional passes, and in particular the reverse-iteration done for `PopOperand` in subst.cpp - Making something like `args_and_kinds() -> std::array<ArgAndKind, 2>`. There's one spot where iteration is already set up as a loop, but for others it felt a little convoluted with less gain than `MutateArgs`-style things. I'm not sure if there's a better way to set up the table generators, I might keep tinkering with those for ideas.
This commit is contained in:
+44
-31
@@ -640,36 +640,52 @@ 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 a lookup table to get constants by Id::Kind. Requires a null IdKind
|
||||
// as a parameter in order to get the type pack.
|
||||
template <typename... Types>
|
||||
static constexpr auto MakeArgHandlerTable(
|
||||
SemIR::TypeEnum<Types...>* /*id_kind*/)
|
||||
-> std::array<ArgHandlerFnT*, SemIR::IdKind::NumValues> {
|
||||
std::array<ArgHandlerFnT*, SemIR::IdKind::NumValues> table = {};
|
||||
((table[SemIR::IdKind::template For<Types>.ToIndex()] =
|
||||
[](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;
|
||||
}
|
||||
}),
|
||||
...);
|
||||
table[SemIR::IdKind::Invalid.ToIndex()] = [](EvalContext& /*context*/,
|
||||
int32_t /*arg*/,
|
||||
Phase* /*phase*/) -> int32_t {
|
||||
CARBON_FATAL("Instruction has argument with invalid IdKind");
|
||||
};
|
||||
table[SemIR::IdKind::None.ToIndex()] =
|
||||
[](EvalContext& /*context*/, int32_t arg, Phase* /*phase*/) -> int32_t {
|
||||
return arg;
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
// 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`.
|
||||
template <typename... Type>
|
||||
static auto GetConstantValueForArg(EvalContext& eval_context,
|
||||
SemIR::TypeEnum<Type...> kind, int32_t arg,
|
||||
SemIR::Inst::ArgAndKind arg_and_kind,
|
||||
Phase* phase) -> int32_t {
|
||||
using Handler = auto(EvalContext&, int32_t arg, Phase * phase)->int32_t;
|
||||
static constexpr Handler* Handlers[] = {
|
||||
[](EvalContext& eval_context, int32_t arg, Phase* phase) -> int32_t {
|
||||
auto id = SemIR::Inst::FromRaw<Type>(arg);
|
||||
if constexpr (HasGetConstantValueOverload<Type>) {
|
||||
// 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;
|
||||
}
|
||||
}...,
|
||||
[](EvalContext&, int32_t, Phase*) -> int32_t {
|
||||
// Handler for IdKind::Invalid is next.
|
||||
CARBON_FATAL("Instruction has argument with invalid IdKind");
|
||||
},
|
||||
[](EvalContext&, int32_t arg, Phase*) -> int32_t {
|
||||
// Handler for IdKind::None is last.
|
||||
return arg;
|
||||
}};
|
||||
return Handlers[kind.ToIndex()](eval_context, arg, phase);
|
||||
static constexpr auto Table =
|
||||
MakeArgHandlerTable(static_cast<SemIR::IdKind*>(nullptr));
|
||||
return Table[arg_and_kind.kind.ToIndex()](eval_context, arg_and_kind.value,
|
||||
phase);
|
||||
}
|
||||
|
||||
// Given an instruction, replaces its type and operands with their constant
|
||||
@@ -680,22 +696,20 @@ static auto ReplaceAllFieldsWithConstantValues(EvalContext& eval_context,
|
||||
SemIR::Inst* inst, Phase* phase)
|
||||
-> bool {
|
||||
auto type_id = SemIR::TypeId(
|
||||
GetConstantValueForArg(eval_context, SemIR::IdKind::For<SemIR::TypeId>,
|
||||
inst->type_id().index, phase));
|
||||
GetConstantValueForArg(eval_context, inst->type_id_and_kind(), phase));
|
||||
inst->SetType(type_id);
|
||||
if (!IsConstant(*phase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto kinds = inst->ArgKinds();
|
||||
auto arg0 =
|
||||
GetConstantValueForArg(eval_context, kinds.first, inst->arg0(), phase);
|
||||
GetConstantValueForArg(eval_context, inst->arg0_and_kind(), phase);
|
||||
if (!IsConstant(*phase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto arg1 =
|
||||
GetConstantValueForArg(eval_context, kinds.second, inst->arg1(), phase);
|
||||
GetConstantValueForArg(eval_context, inst->arg1_and_kind(), phase);
|
||||
if (!IsConstant(*phase)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1601,9 +1615,8 @@ static auto ComputeInstPhase(Context& context, SemIR::Inst inst) -> Phase {
|
||||
|
||||
auto phase = GetPhase(context.constant_values(),
|
||||
context.types().GetConstantId(inst.type_id()));
|
||||
auto kinds = inst.ArgKinds();
|
||||
GetConstantValueForArg(eval_context, kinds.first, inst.arg0(), &phase);
|
||||
GetConstantValueForArg(eval_context, kinds.second, inst.arg1(), &phase);
|
||||
GetConstantValueForArg(eval_context, inst.arg0_and_kind(), &phase);
|
||||
GetConstantValueForArg(eval_context, inst.arg1_and_kind(), &phase);
|
||||
CARBON_CHECK(IsConstant(phase));
|
||||
return phase;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user