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`.
This commit is contained in:
Richard Smith
2026-08-26 19:34:37 +00:00
committed by GitHub
parent 95fc6fae22
commit 197cae22f1
3 changed files with 62 additions and 53 deletions
+26 -41
View File
@@ -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 <typename InstT>
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<SemIR::Name> 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).
+36 -7
View File
@@ -367,13 +367,42 @@ class FunctionContext {
Map<SemIR::InstId, llvm::Value*> 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 <typename InstT>
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 <typename InstT>
using FunctionTypeForHandleInst = std::conditional_t<
HasHandleInst<InstT>,
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<InstT>`
// 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<SemIR::Name> HandleInst;
#include "toolchain/sem_ir/inst_kind.def"
} // namespace Carbon::Lower
-5
View File
@@ -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());