From 031ec0a140575613a6dec97bab692104d501b28e Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Thu, 7 May 2026 12:42:23 -0700 Subject: [PATCH] Implement `BundleStore` (#7173) See [here](https://docs.google.com/document/d/1eWW8MTko3PIqxZ32-GhsdaRSYqoDicxMB1VeessMTOg/edit?tab=t.0#heading=h.igl2myxbaf58) for the background and design. The only usage of `BundleStore` in this PR is artificial, but I'm working on a PR involving an action inst with 3 arguments, which requires something like `BundleStore`. --- toolchain/base/block_value_store.h | 28 +- toolchain/base/value_store.h | 7 + toolchain/check/action.cpp | 11 + toolchain/check/context.h | 1 + toolchain/check/dump.cpp | 5 + toolchain/check/eval.cpp | 49 ++- toolchain/check/pattern_match.cpp | 13 +- .../basics/raw_sem_ir/builtins.carbon | 1 + .../testdata/basics/raw_sem_ir/bundle.carbon | 308 +++++++++++++++++ .../basics/raw_sem_ir/cpp_interop.carbon | 1 + .../basics/raw_sem_ir/multifile.carbon | 2 + .../multifile_with_textual_ir.carbon | 2 + .../raw_sem_ir/non_core_interfaces.carbon | 2 + .../basics/raw_sem_ir/one_file.carbon | 1 + .../one_file_with_textual_ir.carbon | 1 + toolchain/driver/testdata/stdin.carbon | 1 + toolchain/sem_ir/BUILD | 2 + toolchain/sem_ir/bundle.h | 310 ++++++++++++++++++ toolchain/sem_ir/dump.cpp | 13 + toolchain/sem_ir/dump.h | 1 + toolchain/sem_ir/file.cpp | 5 +- toolchain/sem_ir/file.h | 7 + toolchain/sem_ir/formatter.h | 11 + toolchain/sem_ir/id_kind.h | 14 +- toolchain/sem_ir/ids.cpp | 9 + toolchain/sem_ir/ids.h | 31 +- toolchain/sem_ir/inst.cpp | 32 ++ toolchain/sem_ir/inst.h | 3 + toolchain/sem_ir/inst_fingerprinter.cpp | 10 + toolchain/sem_ir/typed_insts.h | 11 +- toolchain/sem_ir/yaml_test.cpp | 1 + 31 files changed, 865 insertions(+), 28 deletions(-) create mode 100644 toolchain/check/testdata/basics/raw_sem_ir/bundle.carbon create mode 100644 toolchain/sem_ir/bundle.h diff --git a/toolchain/base/block_value_store.h b/toolchain/base/block_value_store.h index 7f8d6e96b75e..1c3da4d8abd4 100644 --- a/toolchain/base/block_value_store.h +++ b/toolchain/base/block_value_store.h @@ -38,16 +38,20 @@ class BlockValueStore int32_t initial_reserved_ids = 0) requires(!IdTagIsUntagged) : allocator_(&allocator), values_(tag_id, initial_reserved_ids) { - auto empty = RefType(); - auto empty_val = canonical_blocks_.Insert( - empty, [&] { return values_.Add(empty); }, KeyContext(this)); - CARBON_CHECK(empty_val.key() == IdT::Empty); + if constexpr (requires { IdT::Empty; }) { + auto empty = RefType(); + auto empty_val = canonical_blocks_.Insert( + empty, [&] { return values_.Add(empty); }, KeyContext(this)); + CARBON_CHECK(empty_val.key() == IdT::Empty); + } } // Adds a block with the given content, returning an ID to reference it. auto Add(ConstRefType content) -> IdT { - if (content.empty()) { - return IdT::Empty; + if constexpr (requires { IdT::Empty; }) { + if (content.empty()) { + return IdT::Empty; + } } return values_.Add(AllocateCopy(content)); } @@ -71,8 +75,10 @@ class BlockValueStore // Adds a block or finds an existing canonical block with the given content, // and returns an ID to reference it. auto AddCanonical(ConstRefType content) -> IdT { - if (content.empty()) { - return IdT::Empty; + if constexpr (requires { IdT::Empty; }) { + if (content.empty()) { + return IdT::Empty; + } } auto result = canonical_blocks_.Insert( content, [&] { return Add(content); }, KeyContext(this)); @@ -111,10 +117,14 @@ class BlockValueStore canonical_blocks_, KeyContext(this)); } - auto size() const -> int { return values_.size(); } + auto size() const -> size_t { return values_.size(); } auto GetRawIndex(IdT id) const -> int { return values_.GetRawIndex(id); } + auto GetIdTag() const -> IdTagType { return values_.GetIdTag(); } + + auto ids() const -> auto { return values_.ids(); } + protected: // Allocates a copy of the given data using our slab allocator. auto AllocateCopy(ConstRefType data) -> RefType { diff --git a/toolchain/base/value_store.h b/toolchain/base/value_store.h index b543c195410d..9f3b059e41a8 100644 --- a/toolchain/base/value_store.h +++ b/toolchain/base/value_store.h @@ -200,6 +200,13 @@ class ValueStore auto size() const -> size_t { return size_; } + // Makes an iterable range over the IDs in the ValueStore. + auto ids() const -> auto { + return llvm::map_range(llvm::seq(size_), [tag = tag_](int32_t i) -> IdType { + return tag.Apply(i); + }); + } + // Makes an iterable range over references to all values in the ValueStore. auto values() [[clang::lifetimebound]] -> auto { return llvm::map_range(llvm::seq(size_), [&](int32_t i) -> RefType { diff --git a/toolchain/check/action.cpp b/toolchain/check/action.cpp index 94cd9f50e763..b9c6a1ab703b 100644 --- a/toolchain/check/action.cpp +++ b/toolchain/check/action.cpp @@ -114,6 +114,17 @@ static auto OperandDependence(Context& /*context*/, IdT /*id*/) return SemIR::ConstantDependence::None; } +template +static auto OperandDependence(Context& context, + SemIR::BundleId bundle_id) + -> SemIR::ConstantDependence { + return std::apply( + [&](auto... ids) { + return std::max({OperandDependence(context, ids)...}); + }, + context.bundles().GetAsTuple(bundle_id)); +} + template requires SemIR::Internal::IsIdKindType static auto OperandDependence(Context& /*context*/, IdT /*id*/) diff --git a/toolchain/check/context.h b/toolchain/check/context.h index db95427c3b6d..190388ad4f7e 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -378,6 +378,7 @@ class Context { return sem_ir().inst_blocks(); } auto constants() -> SemIR::ConstantStore& { return sem_ir().constants(); } + auto bundles() -> SemIR::BundleStore& { return sem_ir().bundles(); } auto total_ir_count() const -> int { return total_ir_count_; } // -------------------------------------------------------------------------- diff --git a/toolchain/check/dump.cpp b/toolchain/check/dump.cpp index d987646f7015..51e91da76555 100644 --- a/toolchain/check/dump.cpp +++ b/toolchain/check/dump.cpp @@ -38,6 +38,11 @@ LLVM_DUMP_METHOD static auto Dump(const Context& context, Parse::NodeId node_id) return Parse::Dump(context.parse_tree(), node_id); } +LLVM_DUMP_METHOD static auto Dump(const Context& context, + SemIR::RawBundleId bundle_id) -> std::string { + return SemIR::Dump(context.sem_ir(), bundle_id); +} + LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::ClassId class_id) -> std::string { return SemIR::Dump(context.sem_ir(), class_id); diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 8cc076ea43fc..dabf8fac1a02 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -848,6 +848,23 @@ static auto GetConstantValue(EvalContext& eval_context, return eval_context.entity_names().MakeCanonical(entity_name_id); } +// Returns the constant value of `id` if it has a `GetConstantValue` overload, +// and otherwise returns `id` itself. +template +static auto GetConstantValueOrPassThrough(EvalContext& eval_context, IdT id, + Phase* phase) -> IdT; + +template +static auto GetConstantValue(EvalContext& eval_context, + SemIR::BundleId bundle_id, Phase* phase) + -> SemIR::BundleId { + return eval_context.context().bundles().AddCanonical(std::apply( + [&](Ids... ids) -> BundleT { + return {GetConstantValueOrPassThrough(eval_context, ids, phase)...}; + }, + eval_context.context().bundles().GetAsTuple(bundle_id))); +} + // Replaces the specified field of the given typed instruction with its constant // value, if it has constant phase. Returns true on success, false if the value // has runtime phase. @@ -877,6 +894,16 @@ static constexpr bool HasGetConstantValueOverload = requires { AcceptIdT>(GetConstantValue); }; +template +static auto GetConstantValueOrPassThrough(EvalContext& eval_context, IdT id, + Phase* phase) -> IdT { + if constexpr (HasGetConstantValueOverload) { + return GetConstantValue(eval_context, id, phase); + } else { + return id; + } +} + // Given the stored value `arg` of an instruction field and its corresponding // kind `kind`, returns the constant value to use for that field, if it has a // constant phase. `*phase` is updated to include the new constant value. If @@ -886,15 +913,7 @@ static auto GetConstantValueForArg(EvalContext& eval_context, SemIR::IdAndKind arg_and_kind, Phase* phase) -> int32_t { return arg_and_kind.Dispatch([&](IdT id) -> int32_t { - if constexpr (HasGetConstantValueOverload) { - // If we have a custom `GetConstantValue` overload, call it. - return SemIR::ToRaw(GetConstantValue(eval_context, id, phase)); - } else if constexpr (std::same_as) { - CARBON_FATAL("Unexpected invalid IdKind"); - } else { - // Otherwise, we assume the value is already constant. - return SemIR::ToRaw(id); - } + return SemIR::ToRaw(GetConstantValueOrPassThrough(eval_context, id, phase)); }); } @@ -1028,6 +1047,18 @@ static auto ResolveSpecificDeclForArg(EvalContext& /*eval_context*/, IdT /*id*/) IdT::Label); } } + +template +static auto ResolveSpecificDeclForArg(EvalContext& eval_context, + SemIR::BundleId bundle_id) + -> void { + std::apply( + [&](auto... ids) -> void { + (..., ResolveSpecificDeclForArg(eval_context, ids)); + }, + eval_context.context().bundles().GetAsTuple(bundle_id)); +} + // Resolves the specific declarations for a specific id in any field of the // `inst` instruction. static auto ResolveSpecificDeclForInst(EvalContext& eval_context, diff --git a/toolchain/check/pattern_match.cpp b/toolchain/check/pattern_match.cpp index d134d4d5da3b..81831a2566bd 100644 --- a/toolchain/check/pattern_match.cpp +++ b/toolchain/check/pattern_match.cpp @@ -767,8 +767,10 @@ auto MatchContext::DoPreWork(State state, SemIR::SpliceInst splice, context_, SemIR::LocId(entry.pattern_id), context_.types().GetTypeInstId(splice.type_id), {.type_id = SemIR::InstType::TypeId, - .pattern_id = entry.pattern_id, - .parent_index = callee_state->index.Allocate()}); + .args_id = + context_.bundles().Add( + {.pattern_id = entry.pattern_id, + .parent_index = callee_state->index.Allocate()})}); callee_state->call_param_patterns.push_back(entry.pattern_id); callee_state->call_params.push_back(result_id); results_stack_.AppendToTop(result_id); @@ -976,15 +978,16 @@ auto ThunkPatternMatch(Context& context, SemIR::InstId self_pattern_id, auto PerformAction(Context& context, SemIR::LocId /*loc_id*/, SemIR::CalleePatternMatchAction action) -> SemIR::InstId { - CalleeState state = {.index = IndexSource(action.parent_index)}; + auto args = context.bundles().Get(action.args_id); + CalleeState state = {.index = IndexSource(args.parent_index)}; MatchContext match(context); auto result_id = match.MatchWithResult( &state, - {.pattern_id = action.pattern_id, + {.pattern_id = args.pattern_id, .work = MatchContext::PreWork{.scrutinee_id = SemIR::InstId::None}, .allow_unmarked_ref = true}); - CARBON_CHECK(state.index.Peek().index <= action.parent_index.index + 1, + CARBON_CHECK(state.index.Peek().index <= args.parent_index.index + 1, "TODO: add support for composite forms"); return result_id; } diff --git a/toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon b/toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon index 6f0e871a220a..d2adf8f18a65 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon @@ -78,6 +78,7 @@ // CHECK:STDOUT: 'inst(VtableType)': {kind: VtableType, type: type(TypeType)} // CHECK:STDOUT: 'inst(WitnessType)': {kind: WitnessType, type: type(TypeType)} // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: 'inst(TypeType)': concrete_constant(inst(TypeType)) diff --git a/toolchain/check/testdata/basics/raw_sem_ir/bundle.carbon b/toolchain/check/testdata/basics/raw_sem_ir/bundle.carbon new file mode 100644 index 000000000000..55d4564cbcd3 --- /dev/null +++ b/toolchain/check/testdata/basics/raw_sem_ir/bundle.carbon @@ -0,0 +1,308 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/form.carbon +// EXTRA-ARGS: --dump-raw-sem-ir --no-dump-sem-ir +// +// Check that raw bundle dumping works as expected. +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_sem_ir/bundle.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_sem_ir/bundle.carbon + +fn F(Form:! Core.Form()) ->? Form; + +// CHECK:STDOUT: --- +// CHECK:STDOUT: filename: bundle.carbon +// CHECK:STDOUT: sem_ir: +// CHECK:STDOUT: names: +// CHECK:STDOUT: name0: F +// CHECK:STDOUT: name1: Form +// CHECK:STDOUT: import_irs: +// CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} +// CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} +// CHECK:STDOUT: import_ir70000002: {decl_id: inst70000010, is_export: false} +// CHECK:STDOUT: import_ir70000003: {decl_id: inst70000010, is_export: false} +// CHECK:STDOUT: import_ir_insts: +// CHECK:STDOUT: import_ir_inst0: {ir_id: import_ir70000003, inst_id: inst50000018} +// CHECK:STDOUT: import_ir_inst1: {ir_id: import_ir70000003, inst_id: inst50000018} +// CHECK:STDOUT: import_ir_inst2: {ir_id: import_ir70000003, inst_id: inst50000014} +// CHECK:STDOUT: import_ir_inst3: {ir_id: import_ir70000003, inst_id: inst50000015} +// CHECK:STDOUT: import_ir_inst4: {ir_id: import_ir70000003, inst_id: inst50000010} +// CHECK:STDOUT: import_ir_inst5: {ir_id: import_ir70000003, inst_id: inst50000011} +// CHECK:STDOUT: clang_decls: {} +// CHECK:STDOUT: name_scopes: +// CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name(Core): inst70000011, name0: inst7000003B}} +// CHECK:STDOUT: name_scope70000001: {inst: inst70000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst70000016}} +// CHECK:STDOUT: entity_names: +// CHECK:STDOUT: entity_name70000000: {name: name(PeriodSelf), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: inst} +// CHECK:STDOUT: entity_name70000001: {name: name1, parent_scope: name_scope70000001, index: -1, is_template: 0, is_unused: 0, form: inst} +// CHECK:STDOUT: entity_name70000002: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: inst} +// CHECK:STDOUT: cpp_global_vars: {} +// CHECK:STDOUT: functions: +// CHECK:STDOUT: function70000000: {name: name1, parent_scope: name_scope70000001, call_param_patterns_id: inst_block70000006, return_type_inst_id: inst7000001E, return_form_inst_id: inst7000001F, return_pattern_id: inst7000001C} +// CHECK:STDOUT: function70000001: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block70000008, call_params_id: inst_block70000009, return_type_inst_id: inst7000002E, return_form_inst_id: inst7000002B, return_pattern_id: inst70000035} +// CHECK:STDOUT: classes: {} +// CHECK:STDOUT: interfaces: {} +// CHECK:STDOUT: associated_constants: {} +// CHECK:STDOUT: impls: {} +// CHECK:STDOUT: generics: +// CHECK:STDOUT: generic70000000: {decl: inst7000003B, bindings: inst_block7000000C, self_specific_id: specific70000000, decl_block_id: inst_block7000000E, definition_block_id: inst_block} +// CHECK:STDOUT: specifics: +// CHECK:STDOUT: specific70000000: {generic: generic70000000, args: inst_block7000000D, decl_block_id: inst_block7000000F, decl_has_error: 0, definition_block_id: inst_block, definition_has_error: 0} +// CHECK:STDOUT: specific_interfaces: {} +// CHECK:STDOUT: struct_type_fields: +// CHECK:STDOUT: struct_type_fields_empty: {} +// CHECK:STDOUT: types: +// CHECK:STDOUT: 'type(TypeType)': +// CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 +// CHECK:STDOUT: 'type(inst(FormType))': +// CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 +// CHECK:STDOUT: 'type(Error)': +// CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 +// CHECK:STDOUT: 'type(inst(InstType))': +// CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(InstType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 +// CHECK:STDOUT: 'type(inst(NamespaceType))': +// CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 +// CHECK:STDOUT: 'type(inst70000043)': +// CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000044)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 +// CHECK:STDOUT: 'type(inst70000044)': +// CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000044)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 +// CHECK:STDOUT: facet_types: +// CHECK:STDOUT: facet_type70000000: {} +// CHECK:STDOUT: insts: +// CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} +// CHECK:STDOUT: inst70000010: {kind: ImportDecl, arg0: name(Core)} +// CHECK:STDOUT: inst70000011: {kind: Namespace, arg0: name_scope70000001, arg1: inst70000010, type: type(inst(NamespaceType))} +// CHECK:STDOUT: inst70000012: {kind: FacetType, arg0: facet_type70000000, type: type(TypeType)} +// CHECK:STDOUT: inst70000013: {kind: SymbolicBinding, arg0: entity_name70000000, arg1: inst, type: type(inst70000012)} +// CHECK:STDOUT: inst70000014: {kind: SymbolicBinding, arg0: entity_name70000000, arg1: inst, type: type(inst70000012)} +// CHECK:STDOUT: inst70000015: {kind: NameRef, arg0: name(Core), arg1: inst70000011, type: type(inst(NamespaceType))} +// CHECK:STDOUT: inst70000016: {kind: ImportRefLoaded, arg0: import_ir_inst0, arg1: entity_name70000001, type: type(inst70000018)} +// CHECK:STDOUT: inst70000017: {kind: FunctionDecl, arg0: function70000000, arg1: inst_block_empty, type: type(inst70000018)} +// CHECK:STDOUT: inst70000018: {kind: FunctionType, arg0: function70000000, arg1: specific, type: type(TypeType)} +// CHECK:STDOUT: inst70000019: {kind: StructValue, arg0: inst_block_empty, type: type(inst70000018)} +// CHECK:STDOUT: inst7000001A: {kind: PatternType, arg0: inst(TypeType), type: type(TypeType)} +// CHECK:STDOUT: inst7000001B: {kind: OutParamPattern, arg0: name(ReturnSlot), type: type(inst7000001A)} +// CHECK:STDOUT: inst7000001C: {kind: ReturnSlotPattern, arg0: inst7000001B, arg1: inst, type: type(inst7000001A)} +// CHECK:STDOUT: inst7000001D: {kind: InitForm, arg0: inst(TypeType), type: type(inst(FormType))} +// CHECK:STDOUT: inst7000001E: {kind: ImportRefLoaded, arg0: import_ir_inst4, arg1: entity_name, type: type(TypeType)} +// CHECK:STDOUT: inst7000001F: {kind: ImportRefLoaded, arg0: import_ir_inst5, arg1: entity_name, type: type(inst(FormType))} +// CHECK:STDOUT: inst70000020: {kind: NameRef, arg0: name1, arg1: inst70000016, type: type(inst70000018)} +// CHECK:STDOUT: inst70000021: {kind: Call, arg0: inst70000020, arg1: inst_block_empty, type: type(TypeType)} +// CHECK:STDOUT: inst70000022: {kind: ValueOfInitializer, arg0: inst70000021, type: type(TypeType)} +// CHECK:STDOUT: inst70000023: {kind: Converted, arg0: inst70000021, arg1: inst70000022, type: type(TypeType)} +// CHECK:STDOUT: inst70000024: {kind: PatternType, arg0: inst(FormType), type: type(TypeType)} +// CHECK:STDOUT: inst70000025: {kind: SymbolicBinding, arg0: entity_name70000002, arg1: inst, type: type(inst(FormType))} +// CHECK:STDOUT: inst70000026: {kind: SymbolicBinding, arg0: entity_name70000002, arg1: inst, type: type(inst(FormType))} +// CHECK:STDOUT: inst70000027: {kind: SymbolicBinding, arg0: entity_name70000002, arg1: inst, type: type(inst(FormType))} +// CHECK:STDOUT: inst70000028: {kind: SymbolicBindingPattern, arg0: entity_name70000002, type: type(inst70000024)} +// CHECK:STDOUT: inst70000029: {kind: NameRef, arg0: name1, arg1: inst70000025, type: type(inst(FormType))} +// CHECK:STDOUT: inst7000002A: {kind: RefineFormAction, arg0: inst70000029, type: type(inst(InstType))} +// CHECK:STDOUT: inst7000002B: {kind: SpliceInst, arg0: inst7000002A, type: type(inst(FormType))} +// CHECK:STDOUT: inst7000002C: {kind: SpliceInst, arg0: inst7000002A, type: type(inst(FormType))} +// CHECK:STDOUT: inst7000002D: {kind: SpliceInst, arg0: inst7000002A, type: type(inst(FormType))} +// CHECK:STDOUT: inst7000002E: {kind: TypeComponentOf, arg0: inst7000002C, type: type(TypeType)} +// CHECK:STDOUT: inst7000002F: {kind: PatternType, arg0: inst7000002E, type: type(TypeType)} +// CHECK:STDOUT: inst70000030: {kind: OutFormParamPatternAction, arg0: inst7000002B, type: type(inst(InstType))} +// CHECK:STDOUT: inst70000031: {kind: SpliceInst, arg0: inst70000030, type: type(symbolic_constant7000000A)} +// CHECK:STDOUT: inst70000032: {kind: SpliceInst, arg0: inst70000030, type: type(symbolic_constant70000006)} +// CHECK:STDOUT: inst70000033: {kind: TypeComponentOf, arg0: inst7000002D, type: type(TypeType)} +// CHECK:STDOUT: inst70000034: {kind: SpliceInst, arg0: inst70000030, type: type(symbolic_constant7000000A)} +// CHECK:STDOUT: inst70000035: {kind: ReturnSlotPattern, arg0: inst70000031, arg1: inst7000002E, type: type(symbolic_constant7000000C)} +// CHECK:STDOUT: inst70000036: {kind: PatternType, arg0: inst70000033, type: type(TypeType)} +// CHECK:STDOUT: inst70000037: {kind: SpliceBlock, arg0: inst_block70000005, arg1: inst70000023, type: type(TypeType)} +// CHECK:STDOUT: inst70000038: {kind: CalleePatternMatchAction, arg0: bundle70000000, type: type(inst(InstType))} +// CHECK:STDOUT: inst70000039: {kind: SpliceInst, arg0: inst70000038, type: type(symbolic_constant7000000A)} +// CHECK:STDOUT: inst7000003A: {kind: ReturnSlot, arg0: inst7000002E, arg1: inst70000039, type: type(symbolic_constant7000000A)} +// CHECK:STDOUT: inst7000003B: {kind: FunctionDecl, arg0: function70000001, arg1: inst_block7000000B, type: type(inst70000043)} +// CHECK:STDOUT: inst7000003C: {kind: RefineFormAction, arg0: inst70000026, type: type(inst(InstType))} +// CHECK:STDOUT: inst7000003D: {kind: SpliceInst, arg0: inst7000003C, type: type(inst(FormType))} +// CHECK:STDOUT: inst7000003E: {kind: OutFormParamPatternAction, arg0: inst7000003D, type: type(inst(InstType))} +// CHECK:STDOUT: inst7000003F: {kind: TypeComponentOf, arg0: inst7000003D, type: type(TypeType)} +// CHECK:STDOUT: inst70000040: {kind: SpliceInst, arg0: inst7000003E, type: type(symbolic_constant70000011)} +// CHECK:STDOUT: inst70000041: {kind: PatternType, arg0: inst7000003F, type: type(TypeType)} +// CHECK:STDOUT: inst70000042: {kind: CalleePatternMatchAction, arg0: bundle70000001, type: type(inst(InstType))} +// CHECK:STDOUT: inst70000043: {kind: FunctionType, arg0: function70000001, arg1: specific, type: type(TypeType)} +// CHECK:STDOUT: inst70000044: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} +// CHECK:STDOUT: inst70000045: {kind: StructValue, arg0: inst_block_empty, type: type(inst70000043)} +// CHECK:STDOUT: bundles: +// CHECK:STDOUT: bundle70000000: {arg0: inst70000031, arg1: call_param0} +// CHECK:STDOUT: bundle70000001: {arg0: inst70000040, arg1: call_param0} +// CHECK:STDOUT: constant_values: +// CHECK:STDOUT: values: +// CHECK:STDOUT: instF: concrete_constant(instF) +// CHECK:STDOUT: inst70000011: concrete_constant(inst70000011) +// CHECK:STDOUT: inst70000012: concrete_constant(inst70000012) +// CHECK:STDOUT: inst70000013: symbolic_constant70000000 +// CHECK:STDOUT: inst70000014: symbolic_constant70000000 +// CHECK:STDOUT: inst70000015: concrete_constant(inst70000011) +// CHECK:STDOUT: inst70000016: concrete_constant(inst70000019) +// CHECK:STDOUT: inst70000017: concrete_constant(inst70000019) +// CHECK:STDOUT: inst70000018: concrete_constant(inst70000018) +// CHECK:STDOUT: inst70000019: concrete_constant(inst70000019) +// CHECK:STDOUT: inst7000001A: concrete_constant(inst7000001A) +// CHECK:STDOUT: inst7000001B: concrete_constant(inst7000001B) +// CHECK:STDOUT: inst7000001C: concrete_constant(inst7000001C) +// CHECK:STDOUT: inst7000001D: concrete_constant(inst7000001D) +// CHECK:STDOUT: inst7000001E: concrete_constant(inst(TypeType)) +// CHECK:STDOUT: inst7000001F: concrete_constant(inst7000001D) +// CHECK:STDOUT: inst70000020: concrete_constant(inst70000019) +// CHECK:STDOUT: inst70000021: concrete_constant(inst(FormType)) +// CHECK:STDOUT: inst70000022: concrete_constant(inst(FormType)) +// CHECK:STDOUT: inst70000023: concrete_constant(inst(FormType)) +// CHECK:STDOUT: inst70000024: concrete_constant(inst70000024) +// CHECK:STDOUT: inst70000025: symbolic_constant70000002 +// CHECK:STDOUT: inst70000026: symbolic_constant70000001 +// CHECK:STDOUT: inst70000027: symbolic_constant70000002 +// CHECK:STDOUT: inst70000028: concrete_constant(inst70000028) +// CHECK:STDOUT: inst70000029: symbolic_constant70000002 +// CHECK:STDOUT: inst7000002A: symbolic_constant70000003 +// CHECK:STDOUT: inst7000002B: symbolic_constant70000005 +// CHECK:STDOUT: inst7000002C: symbolic_constant70000004 +// CHECK:STDOUT: inst7000002D: symbolic_constant70000005 +// CHECK:STDOUT: inst7000002E: symbolic_constant70000006 +// CHECK:STDOUT: inst7000002F: symbolic_constant70000007 +// CHECK:STDOUT: inst70000030: symbolic_constant70000008 +// CHECK:STDOUT: inst70000031: symbolic_constant7000000B +// CHECK:STDOUT: inst70000032: symbolic_constant70000009 +// CHECK:STDOUT: inst70000033: symbolic_constant7000000A +// CHECK:STDOUT: inst70000034: symbolic_constant7000000B +// CHECK:STDOUT: inst70000035: concrete_constant(inst70000035) +// CHECK:STDOUT: inst70000036: symbolic_constant7000000C +// CHECK:STDOUT: inst70000037: concrete_constant(inst(FormType)) +// CHECK:STDOUT: inst70000038: symbolic_constant7000000D +// CHECK:STDOUT: inst7000003B: concrete_constant(inst70000045) +// CHECK:STDOUT: inst7000003C: symbolic_constant7000000E +// CHECK:STDOUT: inst7000003D: symbolic_constant7000000F +// CHECK:STDOUT: inst7000003E: symbolic_constant70000010 +// CHECK:STDOUT: inst7000003F: symbolic_constant70000011 +// CHECK:STDOUT: inst70000040: symbolic_constant70000012 +// CHECK:STDOUT: inst70000041: symbolic_constant70000013 +// CHECK:STDOUT: inst70000042: symbolic_constant70000014 +// CHECK:STDOUT: inst70000043: concrete_constant(inst70000043) +// CHECK:STDOUT: inst70000044: concrete_constant(inst70000044) +// CHECK:STDOUT: inst70000045: concrete_constant(inst70000045) +// CHECK:STDOUT: symbolic_constants: +// CHECK:STDOUT: symbolic_constant70000000: {inst: inst70000014, kind: self, attached: null} +// CHECK:STDOUT: symbolic_constant70000001: {inst: inst70000026, kind: checked, attached: null} +// CHECK:STDOUT: symbolic_constant70000002: {inst: inst70000026, kind: checked, attached: {generic: generic70000000, index: generic_inst_in_decl0}} +// CHECK:STDOUT: symbolic_constant70000003: {inst: inst7000002A, kind: template, attached: {generic: generic70000000, index: generic_inst_in_decl1}} +// CHECK:STDOUT: symbolic_constant70000004: {inst: inst7000002C, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant70000005: {inst: inst7000002C, kind: template, attached: {generic: generic70000000, index: generic_inst_in_decl2}} +// CHECK:STDOUT: symbolic_constant70000006: {inst: inst7000002E, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant70000007: {inst: inst7000002F, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant70000008: {inst: inst70000030, kind: template, attached: {generic: generic70000000, index: generic_inst_in_decl3}} +// CHECK:STDOUT: symbolic_constant70000009: {inst: inst70000032, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant7000000A: {inst: inst7000002E, kind: template, attached: {generic: generic70000000, index: generic_inst_in_decl4}} +// CHECK:STDOUT: symbolic_constant7000000B: {inst: inst70000032, kind: template, attached: {generic: generic70000000, index: generic_inst_in_decl5}} +// CHECK:STDOUT: symbolic_constant7000000C: {inst: inst7000002F, kind: template, attached: {generic: generic70000000, index: generic_inst_in_decl6}} +// CHECK:STDOUT: symbolic_constant7000000D: {inst: inst70000038, kind: template, attached: {generic: generic70000000, index: generic_inst_in_decl7}} +// CHECK:STDOUT: symbolic_constant7000000E: {inst: inst7000003C, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant7000000F: {inst: inst7000003D, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant70000010: {inst: inst7000003E, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant70000011: {inst: inst7000003F, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant70000012: {inst: inst70000040, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant70000013: {inst: inst70000041, kind: template, attached: null} +// CHECK:STDOUT: symbolic_constant70000014: {inst: inst70000042, kind: template, attached: null} +// CHECK:STDOUT: inst_blocks: +// CHECK:STDOUT: inst_block_empty: {} +// CHECK:STDOUT: exports: +// CHECK:STDOUT: 0: inst7000003B +// CHECK:STDOUT: generated: {} +// CHECK:STDOUT: imports: +// CHECK:STDOUT: 0: inst70000011 +// CHECK:STDOUT: 1: inst70000016 +// CHECK:STDOUT: 2: inst70000017 +// CHECK:STDOUT: 3: inst7000001B +// CHECK:STDOUT: 4: inst7000001C +// CHECK:STDOUT: 5: inst7000001E +// CHECK:STDOUT: 6: inst7000001F +// CHECK:STDOUT: global_init: {} +// CHECK:STDOUT: inst_block70000005: +// CHECK:STDOUT: 0: inst70000013 +// CHECK:STDOUT: 1: inst70000015 +// CHECK:STDOUT: 2: inst70000020 +// CHECK:STDOUT: 3: inst70000021 +// CHECK:STDOUT: 4: inst70000022 +// CHECK:STDOUT: 5: inst70000023 +// CHECK:STDOUT: inst_block70000006: +// CHECK:STDOUT: 0: inst7000001B +// CHECK:STDOUT: inst_block70000007: +// CHECK:STDOUT: 0: inst70000028 +// CHECK:STDOUT: inst_block70000008: +// CHECK:STDOUT: 0: inst70000031 +// CHECK:STDOUT: inst_block70000009: +// CHECK:STDOUT: 0: inst70000039 +// CHECK:STDOUT: inst_block7000000A: +// CHECK:STDOUT: 0: inst70000028 +// CHECK:STDOUT: 1: inst70000035 +// CHECK:STDOUT: inst_block7000000B: +// CHECK:STDOUT: 0: inst70000029 +// CHECK:STDOUT: 1: inst7000002B +// CHECK:STDOUT: 2: inst70000031 +// CHECK:STDOUT: 3: inst70000037 +// CHECK:STDOUT: 4: inst70000025 +// CHECK:STDOUT: 5: inst70000039 +// CHECK:STDOUT: 6: inst7000003A +// CHECK:STDOUT: inst_block7000000C: +// CHECK:STDOUT: 0: inst70000025 +// CHECK:STDOUT: inst_block7000000D: +// CHECK:STDOUT: 0: inst70000026 +// CHECK:STDOUT: inst_block7000000E: +// CHECK:STDOUT: 0: inst70000027 +// CHECK:STDOUT: 1: inst7000002A +// CHECK:STDOUT: 2: inst7000002D +// CHECK:STDOUT: 3: inst70000030 +// CHECK:STDOUT: 4: inst70000033 +// CHECK:STDOUT: 5: inst70000034 +// CHECK:STDOUT: 6: inst70000036 +// CHECK:STDOUT: 7: inst70000038 +// CHECK:STDOUT: inst_block7000000F: +// CHECK:STDOUT: 0: inst70000026 +// CHECK:STDOUT: 1: inst7000003C +// CHECK:STDOUT: 2: inst7000003D +// CHECK:STDOUT: 3: inst7000003E +// CHECK:STDOUT: 4: inst7000003F +// CHECK:STDOUT: 5: inst70000040 +// CHECK:STDOUT: 6: inst70000041 +// CHECK:STDOUT: 7: inst70000042 +// CHECK:STDOUT: inst_block70000010: +// CHECK:STDOUT: 0: instF +// CHECK:STDOUT: 1: inst70000010 +// CHECK:STDOUT: 2: inst7000003B +// CHECK:STDOUT: value_stores: +// CHECK:STDOUT: shared_values: +// CHECK:STDOUT: ints: {} +// CHECK:STDOUT: reals: {} +// CHECK:STDOUT: floats: {} +// CHECK:STDOUT: identifiers: +// CHECK:STDOUT: identifier0: F +// CHECK:STDOUT: identifier1: Form +// CHECK:STDOUT: strings: +// CHECK:STDOUT: string0: prelude +// CHECK:STDOUT: ... diff --git a/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon b/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon index 4facacad6fb4..73120bef862d 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon @@ -268,6 +268,7 @@ fn G(x: Cpp.X) { // CHECK:STDOUT: inst50000054: {kind: AddrOf, arg0: inst50000053, type: type(inst50000020)} // CHECK:STDOUT: inst50000055: {kind: Call, arg0: inst50000043, arg1: inst_block5000001C, type: type(inst5000001E)} // CHECK:STDOUT: inst50000056: {kind: Return} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: 'inst(TypeType)': concrete_constant(inst(TypeType)) diff --git a/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon b/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon index 0e57d363ca90..449f82b7ead4 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon @@ -96,6 +96,7 @@ fn B() { // CHECK:STDOUT: inst50000012: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst50000013: {kind: StructValue, arg0: inst_block_empty, type: type(inst50000011)} // CHECK:STDOUT: inst50000014: {kind: Return} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) @@ -211,6 +212,7 @@ fn B() { // CHECK:STDOUT: inst7000001B: {kind: NameRef, arg0: name1, arg1: inst70000017, type: type(inst70000019)} // CHECK:STDOUT: inst7000001C: {kind: Call, arg0: inst7000001B, arg1: inst_block_empty, type: type(inst70000014)} // CHECK:STDOUT: inst7000001D: {kind: Return} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) diff --git a/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon b/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon index dcd5ec2f0946..a977a4cbe7d2 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon @@ -96,6 +96,7 @@ fn B() { // CHECK:STDOUT: inst50000012: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst50000013: {kind: StructValue, arg0: inst_block_empty, type: type(inst50000011)} // CHECK:STDOUT: inst50000014: {kind: Return} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) @@ -230,6 +231,7 @@ fn B() { // CHECK:STDOUT: inst7000001B: {kind: NameRef, arg0: name1, arg1: inst70000017, type: type(inst70000019)} // CHECK:STDOUT: inst7000001C: {kind: Call, arg0: inst7000001B, arg1: inst_block_empty, type: type(inst70000014)} // CHECK:STDOUT: inst7000001D: {kind: Return} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) diff --git a/toolchain/check/testdata/basics/raw_sem_ir/non_core_interfaces.carbon b/toolchain/check/testdata/basics/raw_sem_ir/non_core_interfaces.carbon index 8089b408f9b1..860fa0336319 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/non_core_interfaces.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/non_core_interfaces.carbon @@ -114,6 +114,7 @@ fn UseLocalCopy[T:! Copy](_: T.T1, _: T.T2) {} // CHECK:STDOUT: inst6000001C: {kind: AssociatedConstantDecl, arg0: assoc_const60000001, arg1: inst_block6000000C, type: type(TypeType)} // CHECK:STDOUT: inst6000001D: {kind: AssociatedEntity, arg0: element1, arg1: inst6000001C, type: type(inst60000018)} // CHECK:STDOUT: inst6000001E: {kind: AssociatedEntity, arg0: element1, arg1: inst6000001C, type: type(inst60000018)} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) @@ -487,6 +488,7 @@ fn UseLocalCopy[T:! Copy](_: T.T1, _: T.T2) {} // CHECK:STDOUT: inst5000009C: {kind: RequireCompleteType, arg0: inst50000089, type: type(inst(WitnessType))} // CHECK:STDOUT: inst5000009D: {kind: RequireCompleteType, arg0: inst5000008A, type: type(inst(WitnessType))} // CHECK:STDOUT: inst5000009E: {kind: Return} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) diff --git a/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon b/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon index 36024725b7ca..4f41631e4231 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon @@ -887,6 +887,7 @@ fn Foo[T:! type](p: T*) -> (T*, ()) { // CHECK:STDOUT: inst78000189: {kind: TupleInit, arg0: inst_block7800008A, arg1: inst7800003C, type: type(symbolic_constant7800000A)} // CHECK:STDOUT: inst7800018A: {kind: Converted, arg0: inst7800004A, arg1: inst78000189, type: type(symbolic_constant7800000A)} // CHECK:STDOUT: inst7800018B: {kind: ReturnExpr, arg0: inst7800018A, arg1: inst7800003C} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) diff --git a/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon b/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon index f3d9352e9c66..497de519c45e 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon @@ -136,6 +136,7 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: inst50000038: {kind: TupleInit, arg0: inst_block50000012, arg1: inst50000028, type: type(inst5000001A)} // CHECK:STDOUT: inst50000039: {kind: Converted, arg0: inst5000002F, arg1: inst50000038, type: type(inst5000001A)} // CHECK:STDOUT: inst5000003A: {kind: ReturnExpr, arg0: inst50000039, arg1: inst50000028} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) diff --git a/toolchain/driver/testdata/stdin.carbon b/toolchain/driver/testdata/stdin.carbon index 4a5c166ac6bf..7bfaf0d25988 100644 --- a/toolchain/driver/testdata/stdin.carbon +++ b/toolchain/driver/testdata/stdin.carbon @@ -75,6 +75,7 @@ // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} +// CHECK:STDOUT: bundles: {} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 2983fa4e6ad9..bad6f8e23007 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -104,6 +104,7 @@ cc_library( hdrs = [ "associated_constant.h", "builtin_function_kind.h", + "bundle.h", "class.h", "constant.h", "copy_on_write_block.h", @@ -153,6 +154,7 @@ cc_library( "//common:struct_reflection", "//toolchain/base:block_value_store", "//toolchain/base:canonical_value_store", + "//toolchain/base:id_tag", "//toolchain/base:index_base", "//toolchain/base:int", "//toolchain/base:kind_switch", diff --git a/toolchain/sem_ir/bundle.h b/toolchain/sem_ir/bundle.h new file mode 100644 index 000000000000..74a7308ddb4f --- /dev/null +++ b/toolchain/sem_ir/bundle.h @@ -0,0 +1,310 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef CARBON_TOOLCHAIN_SEM_IR_BUNDLE_H_ +#define CARBON_TOOLCHAIN_SEM_IR_BUNDLE_H_ + +#include +#include + +#include "common/raw_string_ostream.h" +#include "common/struct_reflection.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringExtras.h" +#include "toolchain/base/block_value_store.h" +#include "toolchain/base/id_tag.h" +#include "toolchain/base/value_store.h" +#include "toolchain/sem_ir/id_kind.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::SemIR { + +// A value store for bundles of instruction arguments. +// +// When an inst needs to take more than two logical arguments, one of its +// physical arguments can be the ID of a "bundle" of extra arguments, which is +// stored here. The expected pattern looks like this: +// +// struct MyInst { +// struct Args { +// FooId arg1; +// BarId arg2; +// BazId arg3; +// }; +// +// static constexpr auto Kind = ... +// +// TypeId type_id; +// QuuxId arg0; +// BundleId extra_args; +// }; +// +// 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` +// itself must also be added to that list, although bundles should generally not +// have bundle IDs as members. +// +// Unlike insts, bundles do not record their own kind and the `BundleStore` +// is not guaranteed to record it either. Instead, that information is tracked +// by the user, usually in the bundle ID's static type. It may also be tracked +// dynamically with an `IdAndKind`, or discarded entirely by converting to a +// `RawBundleId`. +// +// The `BundleStore` can cache the kinds of the stored bundles for debugging +// purposes, but this must be requested separately by calling `CacheDebugKind` +// because it adds storage overhead, and it affects only the store's debug +// printing operations. +// TODO: Consider storing and populating the cache separately, and passing it +// into the debug printing operations. +// +// In rare cases, a single `RawBundleId` may correspond to multiple typed +// bundle IDs, and hence to multiple bundles with different types. However, +// this happens only with the canonical IDs of bundles that have the same +// representation (i.e. the same sequence of integer IDs), so this has no +// practical effect other than somewhat complicating the debug printing. +class BundleStore { + public: + // Construct a `BundleStore` that uses the given allocator, and applies the + // given tag to bundle IDs. + 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 + auto Add(const BundleT& bundle) -> BundleId { + return BundleId{store_.Add(BundleToArray(bundle))}; + } + + // Returns the canonical ID of the given bundle, allocating a new one if + // it does not already exist. + template + auto AddCanonical(const BundleT& bundle) -> BundleId { + return BundleId{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 + auto MakeCanonical(BundleId bundle_id) -> BundleId { + return BundleId{store_.MakeCanonical(bundle_id.index)}; + } + + // Returns the bundle with the given ID. + template + auto Get(BundleId bundle_id) const -> BundleT { + using TupleType = + decltype(StructReflection::AsTuple(std::declval())); + return FromArray::template AsBundle( + store_.Get(bundle_id)); + } + + // Returns a std::tuple of the fields of the bundle with the given ID, in + // declaration order. + template + auto GetAsTuple(BundleId bundle_id) const + -> decltype(StructReflection::AsTuple(std::declval())) { + using TupleType = + decltype(StructReflection::AsTuple(std::declval())); + return FromArray::AsTuple(store_.Get(bundle_id)); + } + + // Caches additional information about `bundle_id` for debug-printing + // purposes. This incurs some storage overhead, so it should only be called + // before one of the methods that relies on it. + template + auto CacheDebugKind(BundleId bundle_id) const -> void { + if (bundle_kind_cache_.size() < store_.size()) { + bundle_kind_cache_.Resize(store_.size(), IdKindSet{}); + } + bundle_kind_cache_.Get(bundle_id).insert(IdKind::For>); + } + + // Returns the contents of the bundle store as a YAML mapping of untyped + // bundle IDs to bundle values. If an untyped ID corresponds to multiple + // typed IDs, the entry for that ID will be a nested mapping with the + // different bundles as values. + // + // Each bundle's fields will be depicted with the correct ID kinds if + // CacheDebugKind was previously called for that bundle; otherwise they will + // be depicted as AnyRawIds, and may be conflated with other bundles that have + // the same untyped ID (and hence the same numeric field values). + auto OutputYaml() const -> Yaml::OutputMapping { + return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { + for (auto bundle_id : store_.ids()) { + AddYamlMapEntry(map, bundle_id); + } + }); + } + + // Equivalent to OutputYaml, but the resulting mapping will contain only + // the given bundle. + auto OutputBundleYaml(RawBundleId bundle_id) const -> Yaml::OutputMapping { + return Yaml::OutputMapping([this, bundle_id](Yaml::OutputMapping::Map map) { + AddYamlMapEntry(map, bundle_id); + }); + } + + // Adds the store's memory usage to mem_usage. + auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const + -> void { + store_.CollectMemUsage(mem_usage, label); + bundle_kind_cache_.CollectMemUsage(mem_usage, label); + } + + // The number of stored bundles. + auto size() const -> size_t { return store_.size(); } + + private: + // Comparator for sets of `IdKind`. + struct IdKindLess { + auto operator()(IdKind lhs, IdKind rhs) const -> bool { + return lhs.ToIndex() < rhs.ToIndex(); + } + }; + // A set of IdKinds, for use in the debug info cache. + using IdKindSet = llvm::SmallSet; + + // Returns the fields of the given bundle as a `std::array`. + template + auto BundleToArray(const BundleT& bundle) -> auto { + static_assert(std::is_aggregate_v, + "Only aggregates are supported"); + return std::apply( + [](auto... ids)->std::array { + return {AnyRawId(ToRaw(ids))...}; + }, + StructReflection::AsTuple(bundle)); + } + + // Helper class for converting an array of `AnyRawId`s back to typed IDs. + // TupleT must be a std::tuple of the bundle's field types, in declaration + // order. + template + class FromArray; + template + class FromArray> { + public: + // Returns the field values in `array` as a bundle of type `BundleT`. + template + static auto AsBundle(llvm::ArrayRef array) -> BundleT { + static_assert(std::is_aggregate_v, + "Only aggregates are supported"); + return As(array, std::make_index_sequence{}); + } + + // Returns the field values in `array` as a tuple of type `TupleT`. + static auto AsTuple(llvm::ArrayRef array) -> std::tuple { + return As>(array, + std::make_index_sequence{}); + } + + private: + template + static auto As(llvm::ArrayRef array, + std::index_sequence /*is*/) -> ResultT { + CARBON_CHECK(array.size() == sizeof...(Ts)); + return {FromRaw(array[Is].index)...}; + } + }; + + // Prints a single field of a bundle. + template + static auto PrintBundleField(llvm::raw_ostream& out, llvm::ListSeparator& sep, + size_t i, T field) -> void { + out << sep << "arg" << i << ": " << field; + } + + // Returns a YAML string representation of the bundle with the given ID. + // This is overloaded for all ID types, for use with `IdAndKind::Dispatch`, + // but should only actually be called with raw or typed bundle IDs. + template + auto BundleString(BundleId bundle_id) const -> std::string { + static_assert(std::is_aggregate_v, + "Only aggregates are supported"); + RawStringOstream out; + llvm::ListSeparator sep; + size_t i = 0; + out << "{"; + std::apply( + [&](auto... ids) { (..., PrintBundleField(out, sep, i++, ids)); }, + GetAsTuple(bundle_id)); + out << "}"; + return out.TakeStr(); + } + + auto BundleString(RawBundleId bundle_id) const -> std::string { + RawStringOstream out; + llvm::ListSeparator sep; + out << "{"; + for (auto [i, raw_id] : llvm::enumerate(store_.Get(bundle_id))) { + PrintBundleField(out, sep, i, raw_id); + } + out << "}"; + return out.TakeStr(); + } + + template + requires Internal::IsIdKindType + auto BundleString(IdT bundle_id) const -> std::string { + CARBON_FATAL("ID {} is not a bundle ID", bundle_id); + } + + // Adds an entry for `bundle_id` to `map`. + auto AddYamlMapEntry(Yaml::OutputMapping::Map& map, + RawBundleId bundle_id) const -> void { + auto kind_set = bundle_kind_cache_.GetWithDefault(bundle_id, IdKindSet{}); + if (kind_set.empty()) { + map.Add(PrintToString(bundle_id), + Yaml::OutputScalar(BundleString(bundle_id))); + } else if (kind_set.size() == 1) { + IdAndKind typed_bundle_id(*kind_set.begin(), bundle_id.index); + auto bundle_string = typed_bundle_id.Dispatch( + [this](auto id) { return BundleString(id); }); + map.Add(PrintToString(bundle_id), Yaml::OutputScalar(bundle_string)); + } else { + map.Add(PrintToString(bundle_id), YamlBundleMap(bundle_id, kind_set)); + } + } + + // Returns a YAML mapping with an entry for each ID kind in `kinds`, + // consisting of the string representation of the given bundle, interpreted as + // a bundle of that kind. All entries in `kinds` must be specializations of + // `BundleId`. + auto YamlBundleMap(RawBundleId bundle_id, const IdKindSet& kinds) const + -> Yaml::OutputMapping { + return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { + for (auto [i, id_kind] : llvm::enumerate(kinds)) { + IdAndKind typed_bundle_id(id_kind, bundle_id.index); + // TODO: make this a YAML sequence instead of a map. + map.Add(llvm::itostr(i), + typed_bundle_id.Dispatch( + [this](auto id) { return BundleString(id); })); + } + }); + } + + // The bundles in the store, represented as blocks of `AnyRawId`s. + // + // TODO: Consider instead representing this as a flat array of `AnyRawId`s, + // with the Bundle ID's index pointing to the start of the bundle in the + // array. This would probably be more efficient, because it would be a single + // contiguous allocation, and would avoid redundantly storing the size of + // each block. However, it would require a different approach to debug + // printing, which currently needs to get the bundle size from `store_` + // when the kind cache is incomplete or unavailable. + BlockValueStore> store_; + + // The cached ID kinds for the bundles in `store_`. + // + // TODO: Consider factoring this out as a separate class, which is populated + // by the user and then passed into the debug-printing methods. That would + // avoid the need for a mutable member, and reduce the risk of the cache + // being incomplete. + mutable ValueStore> bundle_kind_cache_; +}; + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_BUNDLE_H_ diff --git a/toolchain/sem_ir/dump.cpp b/toolchain/sem_ir/dump.cpp index ae4b08a807d6..d91dfad0d161 100644 --- a/toolchain/sem_ir/dump.cpp +++ b/toolchain/sem_ir/dump.cpp @@ -178,6 +178,16 @@ static auto DumpRequireImplsSummary(const File& file, return out.TakeStr(); } +LLVM_DUMP_METHOD auto Dump(const File& file, RawBundleId bundle_id) + -> std::string { + for (auto inst : file.insts().values()) { + inst.CacheBundleDebugKinds(file.bundles()); + } + RawStringOstream out; + Print(out, file.bundles().OutputBundleYaml(bundle_id)); + return out.TakeStr(); +} + LLVM_DUMP_METHOD auto Dump(const File& file, ClassId class_id) -> std::string { RawStringOstream out; out << class_id; @@ -600,6 +610,9 @@ static LLVM_DUMP_METHOD auto Dump(const File& file, const NameScope& scope) // Functions that can be used instead of the corresponding constructor, which is // unavailable during debugging. +LLVM_DUMP_METHOD static auto MakeBundleId(int id) -> RawBundleId { + return RawBundleId(id); +} LLVM_DUMP_METHOD static auto MakeClassId(int id) -> ClassId { return ClassId(id); } diff --git a/toolchain/sem_ir/dump.h b/toolchain/sem_ir/dump.h index bedae7f56cc3..a66f463261a7 100644 --- a/toolchain/sem_ir/dump.h +++ b/toolchain/sem_ir/dump.h @@ -20,6 +20,7 @@ namespace Carbon::SemIR { +auto Dump(const File& file, RawBundleId bundle_id) -> std::string; auto Dump(const File& file, ClassId class_id) -> std::string; auto Dump(const File& file, ConstantId const_id) -> std::string; auto Dump(const File& file, EntityNameId entity_name_id) -> std::string; diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index b3d3bbe86bcd..7d1867dd5b77 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -68,7 +68,8 @@ File::File(const Parse::Tree* parse_tree, CheckIRId check_ir_id, // 1 reserved id for `CustomLayoutId::Empty`. custom_layouts_(allocator_, check_ir_id, 1), expr_regions_(check_ir_id), - clang_source_locs_(check_ir_id) { + clang_source_locs_(check_ir_id), + bundles_(allocator_, check_ir_id) { // `type`, `form`, and the error type are both complete & concrete types. // TODO: This duplicates the code in `check/type_completion.cpp`. Consider // requiring these types to be complete from Check initialization instead, @@ -164,12 +165,14 @@ auto File::OutputYaml(bool include_singletons) const -> Yaml::OutputMapping { map.Add("insts", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { for (auto [id, inst] : insts_.enumerate()) { + inst.CacheBundleDebugKinds(bundles_); if (!include_singletons && IsSingletonInstId(id)) { continue; } map.Add(PrintToString(id), Yaml::OutputScalar(inst)); } })); + map.Add("bundles", bundles_.OutputYaml()); map.Add("constant_values", constant_values_.OutputYaml(include_singletons)); map.Add("inst_blocks", inst_blocks_.OutputYaml()); diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index aa8cdf21a093..d70313a483f8 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -18,6 +18,7 @@ #include "toolchain/base/yaml.h" #include "toolchain/parse/tree.h" #include "toolchain/sem_ir/associated_constant.h" +#include "toolchain/sem_ir/bundle.h" #include "toolchain/sem_ir/class.h" #include "toolchain/sem_ir/constant.h" #include "toolchain/sem_ir/cpp_file.h" @@ -276,6 +277,9 @@ class File : public Printable { return clang_source_locs_; } + auto bundles() -> BundleStore& { return bundles_; } + auto bundles() const -> const BundleStore& { return bundles_; } + auto top_inst_block_id() const -> InstBlockId { return top_inst_block_id_; } auto set_top_inst_block_id(InstBlockId block_id) -> void { top_inst_block_id_ = block_id; @@ -424,6 +428,9 @@ class File : public Printable { // C++ source locations for C++ interop. ClangSourceLocStore clang_source_locs_; + + // Storage for instruction argument bundles. + BundleStore bundles_; }; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/formatter.h b/toolchain/sem_ir/formatter.h index cf89df6bf66a..3e2123131149 100644 --- a/toolchain/sem_ir/formatter.h +++ b/toolchain/sem_ir/formatter.h @@ -279,6 +279,17 @@ class Formatter { auto FormatArg(StringLiteralValueId id) -> void; auto FormatArg(ConstantId id) -> void { FormatConstant(id); } + template + auto FormatArg(BundleId bundle_id) -> void { + llvm::ListSeparator sep; + auto format_with_sep = [&](auto id) { + out() << sep; + FormatArg(id); + }; + std::apply([&](auto... id) -> void { (..., format_with_sep(id)); }, + sem_ir_->bundles().GetAsTuple(bundle_id)); + } + // Calls `FormatArg` from an `IdAndKind`. auto FormatInstArgAndKind(IdAndKind arg_and_kind) -> void; diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index 6da2d5ff5123..560c04f11bcc 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -8,6 +8,7 @@ #include "common/type_enum.h" #include "toolchain/base/int.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/typed_insts.h" namespace Carbon::SemIR { @@ -31,6 +32,7 @@ using IdKind = TypeEnum< AnyRawId, AssociatedConstantId, BoolValue, + BundleId, CallParamIndex, CharId, ClangDeclId, @@ -62,6 +64,7 @@ using IdKind = TypeEnum< NameId, NameScopeId, NamedConstraintId, + RawBundleId, RequireImplsId, SpecificId, SpecificInterfaceId, @@ -165,7 +168,16 @@ concept IsIdKindType = SameAsOneOf; } -// Specialization for None. +// Specializations for Invalid and None. +inline auto ToRaw(IdAndKind::InvalidType /*invalid*/) -> int32_t { + CARBON_FATAL("Invalid ID kind"); +} +template + requires std::is_same_v +auto FromRaw(int32_t /*raw*/) -> IdAndKind::InvalidType { + CARBON_FATAL("Invalid ID kind"); +} + static constexpr auto ToRaw(IdAndKind::NoneType /*none*/) -> int32_t { return AnyIdBase::NoneIndex; } diff --git a/toolchain/sem_ir/ids.cpp b/toolchain/sem_ir/ids.cpp index 05dc46ed31bf..48b45caeca53 100644 --- a/toolchain/sem_ir/ids.cpp +++ b/toolchain/sem_ir/ids.cpp @@ -286,4 +286,13 @@ auto LocId::Print(llvm::raw_ostream& out) const -> void { } } +auto AnyRawId::Print(llvm::raw_ostream& out) const -> void { + out << AnyRawId::Label; + if (has_value()) { + out << llvm::format_hex_no_prefix(index, 0, /*Upper=*/true); + } else { + out << ""; + } +} + } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 3d5d2dde5ede..2010be67498d 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -988,6 +988,29 @@ struct RequireImplsBlockId : public IdBase { inline constexpr RequireImplsBlockId RequireImplsBlockId::Empty = RequireImplsBlockId(0); +// The ID of a bundle of arguments with type `BundleT`. +template +struct BundleId : public IdBase> { + static constexpr llvm::StringLiteral Label = "bundle"; + + using IdBase>::IdBase; +}; + +// The ID of a bundle of arguments with an unspecified type. +struct RawBundleId : public IdBase { + static constexpr llvm::StringLiteral Label = "bundle"; + + template + explicit(false) RawBundleId(BundleId bundle_id) + : IdBase(bundle_id.index) {} + using IdBase::IdBase; + + template + explicit operator BundleId() const { + return BundleId(index); + } +}; + // A SemIR location used as the location of instructions. This contains either a // InstId, NodeId, ImportIRInstId, or None. The intent is that any of these can // indicate the source of an instruction, and also be used to associate a line @@ -1120,12 +1143,18 @@ struct LocId : public IdBase { // - In the case the specific instruction has no field in the same position, the // `Any[...]` type will hold a default constructed `AnyRawId` with a `None` // value. -struct AnyRawId : public AnyIdBase { +struct AnyRawId : public AnyIdBase, Printable { // For IdKind. static constexpr llvm::StringLiteral Label = "any_raw"; constexpr explicit AnyRawId() : AnyIdBase(AnyIdBase::NoneIndex) {} constexpr explicit AnyRawId(int32_t id) : AnyIdBase(id) {} + + auto Print(llvm::raw_ostream& out) const -> void; + + friend auto operator==(AnyRawId lhs, AnyRawId rhs) -> bool { + return lhs.index == rhs.index; + } }; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/inst.cpp b/toolchain/sem_ir/inst.cpp index ec598ccd4ff1..943e399b89ec 100644 --- a/toolchain/sem_ir/inst.cpp +++ b/toolchain/sem_ir/inst.cpp @@ -36,6 +36,38 @@ auto Inst::Print(llvm::raw_ostream& out) const -> void { out << "}"; } +template +static auto CacheIfBundleId(T /*arg*/, const BundleStore& /*bundle_store*/) + -> void {} + +template +static auto CacheIfBundleId(BundleId arg, + const BundleStore& bundle_store) -> void { + bundle_store.CacheDebugKind(arg); +} + +auto Inst::CacheBundleDebugKinds(const BundleStore& bundles) const -> void { + auto cache_args = [&](auto info) { + using Info = decltype(info); + if constexpr (Info::NumArgs > 0) { + CacheIfBundleId(FromRaw>(arg0_), + bundles); + } + if constexpr (Info::NumArgs > 1) { + CacheIfBundleId(FromRaw>(arg1_), + bundles); + } + }; + + switch (kind()) { +#define CARBON_SEM_IR_INST_KIND(Name) \ + case Name::Kind: \ + cache_args(Internal::InstLikeTypeInfo()); \ + break; +#include "toolchain/sem_ir/inst_kind.def" + } +} + // Returns the IdKind of an instruction's argument, or None if there is no // argument with that index. template diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index af09c57f2133..19d42bf89fa4 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -17,6 +17,7 @@ #include "toolchain/base/index_base.h" #include "toolchain/base/int.h" #include "toolchain/base/value_store.h" +#include "toolchain/sem_ir/bundle.h" #include "toolchain/sem_ir/id_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst_kind.h" @@ -318,6 +319,8 @@ class Inst : public Printable { auto Print(llvm::raw_ostream& out) const -> void; + auto CacheBundleDebugKinds(const BundleStore& bundles) const -> void; + friend auto operator==(Inst lhs, Inst rhs) -> bool { return std::memcmp(&lhs, &rhs, sizeof(Inst)) == 0; } diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index cddd18a1a0bb..cbb88e699474 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/toolchain/sem_ir/inst_fingerprinter.cpp @@ -392,6 +392,16 @@ struct Worklist { ir_inst.inst_id()); } + template + auto Add(BundleId bundle_id) -> void { + std::apply([&](auto... ids) { (..., Add(ids)); }, + sem_ir->bundles().GetAsTuple(bundle_id)); + } + + auto Add(RawBundleId bundle_id) -> void { + CARBON_FATAL("Can't fingerprint untyped bundle ID {}", bundle_id); + } + template requires(SameAsOneOf) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index a617697c343b..48f1bced3c5a 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -396,9 +396,16 @@ struct CalleePatternMatchAction { .constant_kind = InstConstantKind::InstAction, .is_lowered = false}); + // TODO: Inline these args back into the inst. They are only factored out as + // a bundle to validate `BundleStore` until a followup PR introduces a + // "real" use case. + struct Args { + MetaInstId pattern_id; + CallParamIndex parent_index; + }; + TypeId type_id; - MetaInstId pattern_id; - CallParamIndex parent_index; + BundleId args_id; }; // A unicode code point character literal. This type only provides compile-time diff --git a/toolchain/sem_ir/yaml_test.cpp b/toolchain/sem_ir/yaml_test.cpp index 89659a19b2b5..a2842e3ea009 100644 --- a/toolchain/sem_ir/yaml_test.cpp +++ b/toolchain/sem_ir/yaml_test.cpp @@ -96,6 +96,7 @@ TEST(SemIRTest, Yaml) { "function[0-9A-F]+"))), Pair("arg1", "inst_block_empty"), Pair("type", type_id)))))))), + Pair("bundles", Yaml::Mapping(IsEmpty())), Pair("constant_values", Yaml::Mapping(ElementsAre( Pair("values",