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: {