From 5694dd152e22c5bb7d6a3f3522f1fd13d4e1af34 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Mon, 22 Apr 2024 08:04:16 -0700 Subject: [PATCH] Add ExternDecl and ExternType for extern classes. (#3893) This expands `extern` handling for most cases. --- toolchain/check/class.cpp | 9 +- toolchain/check/context.cpp | 2 + toolchain/check/eval.cpp | 10 + toolchain/check/handle_class.cpp | 84 ++++-- toolchain/check/handle_function.cpp | 2 + toolchain/check/import.cpp | 5 + toolchain/check/import_ref.cpp | 16 + toolchain/check/merge.cpp | 78 +++-- toolchain/check/merge.h | 2 + .../class/cross_package_import.carbon | 64 ++-- toolchain/check/testdata/class/extern.carbon | 277 +++++++----------- .../testdata/class/fail_import_misuses.carbon | 18 +- ...decl.carbon => import_forward_decl.carbon} | 35 +-- toolchain/lower/handle_aggregates.cpp | 5 + toolchain/lower/handle_type.cpp | 5 + toolchain/sem_ir/file.cpp | 10 + toolchain/sem_ir/inst_kind.def | 2 + toolchain/sem_ir/typed_insts.h | 16 + 18 files changed, 332 insertions(+), 308 deletions(-) rename toolchain/check/testdata/class/{fail_todo_import_forward_decl.carbon => import_forward_decl.carbon} (56%) diff --git a/toolchain/check/class.cpp b/toolchain/check/class.cpp index 8a19556cb7a1..ffaf6c866b7f 100644 --- a/toolchain/check/class.cpp +++ b/toolchain/check/class.cpp @@ -9,7 +9,7 @@ namespace Carbon::Check { auto MergeClassRedecl(Context& context, SemIRLoc new_loc, - SemIR::Class& new_class, bool /*new_is_import*/, + SemIR::Class& new_class, bool new_is_import, bool new_is_definition, bool new_is_extern, SemIR::ClassId prev_class_id, bool prev_is_extern, SemIR::ImportIRInstId prev_import_ir_inst_id) -> bool { @@ -51,6 +51,13 @@ auto MergeClassRedecl(Context& context, SemIRLoc new_loc, prev_class.object_repr_id = new_class.object_repr_id; } + if ((prev_import_ir_inst_id.is_valid() && !new_is_import) || + (prev_is_extern && !new_is_extern)) { + prev_class.decl_id = new_class.decl_id; + ReplacePrevInstForMerge( + context, prev_class.enclosing_scope_id, prev_class.name_id, + new_is_import ? new_loc.inst_id : new_class.decl_id); + } return true; } diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 46359628d8f8..97bbf5a6ea8f 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -977,6 +977,7 @@ class TypeCompleter { case SemIR::ClassInit::Kind: case SemIR::Converted::Kind: case SemIR::Deref::Kind: + case SemIR::ExternDecl::Kind: case SemIR::FacetTypeAccess::Kind: case SemIR::FloatLiteral::Kind: case SemIR::FieldDecl::Kind: @@ -1056,6 +1057,7 @@ class TypeCompleter { case SemIR::AssociatedEntityType::Kind: case SemIR::BindSymbolicName::Kind: + case SemIR::ExternType::Kind: case SemIR::FloatType::Kind: case SemIR::IntType::Kind: case SemIR::PointerType::Kind: diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 0451afb6f87a..7bb0cebe7fb6 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -897,6 +897,15 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) return RebuildIfFieldsAreConstant(context, inst, &SemIR::BoundMethod::object_id, &SemIR::BoundMethod::function_id); + case CARBON_KIND(SemIR::ExternDecl extern_decl): { + // Return an extern form of the declaration's constant value. + auto non_extern_type_id = context.GetTypeIdForTypeConstant( + context.constant_values().Get(extern_decl.decl_id)); + return MakeConstantResult( + context, + SemIR::ExternType{SemIR::TypeId::TypeType, non_extern_type_id}, + Phase::Template); + } case SemIR::InterfaceWitness::Kind: return RebuildIfFieldsAreConstant(context, inst, &SemIR::InterfaceWitness::elements_id); @@ -975,6 +984,7 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) } case SemIR::ClassType::Kind: + case SemIR::ExternType::Kind: case SemIR::InterfaceType::Kind: CARBON_FATAL() << inst.kind() << " is only created during corresponding Decl handling."; diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index 7aa67913a257..0c5d7c76e7ab 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -2,11 +2,14 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include "toolchain/base/kind_switch.h" #include "toolchain/check/class.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" +#include "toolchain/check/decl_name_stack.h" #include "toolchain/check/merge.h" #include "toolchain/check/modifiers.h" +#include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -35,6 +38,51 @@ auto HandleClassIntroducer(Context& context, Parse::ClassIntroducerId node_id) return true; } +// Adds the name to name lookup. If there's a conflict, tries to merge. May +// update class_decl and class_info when merging. +static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id, + const DeclNameStack::NameContext& name_context, + SemIR::InstId class_decl_id, + SemIR::ClassDecl& class_decl, + SemIR::Class& class_info, bool is_definition, + bool is_extern) -> void { + auto prev_id = + context.decl_name_stack().LookupOrAddName(name_context, class_decl_id); + if (prev_id.is_valid()) { + auto prev_inst_for_merge = + ResolvePrevInstForMerge(context, node_id, prev_id); + + auto prev_class_id = SemIR::ClassId::Invalid; + CARBON_KIND_SWITCH(prev_inst_for_merge.inst) { + case CARBON_KIND(SemIR::ClassDecl class_decl): { + prev_class_id = class_decl.class_id; + break; + } + case CARBON_KIND(SemIR::ClassType class_type): { + prev_class_id = class_type.class_id; + break; + } + default: + // This is a redeclaration of something other than a class. + context.DiagnoseDuplicateName(class_decl_id, prev_id); + break; + } + + if (prev_class_id.is_valid()) { + if (MergeClassRedecl(context, node_id, class_info, + /*new_is_import=*/false, is_definition, is_extern, + prev_class_id, prev_inst_for_merge.is_extern, + prev_inst_for_merge.import_ir_inst_id)) { + // When merging, use the existing entity rather than adding a new one. + class_decl.class_id = prev_class_id; + } + } else { + // This is a redeclaration of something other than a class. + context.DiagnoseDuplicateName(class_decl_id, prev_id); + } + } +} + static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id, bool is_definition) -> std::tuple { @@ -88,30 +136,16 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id, .decl_id = class_decl_id, .inheritance_kind = inheritance_kind}; - // Check whether this is a redeclaration. - auto prev_id = - context.decl_name_stack().LookupOrAddName(name_context, class_decl_id); - if (prev_id.is_valid()) { - auto prev_inst_for_merge = - ResolvePrevInstForMerge(context, node_id, prev_id); - - if (auto prev_class_decl = - prev_inst_for_merge.inst.TryAs()) { - // TODO: Fix prev_is_extern. - if (MergeClassRedecl(context, node_id, class_info, - /*new_is_import=*/false, is_definition, is_extern, - prev_class_decl->class_id, - /*prev_is_extern=*/false, - prev_inst_for_merge.import_ir_inst_id)) { - // When merging, use the existing entity rather than adding a new one. - class_decl.class_id = prev_class_decl->class_id; - } - } else { - // This is a redeclaration of something other than a class. - context.DiagnoseDuplicateName(class_decl_id, prev_id); - } + auto extern_decl_id = SemIR::InstId::Invalid; + if (is_extern) { + extern_decl_id = context.AddPlaceholderInst( + {node_id, SemIR::ExternDecl{SemIR::TypeId::TypeType, class_decl_id}}); } + MergeOrAddName(context, node_id, name_context, + extern_decl_id.is_valid() ? extern_decl_id : class_decl_id, + class_decl, class_info, is_definition, is_extern); + // Create a new class if this isn't a valid redeclaration. bool is_new_class = !class_decl.class_id.is_valid(); if (is_new_class) { @@ -130,6 +164,12 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id, class_info.self_type_id = context.GetTypeIdForTypeInst(class_decl_id); } + if (is_extern) { + context.ReplaceInstBeforeConstantUse( + extern_decl_id, + SemIR::ExternDecl{SemIR::TypeId::TypeType, class_decl_id}); + } + return {class_decl.class_id, class_decl_id}; } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 6e59a90e50f2..d8db94d063e4 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -118,6 +118,8 @@ static auto BuildFunctionDecl(Context& context, auto function_decl = SemIR::FunctionDecl{ context.GetBuiltinType(SemIR::BuiltinKind::FunctionType), SemIR::FunctionId::Invalid, decl_block_id}; + // TODO: Should Function replace is_extern with ExternDecl, similar to + // ClassDecl? auto function_info = SemIR::Function{ .name_id = name_context.name_id_for_new_inst(), .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(), diff --git a/toolchain/check/import.cpp b/toolchain/check/import.cpp index c2db866b7c0f..f5f29a199e1a 100644 --- a/toolchain/check/import.cpp +++ b/toolchain/check/import.cpp @@ -36,6 +36,11 @@ static auto GetImportName(const SemIR::File& import_sem_ir, return {class_info.name_id, class_info.enclosing_scope_id}; } + case CARBON_KIND(SemIR::ExternDecl extern_decl): { + return GetImportName(import_sem_ir, + import_sem_ir.insts().Get(extern_decl.decl_id)); + } + case CARBON_KIND(SemIR::FunctionDecl function_decl): { const auto& function = import_sem_ir.functions().Get(function_decl.function_id); diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 3ec995f89cdb..3dfa5384da63 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -409,6 +409,9 @@ class ImportRefResolver { case CARBON_KIND(SemIR::ConstType inst): { return TryResolveTypedInst(inst); } + case CARBON_KIND(SemIR::ExternDecl inst): { + return TryResolveTypedInst(inst); + } case CARBON_KIND(SemIR::FieldDecl inst): { return TryResolveTypedInst(inst, inst_id); } @@ -669,6 +672,19 @@ class ImportRefResolver { SemIR::ConstType{SemIR::TypeId::TypeType, inner_type_id})}; } + auto TryResolveTypedInst(SemIR::ExternDecl inst) -> ResolveResult { + auto initial_work = work_stack_.size(); + CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType); + auto decl_const_id = GetLocalConstantId(inst.decl_id); + if (HasNewWork(initial_work)) { + return ResolveResult::Retry(); + } + auto extern_id = context_.AddInstInNoBlock(SemIR::LocIdAndInst::Untyped( + AddImportIRInst(inst.decl_id), + SemIR::ExternDecl{SemIR::TypeId::TypeType, decl_const_id.inst_id()})); + return {context_.constant_values().Get(extern_id)}; + } + auto TryResolveTypedInst(SemIR::FieldDecl inst, SemIR::InstId import_inst_id) -> ResolveResult { auto initial_work = work_stack_.size(); diff --git a/toolchain/check/merge.cpp b/toolchain/check/merge.cpp index a66693dac5b9..4440c51af623 100644 --- a/toolchain/check/merge.cpp +++ b/toolchain/check/merge.cpp @@ -127,30 +127,44 @@ auto CheckIsAllowedRedecl(Context& context, Lex::TokenKind decl_kind, auto ResolvePrevInstForMerge(Context& context, Parse::NodeId node_id, SemIR::InstId prev_inst_id) -> InstForMerge { - auto prev_inst = context.insts().Get(prev_inst_id); - auto import_ref = prev_inst.TryAs(); - // If not imported, use the instruction directly. - if (!import_ref) { - return {.inst = prev_inst, - .import_ir_inst_id = SemIR::ImportIRInstId::Invalid}; + InstForMerge result = {.inst = context.insts().Get(prev_inst_id), + .import_ir_inst_id = SemIR::ImportIRInstId::Invalid, + .is_extern = false}; + + CARBON_KIND_SWITCH(result.inst) { + case CARBON_KIND(SemIR::ExternDecl extern_decl): { + result.is_extern = true; + result.inst = context.insts().Get(extern_decl.decl_id); + break; + } + case CARBON_KIND(SemIR::ImportRefUsed import_ref): { + CARBON_DIAGNOSTIC( + RedeclOfUsedImport, Error, + "Redeclaration of imported entity that was previously used."); + CARBON_DIAGNOSTIC(UsedImportLoc, Note, "Import used here."); + context.emitter() + .Build(node_id, RedeclOfUsedImport) + .Note(import_ref.used_id, UsedImportLoc) + .Emit(); + [[fallthrough]]; + } + case SemIR::ImportRefLoaded::Kind: { + // Follow the import ref. + auto import_ref = result.inst.As(); + result.import_ir_inst_id = import_ref.import_ir_inst_id; + result.inst = context.insts().Get( + context.constant_values().Get(prev_inst_id).inst_id()); + if (auto extern_type = result.inst.TryAs()) { + result.inst = + context.types().GetAsInst(extern_type->non_extern_type_id); + } + break; + } + default: + break; } - // If the import ref was previously used, print a diagnostic. - if (auto import_ref_used = prev_inst.TryAs()) { - CARBON_DIAGNOSTIC( - RedeclOfUsedImport, Error, - "Redeclaration of imported entity that was previously used."); - CARBON_DIAGNOSTIC(UsedImportLoc, Note, "Import used here."); - context.emitter() - .Build(node_id, RedeclOfUsedImport) - .Note(import_ref_used->used_id, UsedImportLoc) - .Emit(); - } - - // Follow the import ref. - return {.inst = context.insts().Get( - context.constant_values().Get(prev_inst_id).inst_id()), - .import_ir_inst_id = import_ref->import_ir_inst_id}; + return result; } // Returns the instruction to consider when merging the given inst_id. Returns @@ -185,9 +199,18 @@ static auto ResolveMergeableInst(Context& context, SemIR::InstId inst_id) if (!const_id.is_constant()) { return std::nullopt; } - return { - {.inst = context.insts().Get(const_id.inst_id()), - .import_ir_inst_id = inst.As().import_ir_inst_id}}; + + InstForMerge result = { + .inst = context.insts().Get(const_id.inst_id()), + .import_ir_inst_id = inst.As().import_ir_inst_id, + .is_extern = false}; + + if (auto extern_type = result.inst.TryAs()) { + result.is_extern = true; + result.inst = context.types().GetAsInst(extern_type->non_extern_type_id); + } + + return result; } auto ReplacePrevInstForMerge(Context& context, SemIR::NameScopeId scope_id, @@ -240,11 +263,10 @@ auto MergeImportRef(Context& context, SemIR::InstId new_inst_id, } auto new_class = context.classes().Get(new_type.class_id); - // TODO: Fix new_is_extern and prev_is_extern. MergeClassRedecl(context, new_inst_id, new_class, /*new_is_import=*/true, new_class.is_defined(), - /*new_is_extern=*/false, prev_type->class_id, - /*prev_is_extern=*/false, prev_inst->import_ir_inst_id); + new_inst->is_extern, prev_type->class_id, + prev_inst->is_extern, prev_inst->import_ir_inst_id); return; } default: diff --git a/toolchain/check/merge.h b/toolchain/check/merge.h index de1bd769736c..654d30ebce1b 100644 --- a/toolchain/check/merge.h +++ b/toolchain/check/merge.h @@ -40,6 +40,8 @@ struct InstForMerge { // The imported instruction, or invalid if not an import. This should // typically only be used for the ImportIRId, but we only load it if needed. SemIR::ImportIRInstId import_ir_inst_id; + // True if an `extern` declaration. + bool is_extern; }; // Resolves prev_inst_id for merging (or name conflicts). This handles imports diff --git a/toolchain/check/testdata/class/cross_package_import.carbon b/toolchain/check/testdata/class/cross_package_import.carbon index efe43a141312..8e7fa46547ab 100644 --- a/toolchain/check/testdata/class/cross_package_import.carbon +++ b/toolchain/check/testdata/class/cross_package_import.carbon @@ -44,41 +44,19 @@ library "extern" api; import Other library "extern"; -// CHECK:STDERR: fail_extern.carbon:[[@LINE+10]]:8: ERROR: Variable has incomplete type `C`. +// CHECK:STDERR: fail_extern.carbon:[[@LINE+4]]:1: ERROR: Cannot implicitly convert from `{}` to `extern C`. // CHECK:STDERR: var c: Other.C = {}; -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: fail_extern.carbon:[[@LINE-5]]:1: In import. -// CHECK:STDERR: import Other library "extern"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: other_extern.carbon:4:1: Class was forward declared here. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var c: Other.C = {}; -// --- fail_todo_merge_define_extern.carbon +// --- merge_define_extern.carbon library "merge_define_extern" api; import Other library "define"; -// CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE+12]]:1: In import. -// CHECK:STDERR: import Other library "extern"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: other_extern.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import Other library "define"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: other_define.carbon:4:1: Previously declared here. -// CHECK:STDERR: class C {} -// CHECK:STDERR: ^~~~~~~~~ import Other library "extern"; -// CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE+4]]:8: In name lookup for `C`. -// CHECK:STDERR: var c: Other.C = {}; -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: var c: Other.C = {}; // --- fail_conflict.carbon @@ -130,15 +108,17 @@ var c: Other.C = {}; // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .C = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc4: type = extern_decl %C.decl [template = constants.%.1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; @@ -204,7 +184,8 @@ var c: Other.C = {}; // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] +// CHECK:STDOUT: %.2: type = struct_type {} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -216,31 +197,32 @@ var c: Other.C = {}; // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %Other: = namespace [template] {} // CHECK:STDOUT: %Other.ref: = name_ref Other, %Other [template = %Other] -// CHECK:STDOUT: %import_ref: type = import_ref ir3, inst+2, loc_14 [template = constants.%C] +// CHECK:STDOUT: %import_ref: type = import_ref ir3, inst+3, loc_14 [template = constants.%.1] // CHECK:STDOUT: %C.decl: invalid = class_decl @C [template = constants.%C] {} -// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref [template = constants.%C] -// CHECK:STDOUT: %c.var: ref = var c -// CHECK:STDOUT: %c: ref = bind_name c, %c.var +// CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref [template = constants.%.1] +// CHECK:STDOUT: %c.var: ref extern C = var c +// CHECK:STDOUT: %c: ref extern C = bind_name c, %c.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc16: {} = struct_literal () +// CHECK:STDOUT: %.loc10: {} = struct_literal () // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_merge_define_extern.carbon +// CHECK:STDOUT: --- merge_define_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.1: type = class_type @C.1 [template] // CHECK:STDOUT: %.1: type = struct_type {} [template] // CHECK:STDOUT: %C.2: type = class_type @C.2 [template] -// CHECK:STDOUT: %.2: type = tuple_type () [template] -// CHECK:STDOUT: %.3: type = ptr_type {} [template] -// CHECK:STDOUT: %.4: C = struct_value () [template] +// CHECK:STDOUT: %.2: type = extern_type C [template] +// CHECK:STDOUT: %.3: type = tuple_type () [template] +// CHECK:STDOUT: %.4: type = ptr_type {} [template] +// CHECK:STDOUT: %.5: C = struct_value () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -255,7 +237,7 @@ var c: Other.C = {}; // CHECK:STDOUT: %import_ref.1: type = import_ref ir3, inst+2, loc_19 [template = constants.%C.1] // CHECK:STDOUT: %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {} // CHECK:STDOUT: %import_ref.2 = import_ref ir3, inst+3, unloaded -// CHECK:STDOUT: %import_ref.3: type = import_ref ir4, inst+2, loaded [template = constants.%C.2] +// CHECK:STDOUT: %import_ref.3: type = import_ref ir4, inst+3, loaded [template = constants.%.2] // CHECK:STDOUT: %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {} // CHECK:STDOUT: %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C.1] // CHECK:STDOUT: %c.var: ref C = var c @@ -271,10 +253,10 @@ var c: Other.C = {}; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc23_19.1: {} = struct_literal () -// CHECK:STDOUT: %.loc23_19.2: init C = class_init (), file.%c.var [template = constants.%.4] -// CHECK:STDOUT: %.loc23_20: init C = converted %.loc23_19.1, %.loc23_19.2 [template = constants.%.4] -// CHECK:STDOUT: assign file.%c.var, %.loc23_20 +// CHECK:STDOUT: %.loc7_19.1: {} = struct_literal () +// CHECK:STDOUT: %.loc7_19.2: init C = class_init (), file.%c.var [template = constants.%.5] +// CHECK:STDOUT: %.loc7_20: init C = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%.5] +// CHECK:STDOUT: assign file.%c.var, %.loc7_20 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/extern.carbon b/toolchain/check/testdata/class/extern.carbon index 735c12369d3d..52390709869f 100644 --- a/toolchain/check/testdata/class/extern.carbon +++ b/toolchain/check/testdata/class/extern.carbon @@ -41,12 +41,12 @@ class C {} library "fail_decl_fn_in_extern" api; extern class C; -// CHECK:STDERR: fail_decl_fn_in_extern.carbon:[[@LINE+7]]:4: ERROR: Cannot declare a member of incomplete class `C`. +// CHECK:STDERR: fail_decl_fn_in_extern.carbon:[[@LINE+7]]:6: ERROR: Name qualifiers are only allowed for entities that provide a scope. +// CHECK:STDERR: fn C.F(); +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_decl_fn_in_extern.carbon:[[@LINE+4]]:4: Non-scope entity referenced here. // CHECK:STDERR: fn C.F(); // CHECK:STDERR: ^ -// CHECK:STDERR: fail_decl_fn_in_extern.carbon:[[@LINE-4]]:1: Class was forward declared here. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn C.F(); @@ -100,11 +100,18 @@ class C { extern class D; } -// --- todo_fail_def_after_extern_decl.carbon +// --- fail_def_after_extern_decl.carbon library "fail_def_after_extern_decl" api; extern class C; +// CHECK:STDERR: fail_def_after_extern_decl.carbon:[[@LINE+7]]:1: ERROR: Redeclarations of `class C` in the same library must match use of `extern`. +// CHECK:STDERR: class C {} +// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: fail_def_after_extern_decl.carbon:[[@LINE-4]]:1: Previously declared here. +// CHECK:STDERR: extern class C; +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: class C {} // --- fail_extern_decl_after_decl.carbon @@ -121,64 +128,25 @@ class C; // CHECK:STDERR: extern class C; -// --- fail_todo_import_extern_decl_then_decl.carbon +// --- import_extern_decl_then_decl.carbon library "import_extern_decl_then_decl" api; import library "extern_decl"; -// CHECK:STDERR: fail_todo_import_extern_decl_then_decl.carbon:[[@LINE+13]]:1: In import. -// CHECK:STDERR: import library "decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: decl.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`. -// CHECK:STDERR: class C; -// CHECK:STDERR: ^~~~~~~~ -// CHECK:STDERR: fail_todo_import_extern_decl_then_decl.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import library "extern_decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: import library "decl"; -// --- fail_todo_import_decl_then_extern_decl.carbon +// --- import_decl_then_extern_decl.carbon library "import_decl_then_extern_decl" api; import library "decl"; -// CHECK:STDERR: fail_todo_import_decl_then_extern_decl.carbon:[[@LINE+13]]:1: In import. -// CHECK:STDERR: import library "extern_decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_decl.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_import_decl_then_extern_decl.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import library "decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: decl.carbon:4:1: Previously declared here. -// CHECK:STDERR: class C; -// CHECK:STDERR: ^~~~~~~~ -// CHECK:STDERR: import library "extern_decl"; -// --- fail_todo_import_extern_decl_then_def.carbon +// --- import_extern_decl_then_def.carbon library "import_extern_decl_then_def" api; import library "extern_decl"; -// CHECK:STDERR: fail_todo_import_extern_decl_then_def.carbon:[[@LINE+13]]:1: In import. -// CHECK:STDERR: import library "def"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: def.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`. -// CHECK:STDERR: class C {} -// CHECK:STDERR: ^~~~~~~~~ -// CHECK:STDERR: fail_todo_import_extern_decl_then_def.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import library "extern_decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: import library "def"; // --- fail_import_ownership_conflict.carbon @@ -186,124 +154,58 @@ import library "def"; library "fail_import_ownership_conflict" api; import library "extern_decl"; -// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+13]]:1: In import. -// CHECK:STDERR: import library "decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: decl.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`. -// CHECK:STDERR: class C; -// CHECK:STDERR: ^~~~~~~~ -// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import library "extern_decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: import library "decl"; -// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+13]]:1: In import. +// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+12]]:1: In import. // CHECK:STDERR: import library "def"; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: def.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`. // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ -// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE-21]]:1: In import. -// CHECK:STDERR: import library "extern_decl"; +// CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE-7]]:1: In import. +// CHECK:STDERR: import library "decl"; // CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: +// CHECK:STDERR: decl.carbon:4:1: Previously declared here. +// CHECK:STDERR: class C; +// CHECK:STDERR: ^~~~~~~~ import library "def"; -// --- fail_todo_import_extern_decl_copy.carbon +// --- import_extern_decl_copy.carbon library "import_extern_decl_copy" api; import library "extern_decl"; -// CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE+13]]:1: In import. -// CHECK:STDERR: import library "extern_decl_copy"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_decl_copy.carbon:4:1: ERROR: Only one library can declare `class C` without `extern`. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import library "extern_decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_decl.carbon:4:1: Previously declared here. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: import library "extern_decl_copy"; -// --- fail_todo_extern_decl_after_import_extern_decl.carbon +// --- extern_decl_after_import_extern_decl.carbon library "extern_decl_after_import_extern_decl" api; import library "extern_decl"; -// CHECK:STDERR: fail_todo_extern_decl_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_extern_decl_after_import_extern_decl.carbon:[[@LINE-5]]:1: In import. -// CHECK:STDERR: import library "extern_decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_decl.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: extern class C; -// --- fail_todo_decl_after_import_extern_decl.carbon +// --- decl_after_import_extern_decl.carbon library "decl_after_import_extern_decl" api; import library "decl"; -// CHECK:STDERR: fail_todo_decl_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_decl_after_import_extern_decl.carbon:[[@LINE-5]]:1: In import. -// CHECK:STDERR: import library "decl"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: decl.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: class C; -// CHECK:STDERR: ^~~~~~~~ -// CHECK:STDERR: extern class C; -// --- fail_todo_def_after_import_extern_decl.carbon +// --- def_after_import_extern_decl.carbon library "def_after_import_extern_decl" api; import library "def"; -// CHECK:STDERR: fail_todo_def_after_import_extern_decl.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_def_after_import_extern_decl.carbon:[[@LINE-5]]:1: In import. -// CHECK:STDERR: import library "def"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: def.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: class C {} -// CHECK:STDERR: ^~~~~~~~~ -// CHECK:STDERR: extern class C; -// --- fail_todo_extern_decl_after_import_def.carbon +// --- extern_decl_after_import_def.carbon library "extern_decl_after_import_def" api; import library "def"; -// CHECK:STDERR: fail_todo_extern_decl_after_import_def.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern class C; -// CHECK:STDERR: ^~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_extern_decl_after_import_def.carbon:[[@LINE-5]]:1: In import. -// CHECK:STDERR: import library "def"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: def.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: class C {} -// CHECK:STDERR: ^~~~~~~~~ extern class C; // CHECK:STDOUT: --- decl.carbon @@ -327,15 +229,17 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .C = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc4: type = extern_decl %C.decl [template = constants.%.1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; @@ -344,15 +248,17 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .C = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc4: type = extern_decl %C.decl [template = constants.%.1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; @@ -382,15 +288,17 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .C = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc4: type = extern_decl %C.decl [template = constants.%.1] // CHECK:STDOUT: %.loc12: = fn_decl @.1 [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: @@ -423,16 +331,19 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .C = %C.decl.loc4 +// CHECK:STDOUT: .C = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc4: type = extern_decl %C.decl.loc4 [template = constants.%.1] // CHECK:STDOUT: %C.decl.loc12: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc12: type = extern_decl %C.decl.loc12 [template = constants.%.1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; @@ -441,15 +352,17 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .C = %C.decl.loc4 +// CHECK:STDOUT: .C = %C.decl.loc12 // CHECK:STDOUT: } // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc4: type = extern_decl %C.decl.loc4 [template = constants.%.1] // CHECK:STDOUT: %C.decl.loc12: type = class_decl @C [template = constants.%C] {} // CHECK:STDOUT: } // CHECK:STDOUT: @@ -482,21 +395,23 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: class @D; // CHECK:STDOUT: -// CHECK:STDOUT: --- todo_fail_def_after_extern_decl.carbon +// CHECK:STDOUT: --- fail_def_after_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] +// CHECK:STDOUT: %.2: type = struct_type {} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .C = %C.decl.loc4 +// CHECK:STDOUT: .C = %C.decl.loc12 // CHECK:STDOUT: } // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [template = constants.%C] {} -// CHECK:STDOUT: %C.decl.loc5: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc4: type = extern_decl %C.decl.loc4 [template = constants.%.1] +// CHECK:STDOUT: %C.decl.loc12: type = class_decl @C [template = constants.%C] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { @@ -508,6 +423,7 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -518,23 +434,25 @@ extern class C; // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [template = constants.%C] {} // CHECK:STDOUT: %C.decl.loc12: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %.loc12: type = extern_decl %C.decl.loc12 [template = constants.%.1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_extern_decl_then_decl.carbon +// CHECK:STDOUT: --- import_extern_decl_then_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.1: type = class_type @C.1 [template] // CHECK:STDOUT: %C.2: type = class_type @C.2 [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .C = %import_ref.2 // CHECK:STDOUT: .Core = %Core // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2] +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+3, loaded [template = constants.%.1] // CHECK:STDOUT: %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1] // CHECK:STDOUT: %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {} // CHECK:STDOUT: %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {} @@ -545,10 +463,11 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: class @C.2; // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_decl_then_extern_decl.carbon +// CHECK:STDOUT: --- import_decl_then_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.1: type = class_type @C.1 [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: %C.2: type = class_type @C.2 [template] // CHECK:STDOUT: } // CHECK:STDOUT: @@ -558,7 +477,7 @@ extern class C; // CHECK:STDOUT: .Core = %Core // CHECK:STDOUT: } // CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2] -// CHECK:STDOUT: %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1] +// CHECK:STDOUT: %import_ref.2: type = import_ref ir3, inst+3, loaded [template = constants.%.1] // CHECK:STDOUT: %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {} // CHECK:STDOUT: %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {} // CHECK:STDOUT: %Core: = namespace [template] {} @@ -568,20 +487,21 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: class @C.2; // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_extern_decl_then_def.carbon +// CHECK:STDOUT: --- import_extern_decl_then_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.1: type = class_type @C.1 [template] // CHECK:STDOUT: %.1: type = struct_type {} [template] // CHECK:STDOUT: %C.2: type = class_type @C.2 [template] +// CHECK:STDOUT: %.2: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .C = %import_ref.2 // CHECK:STDOUT: .Core = %Core // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2] +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+3, loaded [template = constants.%.2] // CHECK:STDOUT: %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1] // CHECK:STDOUT: %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {} // CHECK:STDOUT: %import_ref.3 = import_ref ir3, inst+3, unloaded @@ -604,16 +524,17 @@ extern class C; // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.1: type = class_type @C.1 [template] // CHECK:STDOUT: %C.2: type = class_type @C.2 [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: %C.3: type = class_type @C.3 [template] -// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: type = struct_type {} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: .C = %import_ref.2 // CHECK:STDOUT: .Core = %Core // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2] +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+3, loaded [template = constants.%.1] // CHECK:STDOUT: %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1] // CHECK:STDOUT: %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {} // CHECK:STDOUT: %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {} @@ -623,23 +544,25 @@ extern class C; // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C.1; -// CHECK:STDOUT: -// CHECK:STDOUT: class @C.2 { +// CHECK:STDOUT: class @C.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = file.%import_ref.4 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: class @C.2; +// CHECK:STDOUT: // CHECK:STDOUT: class @C.3 { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = file.%import_ref.4 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_extern_decl_copy.carbon +// CHECK:STDOUT: --- import_extern_decl_copy.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.1: type = class_type @C.1 [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: %C.2: type = class_type @C.2 [template] +// CHECK:STDOUT: %.2: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -647,8 +570,8 @@ extern class C; // CHECK:STDOUT: .C = %import_ref.1 // CHECK:STDOUT: .Core = %Core // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C.2] -// CHECK:STDOUT: %import_ref.2: type = import_ref ir3, inst+2, loaded [template = constants.%C.1] +// CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+3, loaded [template = constants.%.2] +// CHECK:STDOUT: %import_ref.2: type = import_ref ir3, inst+3, loaded [template = constants.%.1] // CHECK:STDOUT: %C.decl.1: invalid = class_decl @C.1 [template = constants.%C.1] {} // CHECK:STDOUT: %C.decl.2: invalid = class_decl @C.2 [template = constants.%C.2] {} // CHECK:STDOUT: %Core: = namespace [template] {} @@ -658,71 +581,70 @@ extern class C; // CHECK:STDOUT: // CHECK:STDOUT: class @C.2; // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_extern_decl_after_import_extern_decl.carbon +// CHECK:STDOUT: --- extern_decl_after_import_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.1: type = class_type @.1 [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .C = %import_ref // CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .C = %C.decl.loc6 // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref: type = import_ref ir2, inst+2, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref: type = import_ref ir2, inst+3, loaded [template = constants.%.1] // CHECK:STDOUT: %Core: = namespace [template] {} -// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.1] { -// CHECK:STDOUT: %C.decl: invalid = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %C.decl.loc6: type = class_decl @C [template = constants.%C] { +// CHECK:STDOUT: %C.decl.1: invalid = class_decl @C [template = constants.%C] {} // CHECK:STDOUT: } +// CHECK:STDOUT: %.loc6: type = extern_decl %C.decl.loc6 [template = constants.%.1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: -// CHECK:STDOUT: class @.1; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_decl_after_import_extern_decl.carbon +// CHECK:STDOUT: --- decl_after_import_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.1: type = class_type @.1 [template] +// CHECK:STDOUT: %.1: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .C = %import_ref // CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .C = %C.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %import_ref: type = import_ref ir2, inst+2, loaded [template = constants.%C] // CHECK:STDOUT: %Core: = namespace [template] {} -// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.1] { -// CHECK:STDOUT: %C.decl: invalid = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %C.decl.loc6: type = class_decl @C [template = constants.%C] { +// CHECK:STDOUT: %C.decl.1: invalid = class_decl @C [template = constants.%C] {} // CHECK:STDOUT: } +// CHECK:STDOUT: %.loc6: type = extern_decl %C.decl.loc6 [template = constants.%.1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: -// CHECK:STDOUT: class @.1; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_def_after_import_extern_decl.carbon +// CHECK:STDOUT: --- def_after_import_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %.1: type = struct_type {} [template] -// CHECK:STDOUT: %.2: type = class_type @.1 [template] +// CHECK:STDOUT: %.2: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .C = %import_ref.1 // CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .C = %C.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C] // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+3, unloaded -// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.2] { -// CHECK:STDOUT: %C.decl: invalid = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %C.decl.loc6: type = class_decl @C [template = constants.%C] { +// CHECK:STDOUT: %C.decl.1: invalid = class_decl @C [template = constants.%C] {} // CHECK:STDOUT: } +// CHECK:STDOUT: %.loc6: type = extern_decl %C.decl.loc6 [template = constants.%.2] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { @@ -730,27 +652,26 @@ extern class C; // CHECK:STDOUT: .Self = file.%import_ref.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @.1; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_extern_decl_after_import_def.carbon +// CHECK:STDOUT: --- extern_decl_after_import_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %.1: type = struct_type {} [template] -// CHECK:STDOUT: %.2: type = class_type @.1 [template] +// CHECK:STDOUT: %.2: type = extern_type C [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .C = %import_ref.1 // CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .C = %C.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C] // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+3, unloaded -// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.2] { -// CHECK:STDOUT: %C.decl: invalid = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %C.decl.loc6: type = class_decl @C [template = constants.%C] { +// CHECK:STDOUT: %C.decl.1: invalid = class_decl @C [template = constants.%C] {} // CHECK:STDOUT: } +// CHECK:STDOUT: %.loc6: type = extern_decl %C.decl.loc6 [template = constants.%.2] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { @@ -758,5 +679,3 @@ extern class C; // CHECK:STDOUT: .Self = file.%import_ref.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @.1; -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_import_misuses.carbon b/toolchain/check/testdata/class/fail_import_misuses.carbon index c4f2ffb30376..05af0acfcd9c 100644 --- a/toolchain/check/testdata/class/fail_import_misuses.carbon +++ b/toolchain/check/testdata/class/fail_import_misuses.carbon @@ -19,13 +19,13 @@ library "b" api; import library "a"; -// CHECK:STDERR: fail_b.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: fail_b.carbon:[[@LINE+10]]:1: ERROR: Only one library can declare `class Empty` without `extern`. // CHECK:STDERR: class Empty { // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_b.carbon:[[@LINE-5]]:1: In import. // CHECK:STDERR: import library "a"; // CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: a.carbon:4:1: Name is previously declared here. +// CHECK:STDERR: a.carbon:4:1: Previously declared here. // CHECK:STDERR: class Empty { // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: @@ -74,23 +74,22 @@ var a: Incomplete; // 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 [template] { -// CHECK:STDOUT: .Empty = %import_ref.1 // CHECK:STDOUT: .Incomplete = %import_ref.2 // CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Empty = %Empty.decl.loc16 // CHECK:STDOUT: .a = %a // CHECK:STDOUT: } // CHECK:STDOUT: %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%Empty] // CHECK:STDOUT: %import_ref.2: type = import_ref ir2, inst+5, loc_15 [template = constants.%Incomplete] // CHECK:STDOUT: %Core: = namespace [template] {} // CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+3, unloaded -// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.2] { -// CHECK:STDOUT: %Empty.decl: invalid = class_decl @Empty [template = constants.%Empty] {} +// CHECK:STDOUT: %Empty.decl.loc16: type = class_decl @Empty [template = constants.%Empty] { +// CHECK:STDOUT: %Empty.decl.1: invalid = class_decl @Empty [template = constants.%Empty] {} // CHECK:STDOUT: } // CHECK:STDOUT: %Incomplete.decl: invalid = class_decl @Incomplete [template = constants.%Incomplete] {} // CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, %import_ref.2 [template = constants.%Incomplete] @@ -100,12 +99,7 @@ var a: Incomplete; // CHECK:STDOUT: // CHECK:STDOUT: class @Empty { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = file.%import_ref.3 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @.1 { -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%.2 +// CHECK:STDOUT: .Self = constants.%Empty // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Incomplete; diff --git a/toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon b/toolchain/check/testdata/class/import_forward_decl.carbon similarity index 56% rename from toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon rename to toolchain/check/testdata/class/import_forward_decl.carbon index eed4a44a614f..a9816564764d 100644 --- a/toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon +++ b/toolchain/check/testdata/class/import_forward_decl.carbon @@ -10,22 +10,10 @@ library "a" api; class ForwardDecl; -// --- fail_b.carbon +// --- a.impl.carbon -library "b" api; +library "a" impl; -import library "a"; - -// TODO: This should probably have a valid form. -// CHECK:STDERR: fail_b.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: class ForwardDecl { -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_b.carbon:[[@LINE-6]]:1: In import. -// CHECK:STDERR: import library "a"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: a.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: class ForwardDecl; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ class ForwardDecl { } @@ -46,30 +34,27 @@ class ForwardDecl { // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardDecl; // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_b.carbon +// CHECK:STDOUT: --- a.impl.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: %.1: type = struct_type {} [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .ForwardDecl = %import_ref // CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .ForwardDecl = %ForwardDecl.decl.loc4 // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref: type = import_ref ir2, inst+2, loaded [template = constants.%ForwardDecl] +// CHECK:STDOUT: %import_ref: type = import_ref ir1, inst+2, loaded [template = constants.%ForwardDecl] // CHECK:STDOUT: %Core: = namespace [template] {} -// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.1] { -// CHECK:STDOUT: %ForwardDecl.decl: invalid = class_decl @ForwardDecl [template = constants.%ForwardDecl] {} +// CHECK:STDOUT: %ForwardDecl.decl.loc4: type = class_decl @ForwardDecl [template = constants.%ForwardDecl] { +// CHECK:STDOUT: %ForwardDecl.decl.1: invalid = class_decl @ForwardDecl [template = constants.%ForwardDecl] {} // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @ForwardDecl; -// CHECK:STDOUT: -// CHECK:STDOUT: class @.1 { +// CHECK:STDOUT: class @ForwardDecl { // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%.1 +// CHECK:STDOUT: .Self = constants.%ForwardDecl // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/handle_aggregates.cpp b/toolchain/lower/handle_aggregates.cpp index 14dcaf998747..32c11e52459c 100644 --- a/toolchain/lower/handle_aggregates.cpp +++ b/toolchain/lower/handle_aggregates.cpp @@ -12,6 +12,11 @@ namespace Carbon::Lower { +auto HandleExternDecl(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, + SemIR::ExternDecl /*inst*/) -> void { + // No action to perform. +} + auto HandleClassDecl(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, SemIR::ClassDecl /*inst*/) -> void { // No action to perform. diff --git a/toolchain/lower/handle_type.cpp b/toolchain/lower/handle_type.cpp index 0cdbc547e3f2..9ece0ff3883b 100644 --- a/toolchain/lower/handle_type.cpp +++ b/toolchain/lower/handle_type.cpp @@ -27,6 +27,11 @@ auto HandleConstType(FunctionContext& context, SemIR::InstId inst_id, context.SetLocal(inst_id, context.GetTypeAsValue()); } +auto HandleExternType(FunctionContext& context, SemIR::InstId inst_id, + SemIR::ExternType /*inst*/) -> void { + context.SetLocal(inst_id, context.GetTypeAsValue()); +} + auto HandleFacetTypeAccess(FunctionContext& context, SemIR::InstId inst_id, SemIR::FacetTypeAccess /*inst*/) -> void { context.SetLocal(inst_id, context.GetTypeAsValue()); diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index c57f3feda217..06b911917d7e 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -206,6 +206,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { case BindSymbolicName::Kind: case Builtin::Kind: case ClassType::Kind: + case ExternType::Kind: case FacetTypeAccess::Kind: case FloatType::Kind: case ImportRefLoaded::Kind: @@ -246,6 +247,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { case ClassInit::Kind: case Converted::Kind: case Deref::Kind: + case ExternDecl::Kind: case FieldDecl::Kind: case FloatLiteral::Kind: case FunctionDecl::Kind: @@ -379,6 +381,11 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, } break; } + case CARBON_KIND(ExternType inst): { + out << "extern "; + push_inst_id(sem_ir.types().GetInstId(inst.non_extern_type_id)); + break; + } case CARBON_KIND(FacetTypeAccess inst): { // Print `T as type` as simply `T`. push_inst_id(inst.facet_id); @@ -521,6 +528,7 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, case ClassInit::Kind: case Converted::Kind: case Deref::Kind: + case ExternDecl::Kind: case FieldDecl::Kind: case FloatLiteral::Kind: case FunctionDecl::Kind: @@ -640,6 +648,8 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case ClassDecl::Kind: case ClassType::Kind: case ConstType::Kind: + case ExternDecl::Kind: + case ExternType::Kind: case FacetTypeAccess::Kind: case FloatLiteral::Kind: case FloatType::Kind: diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 46a86f2de52b..22d5243f6cc2 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -48,6 +48,8 @@ CARBON_SEM_IR_INST_KIND(ClassType) CARBON_SEM_IR_INST_KIND(ConstType) CARBON_SEM_IR_INST_KIND(Converted) CARBON_SEM_IR_INST_KIND(Deref) +CARBON_SEM_IR_INST_KIND(ExternDecl) +CARBON_SEM_IR_INST_KIND(ExternType) CARBON_SEM_IR_INST_KIND(FacetTypeAccess) CARBON_SEM_IR_INST_KIND(FloatLiteral) CARBON_SEM_IR_INST_KIND(FloatType) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 563cc5f616fe..15a31df9f4a1 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -434,6 +434,22 @@ struct Deref { InstId pointer_id; }; +struct ExternDecl { + static constexpr auto Kind = + InstKind::ExternDecl.Define("extern_decl"); + + TypeId type_id; + InstId decl_id; +}; + +struct ExternType { + static constexpr auto Kind = + InstKind::ExternType.Define("extern_type"); + + TypeId type_id; + TypeId non_extern_type_id; +}; + // Represents accessing the `type` field in a facet value, which is notionally a // pair of a type and a witness. struct FacetTypeAccess {