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:
Jon Ross-Perkins
2025-03-28 23:24:36 +00:00
committed by GitHub
parent 2973cafd60
commit 4cb61ae4e1
8 changed files with 204 additions and 161 deletions
+28 -28
View File
@@ -44,21 +44,24 @@ auto OperandIsDependent(Context& context, SemIR::MetaInstId inst_id) -> bool {
OperandIsDependent(context, context.constant_values().Get(inst_id));
}
static auto OperandIsDependent(Context& context, SemIR::IdKind kind,
int32_t arg) -> bool {
if (kind == SemIR::IdKind::For<SemIR::MetaInstId>) {
return OperandIsDependent(context, SemIR::MetaInstId(arg));
static auto OperandIsDependent(Context& context, SemIR::Inst::ArgAndKind arg)
-> bool {
switch (arg.kind) {
case SemIR::IdKind::For<SemIR::MetaInstId>:
return OperandIsDependent(context, arg.As<SemIR::MetaInstId>());
case SemIR::IdKind::For<SemIR::TypeId>:
return OperandIsDependent(context, arg.As<SemIR::TypeId>());
case SemIR::IdKind::None:
case SemIR::IdKind::For<SemIR::AbsoluteInstId>:
case SemIR::IdKind::For<SemIR::NameId>:
return false;
default:
// TODO: Properly handle different argument kinds.
CARBON_FATAL("Unexpected argument kind for action");
}
if (kind == SemIR::IdKind::For<SemIR::TypeId>) {
return OperandIsDependent(context, SemIR::TypeId(arg));
}
if (kind == SemIR::IdKind::None ||
kind == SemIR::IdKind::For<SemIR::AbsoluteInstId> ||
kind == SemIR::IdKind::For<SemIR::NameId>) {
return false;
}
// TODO: Properly handle different argument kinds.
CARBON_FATAL("Unexpected argument kind for action");
}
auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool {
@@ -71,9 +74,8 @@ auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool {
if (OperandIsDependent(context, action_inst.type_id())) {
return true;
}
auto [arg0_kind, arg1_kind] = action_inst.ArgKinds();
return OperandIsDependent(context, arg0_kind, action_inst.arg0()) ||
OperandIsDependent(context, arg1_kind, action_inst.arg1());
return OperandIsDependent(context, action_inst.arg0_and_kind()) ||
OperandIsDependent(context, action_inst.arg1_and_kind());
}
static auto AddDependentActionSpliceImpl(Context& context,
@@ -98,14 +100,13 @@ 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::IdKind kind, int32_t arg) -> int32_t {
if (kind == SemIR::IdKind::For<SemIR::MetaInstId>) {
auto inst_id = SemIR::MetaInstId(arg);
auto inst = context.insts().Get(inst_id);
SemIR::Inst::ArgAndKind arg) -> int32_t {
if (auto inst_id = arg.TryAs<SemIR::MetaInstId>()) {
auto inst = context.insts().Get(*inst_id);
if (inst.Is<SemIR::SpliceInst>()) {
// The argument will evaluate to the spliced instruction, which is already
// refined.
return arg;
return arg.value;
}
// If the type of the action argument is dependent, refine to an instruction
@@ -116,7 +117,7 @@ static auto RefineOperand(Context& context, SemIR::LocId loc_id,
SemIR::LocIdAndInst(loc_id,
SemIR::RefineTypeAction{
.type_id = SemIR::InstType::SingletonTypeId,
.inst_id = inst_id,
.inst_id = *inst_id,
.inst_type_id = inst.type_id()}),
inst.type_id());
}
@@ -124,19 +125,18 @@ static auto RefineOperand(Context& context, SemIR::LocId loc_id,
// TODO: Handle the case where the constant value of the instruction is
// template-dependent.
return inst_id.index;
return inst_id->index;
}
return arg;
return arg.value;
}
// Refine the operands of an action, ensuring that they will refer to concrete
// instructions that don't have template-dependent types.
static auto RefineOperands(Context& context, SemIR::LocId loc_id,
SemIR::Inst action) -> SemIR::Inst {
auto [arg0_kind, arg1_kind] = action.ArgKinds();
auto arg0 = RefineOperand(context, loc_id, arg0_kind, action.arg0());
auto arg1 = RefineOperand(context, loc_id, arg1_kind, action.arg1());
auto arg0 = RefineOperand(context, loc_id, action.arg0_and_kind());
auto arg1 = RefineOperand(context, loc_id, action.arg1_and_kind());
action.SetArgs(arg0, arg1);
return action;
}