Include bundle operands in operand refinement (#7391)

Also some Bundle API tweaks:
- Remove support for non-canonical bundle IDs. Bundles don't have a
unique identity, so non-canonical bundle IDs would bloat the SemIR for
no benefit.
- Adjust the conversions between raw and typed bundle IDs to not be
templated. This makes the conversions easier to access in a debugger.
This commit is contained in:
Geoff Romer
2026-06-19 00:22:05 +00:00
committed by GitHub
parent 2ebc7cdb40
commit 4e086a6615
4 changed files with 77 additions and 60 deletions
+58 -30
View File
@@ -222,39 +222,67 @@ static auto AddDependentActionSpliceImpl(Context& context,
// produces an argument that has the template-dependent parts replaced with
// 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::IdAndKind 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.value();
}
//
// This is the default case, for ID kinds that can't be refined.
template <typename IdT>
requires SemIR::Internal::IsIdKindType<IdT>
static auto RefineTypedOperand(Context& /*context*/, SemIR::LocId /*loc_id*/,
IdT id) -> IdT {
return id;
}
// If the type of the action argument is dependent, refine to an instruction
// with a concrete type.
if (OperandDependence(context, inst.type_id()) ==
SemIR::ConstantDependence::Template) {
auto type_inst_id = context.types().GetTypeInstId(inst.type_id());
inst_id = AddDependentActionSpliceImpl(
context,
SemIR::LocIdAndInst(
loc_id,
SemIR::RefineTypeAction{.type_id = GetSingletonType(
context, SemIR::InstType::TypeInstId),
.inst_id = *inst_id,
.inst_type_inst_id = type_inst_id}),
type_inst_id);
}
// TODO: Handle the case where the constant value of the instruction is
// template-dependent.
return inst_id->index;
static auto RefineTypedOperand(Context& context, SemIR::LocId loc_id,
SemIR::MetaInstId inst_id) -> 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 inst_id;
}
return arg.value();
// If the type of the action argument is dependent, refine to an instruction
// with a concrete type.
if (OperandDependence(context, inst.type_id()) ==
SemIR::ConstantDependence::Template) {
auto type_inst_id = context.types().GetTypeInstId(inst.type_id());
inst_id = AddDependentActionSpliceImpl(
context,
SemIR::LocIdAndInst(
loc_id,
SemIR::RefineTypeAction{.type_id = GetSingletonType(
context, SemIR::InstType::TypeInstId),
.inst_id = inst_id,
.inst_type_inst_id = type_inst_id}),
type_inst_id);
}
// TODO: Handle the case where the constant value of the instruction is
// template-dependent.
return inst_id;
}
template <typename BundleT>
static auto RefineTypedOperand(Context& context, SemIR::LocId loc_id,
SemIR::BundleId<BundleT> bundle_id)
-> SemIR::BundleId<BundleT> {
auto bundle_tuple = context.bundles().GetAsTuple(bundle_id);
BundleT refined_bundle = std::apply(
[&](auto... bundle_fields) {
// This can't actually recurse, because bundles can't contain bundle
// IDs.
return BundleT{RefineTypedOperand(context, loc_id, bundle_fields)...};
},
bundle_tuple);
return context.bundles().AddCanonical(refined_bundle);
}
// Dynamically dispatched wrapper for RefineTypedOperand.
static auto RefineOperand(Context& context, SemIR::LocId loc_id,
SemIR::IdAndKind arg) -> int32_t {
return arg.Dispatch<int32_t>([&](auto id) {
return SemIR::ToRaw(RefineTypedOperand(context, loc_id, id));
});
}
// Refine the operands of an action, ensuring that they will refer to concrete
+4 -3
View File
@@ -833,9 +833,10 @@ auto MatchContext::DoPreWork(State state, SemIR::SpliceInst splice,
context_.types().GetTypeInstId(splice.type_id),
{.type_id = SemIR::InstType::TypeId,
.args_id =
context_.bundles().Add<SemIR::CalleePatternMatchAction::Args>(
{.pattern_id = entry.pattern_id,
.parent_index = callee_state->index.Allocate()})});
context_.bundles()
.AddCanonical<SemIR::CalleePatternMatchAction::Args>(
{.pattern_id = entry.pattern_id,
.parent_index = callee_state->index.Allocate()})});
callee_state->PushCallParamPattern(
context_, SemIR::LocId(entry.pattern_id), entry.pattern_id,
specific_id_stack_.back());
+3 -15
View File
@@ -43,8 +43,9 @@ namespace Carbon::SemIR {
//
// A bundle type like `Args` must be an aggregate, and its fields must all be
// ID types, i.e. types listed in the definition of `IdKind`. `BundleId<Args>`
// itself must also be added to that list, although bundles should generally not
// have bundle IDs as members.
// itself must also be added to that list, although bundles must not have
// bundle IDs as members (bundles are an optimization of a flat argument list,
// not a recursive data structure).
//
// Unlike insts, bundles do not record their own kind and the `BundleStore`
// is not guaranteed to record it either. Instead, that information is tracked
@@ -71,12 +72,6 @@ class BundleStore {
explicit BundleStore(llvm::BumpPtrAllocator& allocator, CheckIRId tag_id)
: store_(allocator, tag_id, 0), bundle_kind_cache_(store_.GetIdTag()) {}
// Adds a new bundle to the store, and returns its ID.
template <typename BundleT>
auto Add(const BundleT& bundle) -> BundleId<BundleT> {
return BundleId<BundleT>{store_.Add(BundleToArray(bundle))};
}
// Returns the canonical ID of the given bundle, allocating a new one if
// it does not already exist.
template <typename BundleT>
@@ -84,13 +79,6 @@ class BundleStore {
return BundleId<BundleT>{store_.AddCanonical(BundleToArray(bundle))};
}
// Returns the canonical ID of the bundle specified by `bundle_id`, allocating
// a new canonical ID if none exists already.
template <typename BundleT>
auto MakeCanonical(BundleId<BundleT> bundle_id) -> BundleId<BundleT> {
return BundleId<BundleT>{store_.MakeCanonical(bundle_id.index)};
}
// Returns the bundle with the given ID.
template <typename BundleT>
auto Get(BundleId<BundleT> bundle_id) const -> BundleT {
+12 -12
View File
@@ -1010,26 +1010,26 @@ struct RequireImplsBlockId : public IdBase<RequireImplsBlockId> {
inline constexpr RequireImplsBlockId RequireImplsBlockId::Empty =
RequireImplsBlockId(0);
// The ID of a bundle of arguments with an unspecified type.
struct RawBundleId : public IdBase<RawBundleId> {
static constexpr llvm::StringLiteral Label = "bundle";
using IdBase::IdBase;
};
// The ID of a bundle of arguments with type `BundleT`.
template <typename BundleT>
struct BundleId : public IdBase<BundleId<BundleT>> {
static constexpr llvm::StringLiteral Label = "bundle";
using IdBase<BundleId<BundleT>>::IdBase;
};
// The ID of a bundle of arguments with an unspecified type.
struct RawBundleId : public IdBase<RawBundleId> {
static constexpr llvm::StringLiteral Label = "bundle";
explicit BundleId(RawBundleId raw_id)
: IdBase<BundleId<BundleT>>(raw_id.index) {}
template <typename BundleT>
explicit(false) RawBundleId(BundleId<BundleT> bundle_id)
: IdBase(bundle_id.index) {}
using IdBase::IdBase;
template <typename BundleT>
explicit operator BundleId<BundleT>() const {
return BundleId<BundleT>(index);
// NOLINTNEXTLINE(google-explicit-constructor)
explicit(false) operator RawBundleId() const {
return RawBundleId(this->index);
}
};