From 8ad0c70f9f97ba36caa2d257ce78ce91f26325c8 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Thu, 22 Feb 2024 14:29:06 -0800 Subject: [PATCH] Add ClassDecl/Type import functionality. (#3709) I believe this PR is sufficient to pull in all current class features, including the current bits of inheritance which have been implemented. Because a class declaration can reference its own type, this creates an incomplete type prior to constant loading. Right now, the object representation is imported proactively, but individual fields are left as ImportRefUnused. This means that member functions and similar will only be imported if called. This also adjusts how function parameters are being handled, to match the expectations of Self param structure. When formatting, I'm starting to look into constants. Otherwise we get "unexpected instref". Overall, there are a few things that may be worth further discussion: - The lack of a constant corresponding to the ClassType on ClassDecl is inconvenient -- I'd like to see how zygoloid feels about trying to restructure this. i.e., I'm setting a constant in order to be able to track things down later, it'd be nice if the normal IR did this simply for consistency, or if we were able to combine these rather than having separate instructions. - Should we shift the parse node tracking further, and go with a setup wherein imports can embed import references into that? e.g., negative values go to another array which includes a ImportIRId for printing diagnostics, replacing the invalid NodeId. - Can the formatter switch to a more general scan of instructions for naming, to eliminate the ImportRef constant approach added here? - GetExprValueForLookupResult special-casing instructions felt surprising, I might see if there's a way to restructure to avoid that. But I think these issues are things that can be separated out. --- toolchain/check/handle_name.cpp | 47 ++- toolchain/check/import_ref.cpp | 343 ++++++++++++++++-- .../testdata/class/fail_import_misuses.carbon | 89 +++++ .../testdata/class/fail_todo_import.carbon | 165 --------- .../fail_todo_import_forward_decl.carbon | 62 ++++ toolchain/check/testdata/class/import.carbon | 210 +++++++++++ .../check/testdata/class/import_base.carbon | 165 +++++++++ .../testdata/class/import_member_cycle.carbon | 78 ++++ .../testdata/class/import_struct_cyle.carbon | 101 ++++++ .../function/declaration/import.carbon | 19 +- .../fail_todo_import_forward_decl.carbon | 3 +- .../function/definition/import.carbon | 19 +- ..._conflict_imported_namespace_second.carbon | 3 +- .../check/testdata/namespace/imported.carbon | 14 +- .../namespace/imported_indirect.carbon | 5 +- .../check/testdata/namespace/merging.carbon | 15 +- .../testdata/packages/loaded_global.carbon | 14 +- toolchain/sem_ir/formatter.cpp | 19 +- 18 files changed, 1105 insertions(+), 266 deletions(-) create mode 100644 toolchain/check/testdata/class/fail_import_misuses.carbon delete mode 100644 toolchain/check/testdata/class/fail_todo_import.carbon create mode 100644 toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon create mode 100644 toolchain/check/testdata/class/import.carbon create mode 100644 toolchain/check/testdata/class/import_base.carbon create mode 100644 toolchain/check/testdata/class/import_member_cycle.carbon create mode 100644 toolchain/check/testdata/class/import_struct_cyle.carbon diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 3ba758c192ff..85b3a651d5f5 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -49,21 +49,40 @@ static auto GetExprValueForLookupResult(Context& context, -> SemIR::InstId { // If lookup finds a class declaration, the value is its `Self` type. auto lookup_result = context.insts().Get(lookup_result_id); - if (auto class_decl = lookup_result.TryAs()) { - return context.types().GetInstId( - context.classes().Get(class_decl->class_id).self_type_id); + switch (lookup_result.kind()) { + case SemIR::ClassDecl::Kind: { + auto class_decl = lookup_result.As(); + return context.types().GetInstId( + context.classes().Get(class_decl.class_id).self_type_id); + } + case SemIR::InterfaceDecl::Kind: { + auto interface_decl = lookup_result.As(); + return TryEvalInst(context, SemIR::InstId::Invalid, + SemIR::InterfaceType{SemIR::TypeId::TypeType, + interface_decl.interface_id}) + .inst_id(); + } + case SemIR::ImportRefUsed::Kind: { + auto import_ref = lookup_result.As(); + const auto* import_ir = context.import_irs().Get(import_ref.ir_id); + auto import_kind = import_ir->insts().Get(import_ref.inst_id).kind(); + // For a declared type, recurse to get the appropriate type value. + // Otherwise, the ImportRefUsed is sufficient. + if (import_kind == SemIR::ClassDecl::Kind || + import_kind == SemIR::InterfaceDecl::Kind) { + return GetExprValueForLookupResult( + context, context.constant_values().Get(lookup_result_id).inst_id()); + } else { + return lookup_result_id; + } + } + default: + // Anything else should be a typed value already. + CARBON_CHECK(lookup_result.kind().value_kind() == + SemIR::InstValueKind::Typed) + << "Unexpected kind for lookup result, " << lookup_result; + return lookup_result_id; } - if (auto interface_decl = lookup_result.TryAs()) { - return TryEvalInst(context, SemIR::InstId::Invalid, - SemIR::InterfaceType{SemIR::TypeId::TypeType, - interface_decl->interface_id}) - .inst_id(); - } - - // Anything else should be a typed value already. - CARBON_CHECK(lookup_result.kind().value_kind() == SemIR::InstValueKind::Typed) - << "Unexpected kind for lookup result, " << lookup_result; - return lookup_result_id; } static auto GetClassElementIndex(Context& context, SemIR::InstId element_id) diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 1af6bb7fcda6..8c2a23104c98 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -19,6 +19,37 @@ namespace Carbon::Check { // Resolves an instruction from an imported IR into a constant referring to the // current IR. +// +// Calling Resolve on an instruction operates in an iterative manner, tracking +// Work items on work_stack_. At a high level, the loop is: +// +// 1. If Work has received a constant, it's considered resolved. +// - If made_incomplete_type, resolve unconditionally. +// - The constant check avoids performance costs of deduplication on add. +// 2. Resolve the instruction: (TryResolveInst/TryResolveTypedInst) +// - For most cases: +// A. For types which _can_ be incomplete, when not made_incomplete_type: +// i. Start by making an incomplete type to address circular references. +// ii. If the imported type is incomplete, return the constant. +// iii. Otherwise, set made_incomplete_type and continue resolving. +// - Creating an incomplete type will have set the constant, which +// influences step (1); setting made_incomplete_type gets us a second +// resolve pass when needed. +// B. Gather all input constants. +// - Gathering constants directly adds unresolved values to work_stack_. +// C. If any need to be resolved (HasNewWork), return Invalid; this +// instruction needs two calls to complete. +// D. Build any necessary IR structures, and return the output constant. +// - For trivial cases with zero or one input constants, this may return +// a constant (if one, potentially Invalid) directly. +// 3. If resolving returned a non-Invalid constant, pop the work; otherwise, it +// needs to remain (and may no longer be at the top of the stack). +// +// TryResolveInst/TryResolveTypedInst can complete in one call for a given +// instruction, but should always complete within two calls. However, due to the +// chance of a second call, it's important to reserve all expensive logic until +// it's been established that input constants are available; this in particular +// includes GetTypeIdForTypeConstant calls which do a hash table lookup. class ImportRefResolver { public: explicit ImportRefResolver(Context& context, SemIR::ImportIRId import_ir_id) @@ -33,20 +64,22 @@ class ImportRefResolver { // instruction has unresolved inner references, it will add them to the stack // for inner evaluation and reattempt outer evaluation after. auto Resolve(SemIR::InstId inst_id) -> SemIR::ConstantId { - work_stack_.push_back(inst_id); + work_stack_.push_back({inst_id}); while (!work_stack_.empty()) { - auto inst_id = work_stack_.back(); - CARBON_CHECK(inst_id.is_valid()); + auto work = work_stack_.back(); + CARBON_CHECK(work.inst_id.is_valid()); // Double-check that the constant still doesn't have a calculated value. - // This should typically be checked before adding it, but a given constant - // may be added multiple times before its constant is evaluated. - if (auto current_const_id = import_ir_constant_values_.Get(inst_id); - current_const_id.is_valid()) { + // This should typically be checked before adding it, but a given + // instruction may be added multiple times before its constant is + // evaluated. + if (!work.made_incomplete_type && + import_ir_constant_values_.Get(work.inst_id).is_valid()) { work_stack_.pop_back(); - } else if (auto new_const_id = TryResolveInst(inst_id); + } else if (auto new_const_id = + TryResolveInst(work.inst_id, work.made_incomplete_type); new_const_id.is_valid()) { - import_ir_constant_values_.Set(inst_id, new_const_id); + import_ir_constant_values_.Set(work.inst_id, new_const_id); work_stack_.pop_back(); } } @@ -73,6 +106,15 @@ class ImportRefResolver { } private: + // A step in work_stack_. + struct Work { + // The instruction to work on. + SemIR::InstId inst_id; + + // True if a first pass made an incomplete type. + bool made_incomplete_type = false; + }; + // For imported entities, we use an invalid enclosing scope. This will be okay // if the scope isn't used later, but we may need to change logic for this if // the behavior changes. @@ -96,7 +138,7 @@ class ImportRefResolver { auto GetLocalConstantId(SemIR::InstId inst_id) -> SemIR::ConstantId { auto const_id = import_ir_constant_values_.Get(inst_id); if (!const_id.is_valid()) { - work_stack_.push_back(inst_id); + work_stack_.push_back({inst_id}); } return const_id; } @@ -136,9 +178,52 @@ class ImportRefResolver { const auto& param_refs = import_ir_.inst_blocks().Get(param_refs_id); llvm::SmallVector new_param_refs; for (auto [ref_id, const_id] : llvm::zip(param_refs, const_ids)) { - new_param_refs.push_back(context_.AddInstInNoBlock( - {SemIR::ImportRefUsed{context_.GetTypeIdForTypeConstant(const_id), - import_ir_id_, ref_id}})); + // Figure out the param structure. This echoes + // Function::GetParamFromParamRefId. + // TODO: Consider a different parameter handling to simplify import logic. + auto inst = import_ir_.insts().Get(ref_id); + auto addr_inst = inst.TryAs(); + if (addr_inst) { + inst = import_ir_.insts().Get(addr_inst->inner_id); + } + auto bind_inst = inst.TryAs(); + if (bind_inst) { + inst = import_ir_.insts().Get(bind_inst->value_id); + } + auto param_inst = inst.As(); + + // Rebuild the param instruction. + auto name_id = GetLocalNameId(param_inst.name_id); + auto type_id = context_.GetTypeIdForTypeConstant(const_id); + + auto new_param_id = context_.AddInstInNoBlock( + {Parse::NodeId::Invalid, SemIR::Param{type_id, name_id}}); + if (bind_inst) { + auto bind_name_id = context_.bind_names().Add( + {.name_id = name_id, + .enclosing_scope_id = SemIR::NameScopeId::Invalid}); + switch (bind_inst->kind) { + case SemIR::InstKind::BindName: + new_param_id = context_.AddInstInNoBlock( + {Parse::NodeId::Invalid, + SemIR::BindName{type_id, bind_name_id, new_param_id}}); + break; + case SemIR::InstKind::BindSymbolicName: + new_param_id = context_.AddInstInNoBlock( + {Parse::NodeId::Invalid, + SemIR::BindSymbolicName{type_id, bind_name_id, new_param_id}}); + break; + + default: + CARBON_FATAL() << "Unexpected kind: " << bind_inst->kind; + } + } + if (addr_inst) { + new_param_id = context_.AddInstInNoBlock( + {Parse::NodeId::Invalid, + SemIR::AddrPattern{type_id, new_param_id}}); + } + new_param_refs.push_back(new_param_id); } return context_.inst_blocks().Add(new_param_refs); } @@ -156,34 +241,43 @@ class ImportRefResolver { // more has been added to the stack. A similar API is followed for all // following TryResolveTypedInst helper functions. // - // Logic for each TryResolveTypedInst will be in two phases: - // 1. Gather all input constants. - // - If HasNewWork, return Invalid. - // 2. Produce an output constant. - // - // Although it's possible TryResolveTypedInst could complete in a single call - // when all input constants are ready, a common scenario is that some inputs - // will still be unresolved, and it'll return Invalid between phases. On the - // second call, all previously unready constants will have been resolved, so - // it should run to completion. As a consequence, it's important to reserve - // all expensive logic for the second phase; this in particular includes - // GetTypeIdForTypeConstant calls which do a hash table lookup. - // // TODO: Error is returned when support is missing, but that should go away. - auto TryResolveInst(SemIR::InstId inst_id) -> SemIR::ConstantId { + auto TryResolveInst(SemIR::InstId inst_id, bool made_incomplete_type) + -> SemIR::ConstantId { if (inst_id.is_builtin()) { + CARBON_CHECK(!made_incomplete_type); // Constants for builtins can be directly copied. return context_.constant_values().Get(inst_id); } auto inst = import_ir_.insts().Get(inst_id); + + CARBON_CHECK(!made_incomplete_type || + inst.kind() == SemIR::InstKind::ClassDecl) + << "Currently only decls with incomplete types should need " + "made_incomplete_type states: " + << inst.kind(); + switch (inst.kind()) { + case SemIR::InstKind::BaseDecl: + return TryResolveTypedInst(inst.As()); + case SemIR::InstKind::BindAlias: return TryResolveTypedInst(inst.As()); + case SemIR::InstKind::ClassDecl: + return TryResolveTypedInst(inst.As(), inst_id, + made_incomplete_type); + + case SemIR::InstKind::ClassType: + return TryResolveTypedInst(inst.As()); + case SemIR::InstKind::ConstType: return TryResolveTypedInst(inst.As()); + case SemIR::InstKind::FieldDecl: + return TryResolveTypedInst(inst.As()); + case SemIR::InstKind::FunctionDecl: return TryResolveTypedInst(inst.As()); @@ -196,13 +290,15 @@ class ImportRefResolver { case SemIR::InstKind::TupleType: return TryResolveTypedInst(inst.As()); + case SemIR::InstKind::UnboundElementType: + return TryResolveTypedInst(inst.As()); + case SemIR::InstKind::BindName: case SemIR::InstKind::BindSymbolicName: // Can use TryEvalInst because the resulting constant doesn't really use // `inst`. return TryEvalInst(context_, inst_id, inst); - case SemIR::InstKind::ClassDecl: case SemIR::InstKind::InterfaceDecl: // TODO: Not implemented. return SemIR::ConstantId::Error; @@ -215,6 +311,23 @@ class ImportRefResolver { } } + auto TryResolveTypedInst(SemIR::BaseDecl inst) -> SemIR::ConstantId { + auto initial_work = work_stack_.size(); + auto type_const_id = GetLocalConstantId(inst.type_id); + auto base_type_const_id = GetLocalConstantId(inst.base_type_id); + if (HasNewWork(initial_work)) { + return SemIR::ConstantId::Invalid; + } + + // Import the instruction in order to update contained base_type_id. + auto inst_id = context_.AddInstInNoBlock( + {Parse::NodeId::Invalid, + SemIR::BaseDecl{context_.GetTypeIdForTypeConstant(type_const_id), + context_.GetTypeIdForTypeConstant(base_type_const_id), + inst.index}}); + return context_.constant_values().Get(inst_id); + } + auto TryResolveTypedInst(SemIR::BindAlias inst) -> SemIR::ConstantId { auto initial_work = work_stack_.size(); auto value_id = GetLocalConstantId(inst.value_id); @@ -224,6 +337,146 @@ class ImportRefResolver { return value_id; } + // Makes an incomplete class. This is necessary even with classes with a + // complete declaration, because things such as `Self` may refer back to the + // type. + auto MakeIncompleteClass(SemIR::InstId inst_id, + const SemIR::Class& import_class) + -> SemIR::ConstantId { + auto class_decl = + SemIR::ClassDecl{SemIR::ClassId::Invalid, SemIR::InstBlockId::Empty}; + auto class_decl_id = + context_.AddPlaceholderInst({Parse::NodeId::Invalid, class_decl}); + // Regardless of whether ClassDecl is a complete type, we first need an + // incomplete type so that any references have something to point at. + class_decl.class_id = context_.classes().Add({ + .name_id = GetLocalNameId(import_class.name_id), + .enclosing_scope_id = NoEnclosingScopeForImports, + // `.self_type_id` depends on `class_id`, so is set below. + .self_type_id = SemIR::TypeId::Invalid, + .decl_id = class_decl_id, + .inheritance_kind = import_class.inheritance_kind, + }); + // Write the function ID into the ClassDecl. + context_.ReplaceInstBeforeConstantUse(class_decl_id, + {Parse::NodeId::Invalid, class_decl}); + + // Build the `Self` type. + auto type_id = context_.GetClassType(class_decl.class_id); + context_.classes().Get(class_decl.class_id).self_type_id = type_id; + + // Set a constant corresponding to the incomplete class. + // TODO: This uses the self type's constant, but the underlying instruction + // is NotConstant. Need to discuss whether we want to add a type_id to + // ClassDecl/InterfaceDecl and change behavior, which would also help + // consistency with other types. See also GetExprValueForLookupResult in + // handle_name.cpp. + auto const_id = context_.types().GetConstantId(type_id); + import_ir_constant_values_.Set(inst_id, const_id); + return const_id; + } + + // Fills out the class definition for an incomplete class. + auto AddClassDefinition(const SemIR::Class& import_class, + SemIR::ConstantId class_const_id, + SemIR::ConstantId object_repr_const_id, + SemIR::ConstantId base_const_id) -> void { + auto& new_class = context_.classes().Get( + context_.insts() + .GetAs(class_const_id.inst_id()) + .class_id); + + new_class.object_repr_id = + context_.GetTypeIdForTypeConstant(object_repr_const_id); + + new_class.scope_id = + context_.name_scopes().Add(new_class.decl_id, SemIR::NameId::Invalid, + new_class.enclosing_scope_id); + auto& new_scope = context_.name_scopes().Get(new_class.scope_id); + const auto& old_scope = import_ir_.name_scopes().Get(import_class.scope_id); + // Push a block so that we can add scoped instructions to it, primarily for + // textual IR formatting. + context_.inst_block_stack().Push(); + for (auto [entry_name_id, entry_inst_id] : old_scope.names) { + CARBON_CHECK( + new_scope.names + .insert({GetLocalNameId(entry_name_id), + context_.AddPlaceholderInst(SemIR::ImportRefUnused{ + import_ir_id_, entry_inst_id})}) + .second); + } + new_class.body_block_id = context_.inst_block_stack().Pop(); + + if (import_class.base_id.is_valid()) { + new_class.base_id = base_const_id.inst_id(); + // Add the base scope to extended scopes. + auto base_inst_id = context_.types().GetInstId( + context_.insts() + .GetAs(new_class.base_id) + .base_type_id); + const auto& base_class = context_.classes().Get( + context_.insts().GetAs(base_inst_id).class_id); + new_scope.extended_scopes.push_back(base_class.scope_id); + } + CARBON_CHECK(new_scope.extended_scopes.size() == + old_scope.extended_scopes.size()); + } + + auto TryResolveTypedInst(SemIR::ClassDecl inst, SemIR::InstId inst_id, + bool made_incomplete_type) -> SemIR::ConstantId { + const auto& import_class = import_ir_.classes().Get(inst.class_id); + + SemIR::ConstantId class_const_id = SemIR::ConstantId::Invalid; + // On the first pass, there's no incomplete type; start by adding one for + // any recursive references. + if (!made_incomplete_type) { + class_const_id = MakeIncompleteClass(inst_id, import_class); + // If there's only a forward declaration, we're done. + if (!import_class.object_repr_id.is_valid()) { + return class_const_id; + } + // This may not be needed because all constants might be ready, but we do + // it here so that we don't need to track which work item corresponds to + // this instruction. + work_stack_.back().made_incomplete_type = true; + } + + CARBON_CHECK(import_class.object_repr_id.is_valid()) + << "Only reachable when there's a definition."; + + // Load constants for the definition. + auto initial_work = work_stack_.size(); + + auto object_repr_const_id = GetLocalConstantId(import_class.object_repr_id); + auto base_const_id = import_class.base_id.is_valid() + ? GetLocalConstantId(import_class.base_id) + : SemIR::ConstantId::Invalid; + + if (HasNewWork(initial_work)) { + return SemIR::ConstantId::Invalid; + } + + // On the first pass, we build the incomplete type's constant above. If we + // get here on a subsequent pass we need to fetch the one we built in the + // first pass. + if (made_incomplete_type) { + CARBON_CHECK(!class_const_id.is_valid()) + << "Shouldn't have a const yet when resuming"; + class_const_id = import_ir_constant_values_.Get(inst_id); + } + AddClassDefinition(import_class, class_const_id, object_repr_const_id, + base_const_id); + + return class_const_id; + } + + auto TryResolveTypedInst(SemIR::ClassType inst) -> SemIR::ConstantId { + CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType); + // ClassType uses a straight reference to the constant ID generated as part + // of pulling in the ClassDecl, so there's no need to phase logic. + return GetLocalConstantId(import_ir_.classes().Get(inst.class_id).decl_id); + } + auto TryResolveTypedInst(SemIR::ConstType inst) -> SemIR::ConstantId { auto initial_work = work_stack_.size(); CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType); @@ -238,6 +491,19 @@ class ImportRefResolver { SemIR::ConstType{SemIR::TypeId::TypeType, inner_type_id}); } + auto TryResolveTypedInst(SemIR::FieldDecl inst) -> SemIR::ConstantId { + auto initial_work = work_stack_.size(); + auto const_id = GetLocalConstantId(inst.type_id); + if (HasNewWork(initial_work)) { + return SemIR::ConstantId::Invalid; + } + auto inst_id = context_.AddInstInNoBlock( + {Parse::NodeId::Invalid, + SemIR::FieldDecl{context_.GetTypeIdForTypeConstant(const_id), + GetLocalNameId(inst.name_id), inst.index}}); + return context_.constant_values().Get(inst_id); + } + auto TryResolveTypedInst(SemIR::FunctionDecl inst) -> SemIR::ConstantId { auto initial_work = work_stack_.size(); auto type_const_id = GetLocalConstantId(inst.type_id); @@ -264,8 +530,8 @@ class ImportRefResolver { auto function_decl = SemIR::FunctionDecl{context_.GetTypeIdForTypeConstant(type_const_id), SemIR::FunctionId::Invalid}; - auto function_decl_id = - context_.AddPlaceholderInst({Parse::NodeId::Invalid, function_decl}); + auto function_decl_id = context_.AddPlaceholderInstInNoBlock( + {Parse::NodeId::Invalid, function_decl}); auto new_return_type_id = return_type_const_id.is_valid() @@ -366,11 +632,26 @@ class ImportRefResolver { return context_.types().GetConstantId(context_.GetTupleType(elem_type_ids)); } + auto TryResolveTypedInst(SemIR::UnboundElementType inst) + -> SemIR::ConstantId { + auto initial_work = work_stack_.size(); + CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType); + auto class_const_id = GetLocalConstantId(inst.class_type_id); + auto elem_const_id = GetLocalConstantId(inst.element_type_id); + if (HasNewWork(initial_work)) { + return SemIR::ConstantId::Invalid; + } + + return context_.types().GetConstantId(context_.GetUnboundElementType( + context_.GetTypeIdForTypeConstant(class_const_id), + context_.GetTypeIdForTypeConstant(elem_const_id))); + } + Context& context_; SemIR::ImportIRId import_ir_id_; const SemIR::File& import_ir_; SemIR::ConstantValueStore& import_ir_constant_values_; - llvm::SmallVector work_stack_; + llvm::SmallVector work_stack_; }; auto TryResolveImportRefUnused(Context& context, SemIR::InstId inst_id) diff --git a/toolchain/check/testdata/class/fail_import_misuses.carbon b/toolchain/check/testdata/class/fail_import_misuses.carbon new file mode 100644 index 000000000000..c8545da497e5 --- /dev/null +++ b/toolchain/check/testdata/class/fail_import_misuses.carbon @@ -0,0 +1,89 @@ +// 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 +// +// AUTOUPDATE + +// --- a.carbon + +library "a" api; + +class Empty { +} + +class Incomplete; + +// --- b.carbon + +library "b" api; + +import library "a"; + +// CHECK:STDERR: b.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: class Empty { +// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: a.carbon:4:1: Name is previously declared here. +// CHECK:STDERR: class Empty { +// CHECK:STDERR: ^~~~~~~~~~~~~ +class Empty { +} + +// CHECK:STDERR: b.carbon:[[@LINE+4]]:8: ERROR: Variable has incomplete type `Incomplete`. +// CHECK:STDERR: var a: Incomplete; +// CHECK:STDERR: ^~~~~~~~~~ +// CHECK:STDERR: b.carbon: Class was forward declared here. +var a: Incomplete; + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Empty: type = class_type @Empty [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %Incomplete: type = class_type @Incomplete [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Empty = %Empty.decl, .Incomplete = %Incomplete.decl} [template] +// CHECK:STDOUT: %Empty.decl = class_decl @Empty, () +// CHECK:STDOUT: %Incomplete.decl = class_decl @Incomplete, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Empty { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Incomplete; +// CHECK:STDOUT: +// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Empty: type = class_type @Empty [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: type = class_type @.1 [template] +// CHECK:STDOUT: %Incomplete: type = class_type @Incomplete [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Empty = %import_ref.1, .Incomplete = %import_ref.2, .a = %a} [template] +// CHECK:STDOUT: %import_ref.1: invalid = import_ref ir1, inst+1, used [template = constants.%Empty] +// CHECK:STDOUT: %import_ref.2: invalid = import_ref ir1, inst+4, used [template = constants.%Incomplete] +// CHECK:STDOUT: %.decl = class_decl @.1, () +// CHECK:STDOUT: %Incomplete.decl = class_decl @Incomplete, () +// CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, constants.%Incomplete [template = constants.%Incomplete] +// CHECK:STDOUT: %a.var: ref = var a +// CHECK:STDOUT: %a: ref = bind_name a, %a.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Empty { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.1 { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Incomplete; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_todo_import.carbon b/toolchain/check/testdata/class/fail_todo_import.carbon deleted file mode 100644 index be2e598675fd..000000000000 --- a/toolchain/check/testdata/class/fail_todo_import.carbon +++ /dev/null @@ -1,165 +0,0 @@ -// 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 -// -// AUTOUPDATE -// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TryResolveInst on ClassType`. -// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TryResolveInst on ClassType`. - -// --- a.carbon - -library "a" api; - -class Empty { -} - -class ForwardDeclared; - -class ForwardDeclared { - fn F(); -} - -var a_ref: Empty = {}; -var b_ref: ForwardDeclared = {}; -var c_ref: ForwardDeclared* = &b_ref; -// var d_ref: (ForwardDeclared,) = ({},); - -// --- b.carbon - -library "b" api; - -import library "a"; - -fn Run() { - var x: Empty = {}; - var y: ForwardDeclared = {}; - y.F(); -} - -var a: Empty = a_ref; -var b: ForwardDeclared = b_ref; -var c: ForwardDeclared* = c_ref; -// CHECK:STDERR: b.carbon:[[@LINE+3]]:29: ERROR: Name `d_ref` not found. -// CHECK:STDERR: var d: (ForwardDeclared,) = d_ref; -// CHECK:STDERR: ^~~~~ -var d: (ForwardDeclared,) = d_ref; - -// CHECK:STDOUT: --- a.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %Empty: type = class_type @Empty [template] -// CHECK:STDOUT: %.1: type = struct_type {} [template] -// CHECK:STDOUT: %ForwardDeclared: type = class_type @ForwardDeclared [template] -// CHECK:STDOUT: %.2: type = tuple_type () [template] -// CHECK:STDOUT: %.3: type = ptr_type {} [template] -// CHECK:STDOUT: %.4: Empty = struct_value () [template] -// CHECK:STDOUT: %.5: ForwardDeclared = struct_value () [template] -// CHECK:STDOUT: %.6: type = ptr_type ForwardDeclared [template] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace {.Empty = %Empty.decl, .ForwardDeclared = %ForwardDeclared.decl.loc7, .a_ref = %a_ref, .b_ref = %b_ref, .c_ref = %c_ref} [template] -// CHECK:STDOUT: %Empty.decl = class_decl @Empty, () -// CHECK:STDOUT: %ForwardDeclared.decl.loc7 = class_decl @ForwardDeclared, () -// CHECK:STDOUT: %ForwardDeclared.decl.loc9 = class_decl @ForwardDeclared, () -// CHECK:STDOUT: %Empty.ref: type = name_ref Empty, constants.%Empty [template = constants.%Empty] -// CHECK:STDOUT: %a_ref.var: ref Empty = var a_ref -// CHECK:STDOUT: %a_ref: ref Empty = bind_name a_ref, %a_ref.var -// CHECK:STDOUT: %ForwardDeclared.ref.loc14: type = name_ref ForwardDeclared, constants.%ForwardDeclared [template = constants.%ForwardDeclared] -// CHECK:STDOUT: %b_ref.var: ref ForwardDeclared = var b_ref -// CHECK:STDOUT: %b_ref: ref ForwardDeclared = bind_name b_ref, %b_ref.var -// CHECK:STDOUT: %ForwardDeclared.ref.loc15: type = name_ref ForwardDeclared, constants.%ForwardDeclared [template = constants.%ForwardDeclared] -// CHECK:STDOUT: %.loc15: type = ptr_type ForwardDeclared [template = constants.%.6] -// CHECK:STDOUT: %c_ref.var: ref ForwardDeclared* = var c_ref -// CHECK:STDOUT: %c_ref: ref ForwardDeclared* = bind_name c_ref, %c_ref.var -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @Empty { -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @ForwardDeclared { -// CHECK:STDOUT: %F: = fn_decl @F [template] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @F(); -// CHECK:STDOUT: -// CHECK:STDOUT: fn @__global_init() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc13_21.1: {} = struct_literal () -// CHECK:STDOUT: %.loc13_21.2: init Empty = class_init (), file.%a_ref.var [template = constants.%.4] -// CHECK:STDOUT: %.loc13_21.3: init Empty = converted %.loc13_21.1, %.loc13_21.2 [template = constants.%.4] -// CHECK:STDOUT: assign file.%a_ref.var, %.loc13_21.3 -// CHECK:STDOUT: %.loc14_31.1: {} = struct_literal () -// CHECK:STDOUT: %.loc14_31.2: init ForwardDeclared = class_init (), file.%b_ref.var [template = constants.%.5] -// CHECK:STDOUT: %.loc14_31.3: init ForwardDeclared = converted %.loc14_31.1, %.loc14_31.2 [template = constants.%.5] -// CHECK:STDOUT: assign file.%b_ref.var, %.loc14_31.3 -// CHECK:STDOUT: %b_ref.ref: ref ForwardDeclared = name_ref b_ref, file.%b_ref -// CHECK:STDOUT: %.loc15: ForwardDeclared* = addr_of %b_ref.ref -// CHECK:STDOUT: assign file.%c_ref.var, %.loc15 -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- b.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = struct_type {} [template] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace {.Empty = %import_ref.1, .ForwardDeclared = %import_ref.2, .a_ref = %import_ref.3, .b_ref = %import_ref.4, .c_ref = %import_ref.5, .Run = %Run, .a = %a, .b = %b, .c = %c, .d = %d} [template] -// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = ] -// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+4, used [template = ] -// CHECK:STDOUT: %import_ref.3: ref = import_ref ir1, inst+12, used -// CHECK:STDOUT: %import_ref.4: ref = import_ref ir1, inst+20, used -// CHECK:STDOUT: %import_ref.5: ref = import_ref ir1, inst+30, used -// CHECK:STDOUT: %Run: = fn_decl @Run [template] -// CHECK:STDOUT: %Empty.ref: = name_ref Empty, %import_ref.1 [template = ] -// CHECK:STDOUT: %a.var: ref = var a -// CHECK:STDOUT: %a: ref = bind_name a, %a.var -// CHECK:STDOUT: %ForwardDeclared.ref.loc13: = name_ref ForwardDeclared, %import_ref.2 [template = ] -// CHECK:STDOUT: %b.var: ref = var b -// CHECK:STDOUT: %b: ref = bind_name b, %b.var -// CHECK:STDOUT: %ForwardDeclared.ref.loc14: = name_ref ForwardDeclared, %import_ref.2 [template = ] -// CHECK:STDOUT: %.loc14: type = ptr_type [template = ] -// CHECK:STDOUT: %c.var: ref = var c -// CHECK:STDOUT: %c: ref = bind_name c, %c.var -// CHECK:STDOUT: %ForwardDeclared.ref.loc18: = name_ref ForwardDeclared, %import_ref.2 [template = ] -// CHECK:STDOUT: %.loc18: = tuple_literal (%ForwardDeclared.ref.loc18) -// CHECK:STDOUT: %d.var: ref = var d -// CHECK:STDOUT: %d: ref = bind_name d, %d.var -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @Run() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %Empty.ref: = name_ref Empty, file.%import_ref.1 [template = ] -// CHECK:STDOUT: %x.var: ref = var x -// CHECK:STDOUT: %x: ref = bind_name x, %x.var -// CHECK:STDOUT: %.loc7: {} = struct_literal () -// CHECK:STDOUT: assign %x.var, -// CHECK:STDOUT: %ForwardDeclared.ref: = name_ref ForwardDeclared, file.%import_ref.2 [template = ] -// CHECK:STDOUT: %y.var: ref = var y -// CHECK:STDOUT: %y: ref = bind_name y, %y.var -// CHECK:STDOUT: %.loc8: {} = struct_literal () -// CHECK:STDOUT: assign %y.var, -// CHECK:STDOUT: %y.ref: ref = name_ref y, %y -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @__global_init() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %a_ref.ref: ref = name_ref a_ref, file.%import_ref.3 -// CHECK:STDOUT: assign file.%a.var, -// CHECK:STDOUT: %b_ref.ref: ref = name_ref b_ref, file.%import_ref.4 -// CHECK:STDOUT: assign file.%b.var, -// CHECK:STDOUT: %c_ref.ref: ref = name_ref c_ref, file.%import_ref.5 -// CHECK:STDOUT: assign file.%c.var, -// CHECK:STDOUT: %d_ref.ref: = name_ref d_ref, [template = ] -// CHECK:STDOUT: assign file.%d.var, -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon b/toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon new file mode 100644 index 000000000000..ccd5fdce705c --- /dev/null +++ b/toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon @@ -0,0 +1,62 @@ +// 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 +// +// AUTOUPDATE + +// --- a.carbon + +library "a" api; + +class ForwardDecl; + +// --- b.carbon + +library "b" api; + +import library "a"; + +// TODO: This should probably have a valid form. +// CHECK:STDERR: b.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: class ForwardDecl { +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: a.carbon:4:1: Name is previously declared here. +// CHECK:STDERR: class ForwardDecl; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ +class ForwardDecl { +} + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %ForwardDecl: type = class_type @ForwardDecl [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.ForwardDecl = %ForwardDecl.decl} [template] +// CHECK:STDOUT: %ForwardDecl.decl = class_decl @ForwardDecl, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @ForwardDecl; +// CHECK:STDOUT: +// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %ForwardDecl: type = class_type @ForwardDecl [template] +// CHECK:STDOUT: %.1: type = class_type @.1 [template] +// CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.ForwardDecl = %import_ref} [template] +// CHECK:STDOUT: %import_ref: invalid = import_ref ir1, inst+1, used [template = constants.%ForwardDecl] +// CHECK:STDOUT: %.decl = class_decl @.1, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @ForwardDecl; +// CHECK:STDOUT: +// CHECK:STDOUT: class @.1 { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/import.carbon b/toolchain/check/testdata/class/import.carbon new file mode 100644 index 000000000000..407952f2abe4 --- /dev/null +++ b/toolchain/check/testdata/class/import.carbon @@ -0,0 +1,210 @@ +// 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 +// +// AUTOUPDATE + +// --- a.carbon + +library "a" api; + +class Empty { +} + +class Field { + var x: i32; +} + +class ForwardDeclared; + +class ForwardDeclared { + fn F[self: Self](); + fn G[addr self: Self*](); +} + +class Incomplete; + +// --- b.carbon + +library "b" api; + +import library "a"; + +fn Run() { + var a: Empty = {}; + + var b: Field = {.x = 1}; + b.x = 2; + + var c: ForwardDeclared = {}; + c.F(); + c.G(); + + var d: ForwardDeclared* = &c; + + var e: Incomplete*; +} + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Empty: type = class_type @Empty [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %Field: type = class_type @Field [template] +// CHECK:STDOUT: %.2: type = unbound_element_type Field, i32 [template] +// CHECK:STDOUT: %.3: type = struct_type {.x: i32} [template] +// CHECK:STDOUT: %ForwardDeclared: type = class_type @ForwardDeclared [template] +// CHECK:STDOUT: %.4: type = ptr_type ForwardDeclared [template] +// CHECK:STDOUT: %Incomplete: type = class_type @Incomplete [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Empty = %Empty.decl, .Field = %Field.decl, .ForwardDeclared = %ForwardDeclared.decl.loc11, .Incomplete = %Incomplete.decl} [template] +// CHECK:STDOUT: %Empty.decl = class_decl @Empty, () +// CHECK:STDOUT: %Field.decl = class_decl @Field, () +// CHECK:STDOUT: %ForwardDeclared.decl.loc11 = class_decl @ForwardDeclared, () +// CHECK:STDOUT: %ForwardDeclared.decl.loc13 = class_decl @ForwardDeclared, () +// CHECK:STDOUT: %Incomplete.decl = class_decl @Incomplete, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Empty { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Field { +// CHECK:STDOUT: %.loc8: = field_decl x, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = %.loc8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @ForwardDeclared { +// CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: %G: = fn_decl @G [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .G = %G +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Incomplete; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%self: ForwardDeclared](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G[addr %self: ForwardDeclared*](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Empty: type = class_type @Empty [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.3: type = ptr_type {} [template] +// CHECK:STDOUT: %.4: Empty = struct_value () [template] +// CHECK:STDOUT: %Field: type = class_type @Field [template] +// CHECK:STDOUT: %.5: type = struct_type {.x: i32} [template] +// CHECK:STDOUT: %.6: type = ptr_type {.x: i32} [template] +// CHECK:STDOUT: %.7: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.8: Field = struct_value (%.7) [template] +// CHECK:STDOUT: %.9: type = unbound_element_type Field, i32 [template] +// CHECK:STDOUT: %.10: i32 = int_literal 2 [template] +// CHECK:STDOUT: %ForwardDeclared: type = class_type @ForwardDeclared [template] +// CHECK:STDOUT: %.11: ForwardDeclared = struct_value () [template] +// CHECK:STDOUT: %.12: type = ptr_type ForwardDeclared [template] +// CHECK:STDOUT: %Incomplete: type = class_type @Incomplete [template] +// CHECK:STDOUT: %.13: type = ptr_type Incomplete [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Empty = %import_ref.1, .Field = %import_ref.2, .ForwardDeclared = %import_ref.3, .Incomplete = %import_ref.4, .Run = %Run} [template] +// CHECK:STDOUT: %import_ref.1: invalid = import_ref ir1, inst+1, used [template = constants.%Empty] +// CHECK:STDOUT: %import_ref.2: invalid = import_ref ir1, inst+4, used [template = constants.%Field] +// CHECK:STDOUT: %import_ref.3: invalid = import_ref ir1, inst+11, used [template = constants.%ForwardDeclared] +// CHECK:STDOUT: %import_ref.4: invalid = import_ref ir1, inst+25, used [template = constants.%Incomplete] +// CHECK:STDOUT: %Run: = fn_decl @Run [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Empty { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Field { +// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+7, used [template = imports.%.1] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .x = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @ForwardDeclared { +// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+24, used [template = imports.%G] +// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+17, used [template = imports.%F] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .G = %import_ref.1 +// CHECK:STDOUT: .F = %import_ref.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Incomplete; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Empty.decl = class_decl @Empty, () +// CHECK:STDOUT: %Empty.ref: type = name_ref Empty, constants.%Empty [template = constants.%Empty] +// CHECK:STDOUT: %a.var: ref Empty = var a +// CHECK:STDOUT: %a: ref Empty = bind_name a, %a.var +// CHECK:STDOUT: %.loc7_19.1: {} = struct_literal () +// CHECK:STDOUT: %.loc7_19.2: init Empty = class_init (), %a.var [template = constants.%.4] +// CHECK:STDOUT: %.loc7_19.3: init Empty = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%.4] +// CHECK:STDOUT: assign %a.var, %.loc7_19.3 +// CHECK:STDOUT: %Field.decl = class_decl @Field, () +// CHECK:STDOUT: %Field.ref: type = name_ref Field, constants.%Field [template = constants.%Field] +// CHECK:STDOUT: %b.var: ref Field = var b +// CHECK:STDOUT: %b: ref Field = bind_name b, %b.var +// CHECK:STDOUT: %.loc9_24: i32 = int_literal 1 [template = constants.%.7] +// CHECK:STDOUT: %.loc9_25.1: {.x: i32} = struct_literal (%.loc9_24) +// CHECK:STDOUT: %.loc9_25.2: ref i32 = class_element_access %b.var, element0 +// CHECK:STDOUT: %.loc9_25.3: init i32 = initialize_from %.loc9_24 to %.loc9_25.2 [template = constants.%.7] +// CHECK:STDOUT: %.loc9_25.4: init Field = class_init (%.loc9_25.3), %b.var [template = constants.%.8] +// CHECK:STDOUT: %.loc9_25.5: init Field = converted %.loc9_25.1, %.loc9_25.4 [template = constants.%.8] +// CHECK:STDOUT: assign %b.var, %.loc9_25.5 +// CHECK:STDOUT: %b.ref: ref Field = name_ref b, %b +// CHECK:STDOUT: %.loc10_4: ref i32 = class_element_access %b.ref, element0 +// CHECK:STDOUT: %.loc10_9: i32 = int_literal 2 [template = constants.%.10] +// CHECK:STDOUT: assign %.loc10_4, %.loc10_9 +// CHECK:STDOUT: %ForwardDeclared.decl = class_decl @ForwardDeclared, () +// CHECK:STDOUT: %ForwardDeclared.ref.loc12: type = name_ref ForwardDeclared, constants.%ForwardDeclared [template = constants.%ForwardDeclared] +// CHECK:STDOUT: %c.var: ref ForwardDeclared = var c +// CHECK:STDOUT: %c: ref ForwardDeclared = bind_name c, %c.var +// CHECK:STDOUT: %.loc12_29.1: {} = struct_literal () +// CHECK:STDOUT: %.loc12_29.2: init ForwardDeclared = class_init (), %c.var [template = constants.%.11] +// CHECK:STDOUT: %.loc12_29.3: init ForwardDeclared = converted %.loc12_29.1, %.loc12_29.2 [template = constants.%.11] +// CHECK:STDOUT: assign %c.var, %.loc12_29.3 +// CHECK:STDOUT: %c.ref.loc13: ref ForwardDeclared = name_ref c, %c +// CHECK:STDOUT: %.loc13_4: = bound_method %c.ref.loc13, @ForwardDeclared.%import_ref.2 +// CHECK:STDOUT: %.loc13_3: ForwardDeclared = bind_value %c.ref.loc13 +// CHECK:STDOUT: %.loc13_6: init () = call %.loc13_4(%.loc13_3) +// CHECK:STDOUT: %c.ref.loc14: ref ForwardDeclared = name_ref c, %c +// CHECK:STDOUT: %.loc14_4: = bound_method %c.ref.loc14, @ForwardDeclared.%import_ref.1 +// CHECK:STDOUT: %.loc14_3: ForwardDeclared* = addr_of %c.ref.loc14 +// CHECK:STDOUT: %.loc14_6: init () = call %.loc14_4(%.loc14_3) +// CHECK:STDOUT: %ForwardDeclared.ref.loc16: type = name_ref ForwardDeclared, constants.%ForwardDeclared [template = constants.%ForwardDeclared] +// CHECK:STDOUT: %.loc16_25: type = ptr_type ForwardDeclared [template = constants.%.12] +// CHECK:STDOUT: %d.var: ref ForwardDeclared* = var d +// CHECK:STDOUT: %d: ref ForwardDeclared* = bind_name d, %d.var +// CHECK:STDOUT: %c.ref.loc16: ref ForwardDeclared = name_ref c, %c +// CHECK:STDOUT: %.loc16_29: ForwardDeclared* = addr_of %c.ref.loc16 +// CHECK:STDOUT: assign %d.var, %.loc16_29 +// CHECK:STDOUT: %Incomplete.decl = class_decl @Incomplete, () +// CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, constants.%Incomplete [template = constants.%Incomplete] +// CHECK:STDOUT: %.loc18: type = ptr_type Incomplete [template = constants.%.13] +// CHECK:STDOUT: %e.var: ref Incomplete* = var e +// CHECK:STDOUT: %e: ref Incomplete* = bind_name e, %e.var +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%self: ForwardDeclared](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G[addr %self: ForwardDeclared*](); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/import_base.carbon b/toolchain/check/testdata/class/import_base.carbon new file mode 100644 index 000000000000..16525f6fc3df --- /dev/null +++ b/toolchain/check/testdata/class/import_base.carbon @@ -0,0 +1,165 @@ +// 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 +// +// AUTOUPDATE + +// --- a.carbon + +library "a" api; + +base class Base { + fn F[self: Self](); + fn Unused[self: Self](); + + var x: i32; + var unused: i32; +} + +class Child { + extend base: Base; +} + +// --- b.carbon + +library "b" api; + +import library "a"; + +fn Run() { + var a: Child = {.base = {.x = 0, .unused = 1}}; + a.x = 2; + a.F(); +} + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Base: type = class_type @Base [template] +// CHECK:STDOUT: %.1: type = unbound_element_type Base, i32 [template] +// CHECK:STDOUT: %.2: type = struct_type {.x: i32, .unused: i32} [template] +// CHECK:STDOUT: %Child: type = class_type @Child [template] +// CHECK:STDOUT: %.3: type = ptr_type {.x: i32, .unused: i32} [template] +// CHECK:STDOUT: %.4: type = unbound_element_type Child, Base [template] +// CHECK:STDOUT: %.5: type = struct_type {.base: Base} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Base = %Base.decl, .Child = %Child.decl} [template] +// CHECK:STDOUT: %Base.decl = class_decl @Base, () +// CHECK:STDOUT: %Child.decl = class_decl @Child, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Base { +// CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: %Unused: = fn_decl @Unused [template] +// CHECK:STDOUT: %.loc8: = field_decl x, element0 [template] +// CHECK:STDOUT: %.loc9: = field_decl unused, element1 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .Unused = %Unused +// CHECK:STDOUT: .x = %.loc8 +// CHECK:STDOUT: .unused = %.loc9 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Child { +// CHECK:STDOUT: %Base.ref: type = name_ref Base, constants.%Base [template = constants.%Base] +// CHECK:STDOUT: %.loc13: = base_decl Base, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .base = %.loc13 +// CHECK:STDOUT: extend name_scope1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%self: Base](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Unused[%self: Base](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Child: type = class_type @Child [template] +// CHECK:STDOUT: %Base: type = class_type @Base [template] +// CHECK:STDOUT: %.1: type = struct_type {.x: i32, .unused: i32} [template] +// CHECK:STDOUT: %.2: type = unbound_element_type Child, Base [template] +// CHECK:STDOUT: %.3: type = struct_type {.base: Base} [template] +// CHECK:STDOUT: %.4: type = ptr_type {.x: i32, .unused: i32} [template] +// CHECK:STDOUT: %.5: type = struct_type {.base: {.x: i32, .unused: i32}*} [template] +// CHECK:STDOUT: %.6: type = ptr_type {.base: Base} [template] +// CHECK:STDOUT: %.7: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.8: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.9: type = struct_type {.base: {.x: i32, .unused: i32}} [template] +// CHECK:STDOUT: %.10: Base = struct_value (%.7, %.8) [template] +// CHECK:STDOUT: %.11: Child = struct_value (%.10) [template] +// CHECK:STDOUT: %.12: type = unbound_element_type Base, i32 [template] +// CHECK:STDOUT: %.13: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.14: type = tuple_type () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Base = %import_ref.1, .Child = %import_ref.2, .Run = %Run} [template] +// CHECK:STDOUT: %import_ref.1 = import_ref ir1, inst+1, unused +// CHECK:STDOUT: %import_ref.2: invalid = import_ref ir1, inst+19, used [template = constants.%Child] +// CHECK:STDOUT: %Run: = fn_decl @Run [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Child { +// CHECK:STDOUT: %import_ref = import_ref ir1, inst+24, unused +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .base = %import_ref +// CHECK:STDOUT: extend name_scope1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Base { +// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+6, used [template = imports.%F] +// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+12, used [template = imports.%.1] +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+10, unused +// CHECK:STDOUT: %import_ref.4 = import_ref ir1, inst+15, unused +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %import_ref.1 +// CHECK:STDOUT: .x = %import_ref.2 +// CHECK:STDOUT: .Unused = %import_ref.3 +// CHECK:STDOUT: .unused = %import_ref.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Child.decl = class_decl @Child, () +// CHECK:STDOUT: %Base.decl = class_decl @Base, () +// CHECK:STDOUT: %Child.ref: type = name_ref Child, constants.%Child [template = constants.%Child] +// CHECK:STDOUT: %a.var: ref Child = var a +// CHECK:STDOUT: %a: ref Child = bind_name a, %a.var +// CHECK:STDOUT: %.loc7_33: i32 = int_literal 0 [template = constants.%.7] +// CHECK:STDOUT: %.loc7_46: i32 = int_literal 1 [template = constants.%.8] +// CHECK:STDOUT: %.loc7_47.1: {.x: i32, .unused: i32} = struct_literal (%.loc7_33, %.loc7_46) +// CHECK:STDOUT: %.loc7_48.1: {.base: {.x: i32, .unused: i32}} = struct_literal (%.loc7_47.1) +// CHECK:STDOUT: %.loc7_48.2: ref Base = class_element_access %a.var, element0 +// CHECK:STDOUT: %.loc7_47.2: ref i32 = class_element_access %.loc7_48.2, element0 +// CHECK:STDOUT: %.loc7_47.3: init i32 = initialize_from %.loc7_33 to %.loc7_47.2 [template = constants.%.7] +// CHECK:STDOUT: %.loc7_47.4: ref i32 = class_element_access %.loc7_48.2, element1 +// CHECK:STDOUT: %.loc7_47.5: init i32 = initialize_from %.loc7_46 to %.loc7_47.4 [template = constants.%.8] +// CHECK:STDOUT: %.loc7_47.6: init Base = class_init (%.loc7_47.3, %.loc7_47.5), %.loc7_48.2 [template = constants.%.10] +// CHECK:STDOUT: %.loc7_47.7: init Base = converted %.loc7_47.1, %.loc7_47.6 [template = constants.%.10] +// CHECK:STDOUT: %.loc7_48.3: init Child = class_init (%.loc7_47.7), %a.var [template = constants.%.11] +// CHECK:STDOUT: %.loc7_48.4: init Child = converted %.loc7_48.1, %.loc7_48.3 [template = constants.%.11] +// CHECK:STDOUT: assign %a.var, %.loc7_48.4 +// CHECK:STDOUT: %a.ref.loc8: ref Child = name_ref a, %a +// CHECK:STDOUT: %.loc8_4.1: ref Base = class_element_access %a.ref.loc8, element0 +// CHECK:STDOUT: %.loc8_3: ref Base = converted %a.ref.loc8, %.loc8_4.1 +// CHECK:STDOUT: %.loc8_4.2: ref i32 = class_element_access %.loc8_3, element0 +// CHECK:STDOUT: %.loc8_9: i32 = int_literal 2 [template = constants.%.13] +// CHECK:STDOUT: assign %.loc8_4.2, %.loc8_9 +// CHECK:STDOUT: %a.ref.loc9: ref Child = name_ref a, %a +// CHECK:STDOUT: %.loc9_4: = bound_method %a.ref.loc9, @Base.%import_ref.1 +// CHECK:STDOUT: %.loc9_6.1: ref Base = class_element_access %a.ref.loc9, element0 +// CHECK:STDOUT: %.loc9_3.1: ref Base = converted %a.ref.loc9, %.loc9_6.1 +// CHECK:STDOUT: %.loc9_3.2: Base = bind_value %.loc9_3.1 +// CHECK:STDOUT: %.loc9_6.2: init () = call %.loc9_4(%.loc9_3.2) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%self: Base](); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/import_member_cycle.carbon b/toolchain/check/testdata/class/import_member_cycle.carbon new file mode 100644 index 000000000000..d8e246fddf90 --- /dev/null +++ b/toolchain/check/testdata/class/import_member_cycle.carbon @@ -0,0 +1,78 @@ +// 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 +// +// AUTOUPDATE + +// --- a.carbon + +library "a" api; + +class Cycle { + var a: Cycle*; +} + +// --- b.carbon + +library "b" api; + +import library "a"; + +fn Run() { + var a: Cycle*; +} + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Cycle: type = class_type @Cycle [template] +// CHECK:STDOUT: %.1: type = ptr_type Cycle [template] +// CHECK:STDOUT: %.2: type = unbound_element_type Cycle, Cycle* [template] +// CHECK:STDOUT: %.3: type = struct_type {.a: Cycle*} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Cycle = %Cycle.decl} [template] +// CHECK:STDOUT: %Cycle.decl = class_decl @Cycle, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Cycle { +// CHECK:STDOUT: %Cycle.ref: type = name_ref Cycle, constants.%Cycle [template = constants.%Cycle] +// CHECK:STDOUT: %.loc5_15: type = ptr_type Cycle [template = constants.%.1] +// CHECK:STDOUT: %.loc5_8: = field_decl a, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .a = %.loc5_8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Cycle: type = class_type @Cycle [template] +// CHECK:STDOUT: %.1: type = ptr_type Cycle [template] +// CHECK:STDOUT: %.2: type = struct_type {.a: Cycle*} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Cycle = %import_ref, .Run = %Run} [template] +// CHECK:STDOUT: %import_ref: invalid = import_ref ir1, inst+1, used [template = constants.%Cycle] +// CHECK:STDOUT: %Run: = fn_decl @Run [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Cycle { +// CHECK:STDOUT: %import_ref = import_ref ir1, inst+7, unused +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .a = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cycle.decl = class_decl @Cycle, () +// CHECK:STDOUT: %Cycle.ref: type = name_ref Cycle, constants.%Cycle [template = constants.%Cycle] +// CHECK:STDOUT: %.loc7: type = ptr_type Cycle [template = constants.%.1] +// CHECK:STDOUT: %a.var: ref Cycle* = var a +// CHECK:STDOUT: %a: ref Cycle* = bind_name a, %a.var +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/import_struct_cyle.carbon b/toolchain/check/testdata/class/import_struct_cyle.carbon new file mode 100644 index 000000000000..8990c85c94a6 --- /dev/null +++ b/toolchain/check/testdata/class/import_struct_cyle.carbon @@ -0,0 +1,101 @@ +// 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 +// +// AUTOUPDATE + +// --- a.carbon + +library "a" api; + +class Cycle; + +var a: {.b: Cycle*}; + +class Cycle { + // The type here is equivalent to the `a` above, but on import can be resolved first. + var c: {.b: Cycle*}; +} + +// --- b.carbon + +library "b" api; + +import library "a"; + +fn Run() { + a.b = (*a.b).c.b; +} + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Cycle: type = class_type @Cycle [template] +// CHECK:STDOUT: %.1: type = ptr_type Cycle [template] +// CHECK:STDOUT: %.2: type = struct_type {.b: Cycle*} [template] +// CHECK:STDOUT: %.3: type = unbound_element_type Cycle, {.b: Cycle*} [template] +// CHECK:STDOUT: %.4: type = struct_type {.c: {.b: Cycle*}} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Cycle = %Cycle.decl.loc4, .a = %a} [template] +// CHECK:STDOUT: %Cycle.decl.loc4 = class_decl @Cycle, () +// CHECK:STDOUT: %Cycle.ref: type = name_ref Cycle, constants.%Cycle [template = constants.%Cycle] +// CHECK:STDOUT: %.loc6_18: type = ptr_type Cycle [template = constants.%.1] +// CHECK:STDOUT: %.loc6_19: type = struct_type {.b: Cycle*} [template = constants.%.2] +// CHECK:STDOUT: %a.var: ref {.b: Cycle*} = var a +// CHECK:STDOUT: %a: ref {.b: Cycle*} = bind_name a, %a.var +// CHECK:STDOUT: %Cycle.decl.loc8 = class_decl @Cycle, () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Cycle { +// CHECK:STDOUT: %Cycle.ref: type = name_ref Cycle, constants.%Cycle [template = constants.%Cycle] +// CHECK:STDOUT: %.loc10_20: type = ptr_type Cycle [template = constants.%.1] +// CHECK:STDOUT: %.loc10_21: type = struct_type {.b: Cycle*} [template = constants.%.2] +// CHECK:STDOUT: %.loc10_8: = field_decl c, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .c = %.loc10_8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Cycle: type = class_type @Cycle [template] +// CHECK:STDOUT: %.1: type = ptr_type Cycle [template] +// CHECK:STDOUT: %.2: type = struct_type {.b: Cycle*} [template] +// CHECK:STDOUT: %.3: type = struct_type {.c: {.b: Cycle*}} [template] +// CHECK:STDOUT: %.4: type = ptr_type {.c: {.b: Cycle*}} [template] +// CHECK:STDOUT: %.5: type = unbound_element_type Cycle, {.b: Cycle*} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Cycle = %import_ref.1, .a = %import_ref.2, .Run = %Run} [template] +// CHECK:STDOUT: %import_ref.1 = import_ref ir1, inst+1, unused +// CHECK:STDOUT: %import_ref.2: ref {.b: Cycle*} = import_ref ir1, inst+11, used +// CHECK:STDOUT: %Run: = fn_decl @Run [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Cycle { +// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+18, used [template = imports.%.1] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .c = %import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Run() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cycle.decl = class_decl @Cycle, () +// CHECK:STDOUT: %a.ref.loc7_3: ref {.b: Cycle*} = name_ref a, file.%import_ref.2 +// CHECK:STDOUT: %.loc7_4: ref Cycle* = struct_access %a.ref.loc7_3, element0 +// CHECK:STDOUT: %a.ref.loc7_11: ref {.b: Cycle*} = name_ref a, file.%import_ref.2 +// CHECK:STDOUT: %.loc7_12.1: ref Cycle* = struct_access %a.ref.loc7_11, element0 +// CHECK:STDOUT: %.loc7_12.2: Cycle* = bind_value %.loc7_12.1 +// CHECK:STDOUT: %.loc7_10: ref Cycle = deref %.loc7_12.2 +// CHECK:STDOUT: %.loc7_15: ref {.b: Cycle*} = class_element_access %.loc7_10, element0 +// CHECK:STDOUT: %.loc7_17.1: ref Cycle* = struct_access %.loc7_15, element0 +// CHECK:STDOUT: %.loc7_17.2: Cycle* = bind_value %.loc7_17.1 +// CHECK:STDOUT: assign %.loc7_4, %.loc7_17.2 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/declaration/import.carbon b/toolchain/check/testdata/function/declaration/import.carbon index 32f9d469f88c..12b5d0a2fb0e 100644 --- a/toolchain/check/testdata/function/declaration/import.carbon +++ b/toolchain/check/testdata/function/declaration/import.carbon @@ -55,9 +55,9 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.A = %import_ref.1, .B = %import_ref.2, .C = %import_ref.3, .a = %a, .b = %b, .c = %c} [template] -// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = @__global_init.%A] -// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+5, used [template = @__global_init.%B] -// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+17, used [template = @__global_init.%C] +// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = imports.%A] +// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+5, used [template = imports.%B] +// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+17, used [template = imports.%C] // CHECK:STDOUT: %.loc6_9.1: () = tuple_literal () // CHECK:STDOUT: %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1] // CHECK:STDOUT: %a.var: ref () = var a @@ -71,23 +71,20 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @B(%import_ref: i32) -> i32; +// CHECK:STDOUT: fn @B(%b: i32) -> i32; // CHECK:STDOUT: -// CHECK:STDOUT: fn @C(%import_ref: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %A: = fn_decl @A [template] -// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.1 [template = %A] +// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.1 [template = imports.%A] // CHECK:STDOUT: %.loc6: init () = call %A.ref() // CHECK:STDOUT: assign file.%a.var, %.loc6 -// CHECK:STDOUT: %B: = fn_decl @B [template] -// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = %B] +// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = imports.%B] // CHECK:STDOUT: %.loc7_16: i32 = int_literal 1 [template = constants.%.2] // CHECK:STDOUT: %.loc7_15: init i32 = call %B.ref(%.loc7_16) // CHECK:STDOUT: assign file.%b.var, %.loc7_15 -// CHECK:STDOUT: %C: = fn_decl @C [template] -// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = %C] +// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = imports.%C] // CHECK:STDOUT: %.loc8_23: i32 = int_literal 1 [template = constants.%.2] // CHECK:STDOUT: %.loc8_25.1: (i32,) = tuple_literal (%.loc8_23) // CHECK:STDOUT: %.loc8_25.2: (i32,) = tuple_value (%.loc8_23) [template = constants.%.5] diff --git a/toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon b/toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon index 2a5090bb7667..6f75230ee9e2 100644 --- a/toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon +++ b/toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon @@ -39,8 +39,7 @@ fn Foo() {} // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.Foo = %import_ref} [template] -// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = %Foo] -// CHECK:STDOUT: %Foo: = fn_decl @Foo [template] +// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = imports.%Foo] // CHECK:STDOUT: %.loc14: = fn_decl @.1 [template] // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/definition/import.carbon b/toolchain/check/testdata/function/definition/import.carbon index 5650064f8e2b..5b6abe87bd5d 100644 --- a/toolchain/check/testdata/function/definition/import.carbon +++ b/toolchain/check/testdata/function/definition/import.carbon @@ -72,9 +72,9 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.A = %import_ref.1, .B = %import_ref.2, .C = %import_ref.3, .a = %a, .b = %b, .c = %c} [template] -// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = @__global_init.%A] -// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+6, used [template = @__global_init.%B] -// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+20, used [template = @__global_init.%C] +// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = imports.%A] +// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+6, used [template = imports.%B] +// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+20, used [template = imports.%C] // CHECK:STDOUT: %.loc6_9.1: () = tuple_literal () // CHECK:STDOUT: %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1] // CHECK:STDOUT: %a.var: ref () = var a @@ -88,23 +88,20 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @B(%import_ref: i32) -> i32; +// CHECK:STDOUT: fn @B(%b: i32) -> i32; // CHECK:STDOUT: -// CHECK:STDOUT: fn @C(%import_ref: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %A: = fn_decl @A [template] -// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.1 [template = %A] +// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.1 [template = imports.%A] // CHECK:STDOUT: %.loc6: init () = call %A.ref() // CHECK:STDOUT: assign file.%a.var, %.loc6 -// CHECK:STDOUT: %B: = fn_decl @B [template] -// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = %B] +// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = imports.%B] // CHECK:STDOUT: %.loc7_16: i32 = int_literal 1 [template = constants.%.2] // CHECK:STDOUT: %.loc7_15: init i32 = call %B.ref(%.loc7_16) // CHECK:STDOUT: assign file.%b.var, %.loc7_15 -// CHECK:STDOUT: %C: = fn_decl @C [template] -// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = %C] +// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = imports.%C] // CHECK:STDOUT: %.loc8_23: i32 = int_literal 1 [template = constants.%.2] // CHECK:STDOUT: %.loc8_25.1: (i32,) = tuple_literal (%.loc8_23) // CHECK:STDOUT: %.loc8_25.2: (i32,) = tuple_value (%.loc8_23) [template = constants.%.5] diff --git a/toolchain/check/testdata/namespace/fail_conflict_imported_namespace_second.carbon b/toolchain/check/testdata/namespace/fail_conflict_imported_namespace_second.carbon index 6d7350cb9db2..c110d5e555f2 100644 --- a/toolchain/check/testdata/namespace/fail_conflict_imported_namespace_second.carbon +++ b/toolchain/check/testdata/namespace/fail_conflict_imported_namespace_second.carbon @@ -45,8 +45,7 @@ fn NS.Foo(); // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.NS = %import_ref} [template] -// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = %NS] -// CHECK:STDOUT: %NS: = fn_decl @NS [template] +// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = imports.%NS] // CHECK:STDOUT: %.loc12: = namespace {} [template] // CHECK:STDOUT: %.loc20: = fn_decl @.1 [template] // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/namespace/imported.carbon b/toolchain/check/testdata/namespace/imported.carbon index 4d2b67a3c059..f85f603822cd 100644 --- a/toolchain/check/testdata/namespace/imported.carbon +++ b/toolchain/check/testdata/namespace/imported.carbon @@ -50,8 +50,8 @@ var package_b: () = package.NS.ChildNS.B(); // CHECK:STDOUT: %.2: = namespace {.ChildNS = %.3, .A = %import_ref.3}, %import_ref.1 [template] // CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+2, used // CHECK:STDOUT: %.3: = namespace {.B = %import_ref.4}, %import_ref.2 [template] -// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+3, used [template = @__global_init.%A] -// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+4, used [template = @__global_init.%B] +// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+3, used [template = imports.%A] +// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+4, used [template = imports.%B] // CHECK:STDOUT: %.loc4_9.1: () = tuple_literal () // CHECK:STDOUT: %.loc4_9.2: type = converted %.loc4_9.1, constants.%.1 [template = constants.%.1] // CHECK:STDOUT: %a.var: ref () = var a @@ -77,25 +77,23 @@ var package_b: () = package.NS.ChildNS.B(); // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %NS.ref.loc4: = name_ref NS, file.%.2 [template = file.%.2] -// CHECK:STDOUT: %A: = fn_decl @A [template] -// CHECK:STDOUT: %A.ref.loc4: = name_ref A, file.%import_ref.3 [template = %A] +// CHECK:STDOUT: %A.ref.loc4: = name_ref A, file.%import_ref.3 [template = imports.%A] // CHECK:STDOUT: %.loc4: init () = call %A.ref.loc4() // CHECK:STDOUT: assign file.%a.var, %.loc4 // CHECK:STDOUT: %NS.ref.loc5: = name_ref NS, file.%.2 [template = file.%.2] // CHECK:STDOUT: %ChildNS.ref.loc5: = name_ref ChildNS, file.%.3 [template = file.%.3] -// CHECK:STDOUT: %B: = fn_decl @B [template] -// CHECK:STDOUT: %B.ref.loc5: = name_ref B, file.%import_ref.4 [template = %B] +// CHECK:STDOUT: %B.ref.loc5: = name_ref B, file.%import_ref.4 [template = imports.%B] // CHECK:STDOUT: %.loc5: init () = call %B.ref.loc5() // CHECK:STDOUT: assign file.%b.var, %.loc5 // CHECK:STDOUT: %package.ref.loc7: = name_ref package, package [template = package] // CHECK:STDOUT: %NS.ref.loc7: = name_ref NS, file.%.2 [template = file.%.2] -// CHECK:STDOUT: %A.ref.loc7: = name_ref A, file.%import_ref.3 [template = %A] +// CHECK:STDOUT: %A.ref.loc7: = name_ref A, file.%import_ref.3 [template = imports.%A] // CHECK:STDOUT: %.loc7: init () = call %A.ref.loc7() // CHECK:STDOUT: assign file.%package_a.var, %.loc7 // CHECK:STDOUT: %package.ref.loc8: = name_ref package, package [template = package] // CHECK:STDOUT: %NS.ref.loc8: = name_ref NS, file.%.2 [template = file.%.2] // CHECK:STDOUT: %ChildNS.ref.loc8: = name_ref ChildNS, file.%.3 [template = file.%.3] -// CHECK:STDOUT: %B.ref.loc8: = name_ref B, file.%import_ref.4 [template = %B] +// CHECK:STDOUT: %B.ref.loc8: = name_ref B, file.%import_ref.4 [template = imports.%B] // CHECK:STDOUT: %.loc8: init () = call %B.ref.loc8() // CHECK:STDOUT: assign file.%package_b.var, %.loc8 // CHECK:STDOUT: return diff --git a/toolchain/check/testdata/namespace/imported_indirect.carbon b/toolchain/check/testdata/namespace/imported_indirect.carbon index 87fbc9b59837..0e432aee56e6 100644 --- a/toolchain/check/testdata/namespace/imported_indirect.carbon +++ b/toolchain/check/testdata/namespace/imported_indirect.carbon @@ -97,7 +97,7 @@ var e: () = A.B.C.D(); // CHECK:STDOUT: %.3: = namespace {.C = %.4}, %import_ref.2 [template] // CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+6, used // CHECK:STDOUT: %.4: = namespace {.D = %import_ref.4}, %import_ref.3 [template] -// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+7, used [template = @__global_init.%D] +// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+7, used [template = imports.%D] // CHECK:STDOUT: %.loc5_9.1: () = tuple_literal () // CHECK:STDOUT: %.loc5_9.2: type = converted %.loc5_9.1, constants.%.1 [template = constants.%.1] // CHECK:STDOUT: %e.var: ref () = var e @@ -111,8 +111,7 @@ var e: () = A.B.C.D(); // CHECK:STDOUT: %A.ref: = name_ref A, file.%.2 [template = file.%.2] // CHECK:STDOUT: %B.ref: = name_ref B, file.%.3 [template = file.%.3] // CHECK:STDOUT: %C.ref: = name_ref C, file.%.4 [template = file.%.4] -// CHECK:STDOUT: %D: = fn_decl @D [template] -// CHECK:STDOUT: %D.ref: = name_ref D, file.%import_ref.4 [template = %D] +// CHECK:STDOUT: %D.ref: = name_ref D, file.%import_ref.4 [template = imports.%D] // CHECK:STDOUT: %.loc5: init () = call %D.ref() // CHECK:STDOUT: assign file.%e.var, %.loc5 // CHECK:STDOUT: return diff --git a/toolchain/check/testdata/namespace/merging.carbon b/toolchain/check/testdata/namespace/merging.carbon index 027f92c5c0c2..556aafe4fd44 100644 --- a/toolchain/check/testdata/namespace/merging.carbon +++ b/toolchain/check/testdata/namespace/merging.carbon @@ -85,9 +85,9 @@ fn Run() { // CHECK:STDOUT: package: = namespace {.NS = %.loc7_13.1, .Run = %Run} [template] // CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used // CHECK:STDOUT: %.loc7_13.1: = namespace {.A = %import_ref.2, .B1 = %import_ref.3, .B2 = %import_ref.4, .C = %C}, %import_ref.1 [template] -// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+2, used [template = @Run.%A] -// CHECK:STDOUT: %import_ref.3: = import_ref ir2, inst+2, used [template = @Run.%B1] -// CHECK:STDOUT: %import_ref.4: = import_ref ir2, inst+5, used [template = @Run.%B2] +// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+2, used [template = imports.%A] +// CHECK:STDOUT: %import_ref.3: = import_ref ir2, inst+2, used [template = imports.%B1] +// CHECK:STDOUT: %import_ref.4: = import_ref ir2, inst+5, used [template = imports.%B2] // CHECK:STDOUT: %.loc7_13.2: = namespace {} [template] // CHECK:STDOUT: %C: = fn_decl @C [template] // CHECK:STDOUT: %Run: = fn_decl @Run [template] @@ -101,16 +101,13 @@ fn Run() { // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %NS.ref.loc12: = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1] -// CHECK:STDOUT: %A: = fn_decl @A [template] -// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.2 [template = %A] +// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.2 [template = imports.%A] // CHECK:STDOUT: %.loc12: init () = call %A.ref() // CHECK:STDOUT: %NS.ref.loc13: = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1] -// CHECK:STDOUT: %B1: = fn_decl @B1 [template] -// CHECK:STDOUT: %B1.ref: = name_ref B1, file.%import_ref.3 [template = %B1] +// CHECK:STDOUT: %B1.ref: = name_ref B1, file.%import_ref.3 [template = imports.%B1] // CHECK:STDOUT: %.loc13: init () = call %B1.ref() // CHECK:STDOUT: %NS.ref.loc14: = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1] -// CHECK:STDOUT: %B2: = fn_decl @B2 [template] -// CHECK:STDOUT: %B2.ref: = name_ref B2, file.%import_ref.4 [template = %B2] +// CHECK:STDOUT: %B2.ref: = name_ref B2, file.%import_ref.4 [template = imports.%B2] // CHECK:STDOUT: %.loc14: init () = call %B2.ref() // CHECK:STDOUT: %NS.ref.loc15: = name_ref NS, file.%.loc7_13.1 [template = file.%.loc7_13.1] // CHECK:STDOUT: %C.ref: = name_ref C, file.%C [template = file.%C] diff --git a/toolchain/check/testdata/packages/loaded_global.carbon b/toolchain/check/testdata/packages/loaded_global.carbon index 079589a365ec..9c31499db71b 100644 --- a/toolchain/check/testdata/packages/loaded_global.carbon +++ b/toolchain/check/testdata/packages/loaded_global.carbon @@ -51,7 +51,7 @@ var package_b: () = package.B(); // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.A = %import_ref, .a = %a, .package_a = %package_a} [template] -// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = @__global_init.%A] +// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = imports.%A] // CHECK:STDOUT: %.loc4_9.1: () = tuple_literal () // CHECK:STDOUT: %.loc4_9.2: type = converted %.loc4_9.1, constants.%.1 [template = constants.%.1] // CHECK:STDOUT: %a.var: ref () = var a @@ -66,12 +66,11 @@ var package_b: () = package.B(); // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %A: = fn_decl @A [template] -// CHECK:STDOUT: %A.ref.loc4: = name_ref A, file.%import_ref [template = %A] +// CHECK:STDOUT: %A.ref.loc4: = name_ref A, file.%import_ref [template = imports.%A] // CHECK:STDOUT: %.loc4: init () = call %A.ref.loc4() // CHECK:STDOUT: assign file.%a.var, %.loc4 // CHECK:STDOUT: %package.ref: = name_ref package, package [template = package] -// CHECK:STDOUT: %A.ref.loc6: = name_ref A, file.%import_ref [template = %A] +// CHECK:STDOUT: %A.ref.loc6: = name_ref A, file.%import_ref [template = imports.%A] // CHECK:STDOUT: %.loc6: init () = call %A.ref.loc6() // CHECK:STDOUT: assign file.%package_a.var, %.loc6 // CHECK:STDOUT: return @@ -94,7 +93,7 @@ var package_b: () = package.B(); // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace {.B = %import_ref, .b = %b, .package_b = %package_b} [template] -// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = @__global_init.%B] +// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = imports.%B] // CHECK:STDOUT: %.loc6_9.1: () = tuple_literal () // CHECK:STDOUT: %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1] // CHECK:STDOUT: %b.var: ref () = var b @@ -109,12 +108,11 @@ var package_b: () = package.B(); // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %B: = fn_decl @B [template] -// CHECK:STDOUT: %B.ref.loc6: = name_ref B, file.%import_ref [template = %B] +// CHECK:STDOUT: %B.ref.loc6: = name_ref B, file.%import_ref [template = imports.%B] // CHECK:STDOUT: %.loc6: init () = call %B.ref.loc6() // CHECK:STDOUT: assign file.%b.var, %.loc6 // CHECK:STDOUT: %package.ref: = name_ref package, package [template = package] -// CHECK:STDOUT: %B.ref.loc8: = name_ref B, file.%import_ref [template = %B] +// CHECK:STDOUT: %B.ref.loc8: = name_ref B, file.%import_ref [template = imports.%B] // CHECK:STDOUT: %.loc8: init () = call %B.ref.loc8() // CHECK:STDOUT: assign file.%package_b.var, %.loc8 // CHECK:STDOUT: return diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 62b67e9f1fd1..b5d38ae9f650 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -32,8 +32,9 @@ class InstNamer { enum class ScopeId : int32_t { None = -1, File = 0, - Constants = 1, - FirstFunction = 2, + ImportRef = 1, + Constants = 2, + FirstFunction = 3, }; static_assert(sizeof(ScopeId) == sizeof(FunctionId)); @@ -57,6 +58,13 @@ class InstNamer { GetScopeInfo(ScopeId::File).name = globals.AddNameUnchecked("file"); CollectNamesInBlock(ScopeId::File, sem_ir.top_inst_block_id()); + // Build the imports scope, used only by import-related instructions without + // a block. + // TODO: Consider other approaches for ImportRef constant formatting, as the + // actual source of these remains unclear even though they're referenced in + // constants. + GetScopeInfo(ScopeId::ImportRef).name = globals.AddNameUnchecked("imports"); + // Build each function scope. for (auto [i, fn] : llvm::enumerate(sem_ir.functions().array_ref())) { auto fn_id = FunctionId(i); @@ -497,6 +505,13 @@ class InstNamer { case ImportRefUnused::Kind: case ImportRefUsed::Kind: { add_inst_name("import_ref"); + // When building import refs, we frequently add instructions without a + // block. Constants that refer to them need to be separately named. + auto const_id = sem_ir_.constant_values().Get(inst_id); + if (const_id.is_valid() && const_id.is_template() && + !insts[const_id.inst_id().index].second) { + CollectNamesInBlock(ScopeId::ImportRef, const_id.inst_id()); + } continue; } case InterfaceDecl::Kind: {