Просмотр исходного кода

Add ExternDecl and ExternType for extern classes. (#3893)

This expands `extern` handling for most cases.
Jon Ross-Perkins 2 лет назад
Родитель
Сommit
5694dd152e

+ 8 - 1
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;
 }
 

+ 2 - 0
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:

+ 10 - 0
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.";

+ 62 - 22
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<SemIR::ClassId, SemIR::InstId> {
@@ -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<SemIR::ClassDecl>()) {
-      // 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};
 }
 

+ 2 - 0
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(),

+ 5 - 0
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);

+ 16 - 0
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();

+ 49 - 27
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<SemIR::AnyImportRef>();
-  // 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};
 
-  // If the import ref was previously used, print a diagnostic.
-  if (auto import_ref_used = prev_inst.TryAs<SemIR::ImportRefUsed>()) {
-    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();
+  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<SemIR::AnyImportRef>();
+      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<SemIR::ExternType>()) {
+        result.inst =
+            context.types().GetAsInst(extern_type->non_extern_type_id);
+      }
+      break;
+    }
+    default:
+      break;
   }
 
-  // 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<SemIR::AnyImportRef>().import_ir_inst_id}};
+
+  InstForMerge result = {
+      .inst = context.insts().Get(const_id.inst_id()),
+      .import_ir_inst_id = inst.As<SemIR::AnyImportRef>().import_ir_inst_id,
+      .is_extern = false};
+
+  if (auto extern_type = result.inst.TryAs<SemIR::ExternType>()) {
+    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:

+ 2 - 0
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

+ 23 - 41
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> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .C = %.loc4
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = 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> = namespace [template] {}
 // CHECK:STDOUT:   %Other: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Other.ref: <namespace> = 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 <error> = var c
-// CHECK:STDOUT:   %c: ref <error> = 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, <error>
 // 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:

+ 98 - 179
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> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .C = %.loc4
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = 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> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .C = %.loc4
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = 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> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .C = %.loc4
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = 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: <function> = 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> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:     .C = %.loc4
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = 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> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:     .C = %C.decl.loc12
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = 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> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl.loc4
+// CHECK:STDOUT:     .C = %C.decl.loc12
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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> = 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:

+ 6 - 12
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> = 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> = 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;

+ 10 - 25
toolchain/check/testdata/class/fail_todo_import_forward_decl.carbon → 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> = 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> = 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:

+ 5 - 0
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.

+ 5 - 0
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());

+ 10 - 0
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:

+ 2 - 0
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)

+ 16 - 0
toolchain/sem_ir/typed_insts.h

@@ -434,6 +434,22 @@ struct Deref {
   InstId pointer_id;
 };
 
+struct ExternDecl {
+  static constexpr auto Kind =
+      InstKind::ExternDecl.Define<Parse::NodeId>("extern_decl");
+
+  TypeId type_id;
+  InstId decl_id;
+};
+
+struct ExternType {
+  static constexpr auto Kind =
+      InstKind::ExternType.Define<Parse::NodeId>("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 {