From 197cae22f16024965b57f11c3c1e6432055fd03b Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 26 Aug 2026 19:34:37 +0000 Subject: [PATCH] Use consistent pattern to generate constrained overload sets. (#7679) Follow the pattern used by eval_inst.h's `EvalConstantInst` to generate declarations of an overload set that handles some but not all typed inst classes. The pattern is: * A template computes the signature to use for a particular overload, producing a fallback `() -> void` signature for overloads that should not exist. * The `.def` file is used to generate a declaration per instruction kind, whose signature is generated by the template. * The `() -> void` signature that all the "should not exist" cases generate is explicitly deleted. This avoids the redundancy of manually declaring all the overloads, as we did for `PerformAction`, and is less error-prone as it both catches signature errors and definitions of overloads that are dead code and should not exist, as it did for the `FacetAccessType` overload of `LowerInst`. --- toolchain/check/action.h | 67 ++++++++++++------------------ toolchain/lower/function_context.h | 43 +++++++++++++++---- toolchain/lower/handle.cpp | 5 --- 3 files changed, 62 insertions(+), 53 deletions(-) diff --git a/toolchain/check/action.h b/toolchain/check/action.h index 3e40405933e0..aaef33d0afa1 100644 --- a/toolchain/check/action.h +++ b/toolchain/check/action.h @@ -9,53 +9,38 @@ #include "toolchain/check/inst.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" +#include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { -// Performs a member access action. Defined in member_access.cpp. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::AccessMemberAction action) -> SemIR::InstId; -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::AccessOptionalMemberAction action) -> SemIR::InstId; +namespace Internal { +// Computes the function type to use for PerformAction for InstT. +template +using FunctionTypeForPerformAction = std::conditional_t< + InstT::Kind.constant_kind() == SemIR::InstConstantKind::InstAction, + auto(Context& context, SemIR::LocId loc_id, InstT inst)->SemIR::InstId, + auto()->void>; +} // namespace Internal -// Performs a C++ template call action. Defined in cpp/call.cpp. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::CallCppTemplateAction action) -> SemIR::InstId; +// Explicitly delete the overload generated for non-action instructions. These +// all produce the same signature, so we only need to delete it once. +auto PerformAction() -> void = delete; -// Performs a conversion action. Defined in convert.cpp. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::ConvertAction action) -> SemIR::InstId; -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::ConvertToCategoryAction action) -> SemIR::InstId; -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::ConvertToValueAction action) -> SemIR::InstId; - -// Performs a form parameter pattern action. Defined in pattern.cpp. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::FormParamPatternAction action) -> SemIR::InstId; - -// Performs an output form parameter pattern action. Defined in pattern.cpp. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::OutFormParamPatternAction action) -> SemIR::InstId; - -// Performs a caller pattern match action. Defined in pattern_match.cpp. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::CallerPatternMatchAction action) -> SemIR::InstId; - -// Performs a callee pattern match action. Defined in pattern_match.cpp. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::CalleePatternMatchAction action) -> SemIR::InstId; - -// Performs a compound member access action. Defined in member_access.cpp. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::CompoundMemberAccessAction action) -> SemIR::InstId; - -// Performs a type refinement action, by creating a conversion from an -// instruction with a template-dependent symbolic type to the corresponding -// instantiated type. -auto PerformAction(Context& context, SemIR::LocId loc_id, - SemIR::RefineTypeAction action) -> SemIR::InstId; +// Performs an action. Each PerformAction implementation lives with the code +// that creates and defines the action. For an instruction whose constant kind +// is InstAction, an overload should be provided with the signature: +// +// auto PerformAction(Context& context, SemIR::LocId loc_id, InstT inst) +// -> SemIR::InstId; +// +// that returns the value that should be used as the result of evaluating the +// instructions produced by the action. Any instructions generated during +// `PerformAction` will be spliced into the code at the point where the action +// was created. +#define CARBON_SEM_IR_INST_KIND(Name) \ + Internal::FunctionTypeForPerformAction PerformAction; +#include "toolchain/sem_ir/inst_kind.def" // Determines whether the given action can be performed immediately (i.e. // whether it is non-template-dependent). diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index 75859e100fec..73017125c0c6 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -367,13 +367,42 @@ class FunctionContext { Map locals_; }; -// Provides handlers for instructions that occur in a FunctionContext. Although -// this is declared for all instructions, it should only be defined for -// instructions which are non-constant and not always typed. See -// `FunctionContext::LowerInst` for how this is used. -#define CARBON_SEM_IR_INST_KIND(Name) \ - auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, \ - SemIR::Name inst) -> void; +namespace Internal { +// Determines whether InstT should have a `HandleInst` function. +template +constexpr bool HasHandleInst = + // is_lowered() == false indicates lowering should never see this inst kind. + InstT::Kind.is_lowered() && + // Constant instructions never need to be explicitly lowered. + InstT::Kind.constant_kind() != SemIR::InstConstantKind::Always && + InstT::Kind.constant_kind() != SemIR::InstConstantKind::AlwaysUnique && + // Instructions that always produce types don't need a `HandleInst` even if + // they're not constant, because `type` has an empty runtime representation + // and we assume that `InstIsType::Always` implies a lack of side effects. + InstT::Kind.is_type() != SemIR::InstIsType::Always; + +// Computes the function type to use for HandleInst for InstT. +template +using FunctionTypeForHandleInst = std::conditional_t< + HasHandleInst, + auto(FunctionContext& context, SemIR::InstId inst_id, InstT inst)->void, + auto()->void>; +} // namespace Internal + +// Explicitly delete the overload generated for non-lowered instructions. +// These all produce the same signature, so we only need to delete it once. +auto HandleInst() -> void = delete; + +// Provides handlers for instructions that occur in a FunctionContext. This +// should be defined for instructions for which `Internal::HasHandleInst` +// is true. See `FunctionContext::LowerInst` for how this is used. +// +// The signature of an overload is: +// +// auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, +// InstT inst) -> void; +#define CARBON_SEM_IR_INST_KIND(Name) \ + Internal::FunctionTypeForHandleInst HandleInst; #include "toolchain/sem_ir/inst_kind.def" } // namespace Carbon::Lower diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index fadea586bc33..59921946fe9d 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -188,11 +188,6 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, context.SetLocal(inst_id, context.GetValue(inst.pointer_id)); } -auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, - SemIR::FacetAccessType /*inst*/) -> void { - context.SetLocal(inst_id, context.GetTypeAsValue()); -} - auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::FacetValue /*inst*/) -> void { context.SetLocal(inst_id, context.GetTypeAsValue());