Restructure action-dependence APIs (#7074)

- Rename `ActionIsDependent` to `ActionIsPerformable` (with negated
meaning), because that name is more concrete and, um, actionable.
- Replace `OperandIsDependent` with `OperandDependence`, which returns a
`ConstantDependence` instead of a bool. We need this additional
generality for handling form actions, where we sometimes need to ask
whether something has _any_ dependence, not just whether it has template
dependence.
This commit is contained in:
Geoff Romer
2026-04-17 19:03:21 +00:00
committed by GitHub
parent f91990aa87
commit ad0a4ea8a4
4 changed files with 62 additions and 56 deletions
+34 -28
View File
@@ -24,56 +24,59 @@ auto PerformAction(Context& context, SemIR::LocId loc_id,
.source_id = action.inst_id});
}
static auto OperandIsDependent(Context& context, SemIR::ConstantId const_id)
-> bool {
static auto OperandDependence(Context& context, SemIR::ConstantId const_id)
-> SemIR::ConstantDependence {
// A type operand makes the instruction dependent if it is a
// template-dependent constant.
if (!const_id.is_symbolic()) {
return false;
return SemIR::ConstantDependence::None;
}
return context.constant_values().GetSymbolicConstant(const_id).dependence ==
SemIR::ConstantDependence::Template;
return context.constant_values().GetSymbolicConstant(const_id).dependence;
}
auto OperandIsDependent(Context& context, SemIR::TypeId type_id) -> bool {
auto OperandDependence(Context& context, SemIR::TypeId type_id)
-> SemIR::ConstantDependence {
// A type operand makes the instruction dependent if it is a
// template-dependent type.
return OperandIsDependent(context, context.types().GetConstantId(type_id));
return OperandDependence(context, context.types().GetConstantId(type_id));
}
auto OperandIsDependent(Context& context, SemIR::InstId inst_id) -> bool {
auto OperandDependence(Context& context, SemIR::InstId inst_id)
-> SemIR::ConstantDependence {
// An instruction operand makes the instruction dependent if its type or
// constant value is dependent.
return OperandIsDependent(context, context.insts().Get(inst_id).type_id()) ||
OperandIsDependent(context, context.constant_values().Get(inst_id));
return std::max(
OperandDependence(context, context.insts().Get(inst_id).type_id()),
OperandDependence(context, context.constant_values().Get(inst_id)));
}
auto OperandIsDependent(Context& context, SemIR::TypeInstId inst_id) -> bool {
auto OperandDependence(Context& context, SemIR::TypeInstId inst_id)
-> SemIR::ConstantDependence {
// An instruction operand makes the instruction dependent if its type or
// constant value is dependent. TypeInstId has type `TypeType` which is
// concrete, so we only need to look at the constant value.
return OperandIsDependent(context, context.constant_values().Get(inst_id));
return OperandDependence(context, context.constant_values().Get(inst_id));
}
static auto OperandIsDependent(Context& context, SemIR::Inst::ArgAndKind arg)
-> bool {
static auto OperandDependence(Context& context, SemIR::Inst::ArgAndKind arg)
-> SemIR::ConstantDependence {
CARBON_KIND_SWITCH(arg) {
case CARBON_KIND(SemIR::InstId inst_id): {
return OperandIsDependent(context, inst_id);
return OperandDependence(context, inst_id);
}
case CARBON_KIND(SemIR::MetaInstId inst_id): {
return OperandIsDependent(context, inst_id);
return OperandDependence(context, inst_id);
}
case CARBON_KIND(SemIR::TypeInstId inst_id): {
return OperandIsDependent(context, inst_id);
return OperandDependence(context, inst_id);
}
case SemIR::IdKind::None:
case SemIR::IdKind::For<SemIR::AbsoluteInstId>:
case SemIR::IdKind::For<SemIR::NameId>:
return false;
return SemIR::ConstantDependence::None;
default:
// TODO: Properly handle different argument kinds.
@@ -81,18 +84,20 @@ static auto OperandIsDependent(Context& context, SemIR::Inst::ArgAndKind arg)
}
}
auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool {
auto ActionIsPerformable(Context& context, SemIR::Inst action_inst) -> bool {
if (auto refine_action = action_inst.TryAs<SemIR::RefineTypeAction>()) {
// `RefineTypeAction` can be performed whenever the type is non-dependent,
// even if we don't know the instruction yet.
return OperandIsDependent(context, refine_action->inst_type_inst_id);
// `RefineTypeAction` can be performed whenever the type is not template-
// dependent, even if we don't know the instruction yet.
return OperandDependence(context, refine_action->inst_type_inst_id) <
SemIR::ConstantDependence::Template;
}
if (OperandIsDependent(context, action_inst.type_id())) {
return true;
}
return OperandIsDependent(context, action_inst.arg0_and_kind()) ||
OperandIsDependent(context, action_inst.arg1_and_kind());
return OperandDependence(context, action_inst.type_id()) <
SemIR::ConstantDependence::Template &&
OperandDependence(context, action_inst.arg0_and_kind()) <
SemIR::ConstantDependence::Template &&
OperandDependence(context, action_inst.arg1_and_kind()) <
SemIR::ConstantDependence::Template;
}
static auto AddDependentActionSpliceImpl(Context& context,
@@ -129,7 +134,8 @@ static auto RefineOperand(Context& context, SemIR::LocId loc_id,
// If the type of the action argument is dependent, refine to an instruction
// with a concrete type.
if (OperandIsDependent(context, inst.type_id())) {
if (OperandDependence(context, inst.type_id()) ==
SemIR::ConstantDependence::Template) {
auto type_inst_id = context.types().GetTypeInstId(inst.type_id());
inst_id = AddDependentActionSpliceImpl(
context,
+15 -17
View File
@@ -28,21 +28,21 @@ auto PerformAction(Context& context, SemIR::LocId loc_id,
auto PerformAction(Context& context, SemIR::LocId loc_id,
SemIR::RefineTypeAction action) -> SemIR::InstId;
// Determines whether the given action depends on a template parameter in a way
// that means it cannot be performed immediately.
auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool;
// Determines whether the given action can be performed immediately (i.e.
// whether it is non-template-dependent).
auto ActionIsPerformable(Context& context, SemIR::Inst action_inst) -> bool;
// Determines whether the given action operand depends on a template parameter
// in a way that means the action cannot be performed immediately.
auto OperandIsDependent(Context& context, SemIR::InstId inst_id) -> bool;
// Returns the constant-dependence of `inst_id` (i.e. the maximum of the
// constant-dependences of its type and its value).
auto OperandDependence(Context& context, SemIR::InstId inst_id)
-> SemIR::ConstantDependence;
auto OperandDependence(Context& context, SemIR::TypeInstId inst_id)
-> SemIR::ConstantDependence;
// Determines whether the given action operand depends on a template parameter
// in a way that means the action cannot be performed immediately.
auto OperandIsDependent(Context& context, SemIR::TypeInstId inst_id) -> bool;
// Determines whether the given type depends on a template parameter
// in a way that means the action cannot be performed immediately.
auto OperandIsDependent(Context& context, SemIR::TypeId type_id) -> bool;
// Returns the constant-dependence of `type_id` (i.e. the constant-dependence
// of the corresponding type constant).
auto OperandDependence(Context& context, SemIR::TypeId type_id)
-> SemIR::ConstantDependence;
// Adds an instruction to the current block to splice in the result of
// performing a dependent action.
@@ -66,9 +66,7 @@ template <typename ActionT>
auto HandleAction(Context& context, SemIR::LocId loc_id, ActionT action_inst,
SemIR::TypeInstId result_type_inst_id =
SemIR::TypeInstId::None) -> SemIR::InstId {
if (ActionIsDependent(context, action_inst) ||
(result_type_inst_id.has_value() &&
OperandIsDependent(context, result_type_inst_id))) {
if (!ActionIsPerformable(context, action_inst)) {
return AddDependentActionSplice(
context, SemIR::LocIdAndInst(loc_id, action_inst), result_type_inst_id);
}
@@ -93,7 +91,7 @@ auto EndPerformDelayedAction(Context& context, SemIR::InstId result_id)
template <typename ActionT>
auto PerformDelayedAction(Context& context, SemIR::LocId loc_id,
ActionT action_inst) -> SemIR::InstId {
if (ActionIsDependent(context, action_inst)) {
if (!ActionIsPerformable(context, action_inst)) {
return SemIR::InstId::None;
}
Internal::BeginPerformDelayedAction(context);
+11 -10
View File
@@ -1971,17 +1971,18 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
// didn't apply in the template definition.
// TODO: Support this for targets other than `Value`.
if (sem_ir.insts().Get(expr_id).type_id() != target.type_id &&
target.kind == ConversionTarget::Value &&
(OperandIsDependent(context, expr_id) ||
OperandIsDependent(context, target.type_id))) {
target.kind == ConversionTarget::Value) {
auto target_type_inst_id = context.types().GetTypeInstId(target.type_id);
return AddDependentActionSplice(
context, loc_id,
SemIR::ConvertToValueAction{
.type_id = GetSingletonType(context, SemIR::InstType::TypeInstId),
.inst_id = expr_id,
.target_type_inst_id = target_type_inst_id},
target_type_inst_id);
SemIR::ConvertToValueAction convert_action = {
.type_id = GetSingletonType(context, SemIR::InstType::TypeInstId),
.inst_id = expr_id,
.target_type_inst_id = target_type_inst_id};
// We don't use `HandleAction` here because it would call `PerformAction`
// inline if it's performable, which would lead to infinite recursion.
if (!ActionIsPerformable(context, convert_action)) {
return AddDependentActionSplice(context, loc_id, convert_action,
target_type_inst_id);
}
}
// If this is not a builtin conversion, try an `ImplicitAs` conversion.
+2 -1
View File
@@ -545,7 +545,8 @@ static auto GetConstantValue(EvalContext& eval_context,
}
// Otherwise, this is a normal instruction.
if (OperandIsDependent(eval_context.context(), inst_id)) {
if (OperandDependence(eval_context.context(), inst_id) ==
SemIR::ConstantDependence::Template) {
*phase = LatestPhase(*phase, Phase::TemplateSymbolic);
}
return inst_id;