Răsfoiți Sursa

Handle merging of conflicting 'export name' cross-package imports. (#3973)

Jon Ross-Perkins 1 an în urmă
părinte
comite
d4025dc6c4

+ 19 - 11
toolchain/check/context.cpp

@@ -23,6 +23,7 @@
 #include "toolchain/sem_ir/builtin_kind.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/ids.h"
+#include "toolchain/sem_ir/import_ir.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/inst_kind.h"
 #include "toolchain/sem_ir/typed_insts.h"
@@ -300,7 +301,8 @@ static auto LookupInImportIRScopes(Context& context, SemIRLoc loc,
       });
 
   auto result_id = SemIR::InstId::Invalid;
-  auto bind_name_id = SemIR::BindNameId::Invalid;
+  std::optional<SemIR::ImportIRInst> canonical_result_inst;
+
   for (auto [import_ir_id, import_scope_id] : scope.import_ir_scopes) {
     auto& import_ir = context.import_irs().Get(import_ir_id);
 
@@ -330,19 +332,25 @@ static auto LookupInImportIRScopes(Context& context, SemIRLoc loc,
       continue;
     }
 
-    if (!bind_name_id.is_valid()) {
-      bind_name_id = context.bind_names().Add(
+    if (result_id.is_valid()) {
+      // On a conflict, we verify the canonical instruction is the same.
+      if (!canonical_result_inst) {
+        canonical_result_inst =
+            GetCanonicalImportIRInst(context, &context.sem_ir(), result_id);
+      }
+      VerifySameCanonicalImportIRInst(context, result_id,
+                                      *canonical_result_inst, import_ir_id,
+                                      import_ir.sem_ir, it->second);
+    } else {
+      // Add the first result found.
+      auto bind_name_id = context.bind_names().Add(
           {.name_id = name_id,
            .enclosing_scope_id = scope_id,
            .bind_index = SemIR::CompileTimeBindIndex::Invalid});
-    }
-    auto import_inst_id = AddImportRef(
-        context, {.ir_id = import_ir_id, .inst_id = it->second}, bind_name_id);
-    if (result_id.is_valid()) {
-      context.DiagnoseDuplicateName(import_inst_id, result_id);
-    } else {
-      LoadImportRef(context, import_inst_id);
-      result_id = import_inst_id;
+      result_id =
+          AddImportRef(context, {.ir_id = import_ir_id, .inst_id = it->second},
+                       bind_name_id);
+      LoadImportRef(context, result_id);
     }
   }
 

+ 2 - 48
toolchain/check/import.cpp

@@ -217,43 +217,6 @@ static auto CopyEnclosingNameScopesFromImportIR(
   return scope_cursor;
 }
 
-// Returns the canonical IR inst for an entity. Returns an invalid ir_id for the
-// current IR.
-static auto GetCanonicalImportIRInst(Context& context,
-                                     const SemIR::File* cursor_ir,
-                                     SemIR::InstId cursor_inst_id)
-    -> SemIR::ImportIRInst {
-  while (true) {
-    auto inst = cursor_ir->insts().Get(cursor_inst_id);
-    CARBON_KIND_SWITCH(inst) {
-      case CARBON_KIND(SemIR::ExportDecl bind_export): {
-        cursor_inst_id = bind_export.value_id;
-        continue;
-      }
-      case SemIR::ImportRefLoaded::Kind:
-      case SemIR::ImportRefUnloaded::Kind: {
-        auto import_ref = inst.As<SemIR::AnyImportRef>();
-        auto import_ir_inst =
-            cursor_ir->import_ir_insts().Get(import_ref.import_ir_inst_id);
-        cursor_ir = cursor_ir->import_irs().Get(import_ir_inst.ir_id).sem_ir;
-        cursor_inst_id = import_ir_inst.inst_id;
-        continue;
-      }
-      default: {
-        auto ir_id = SemIR::ImportIRId::Invalid;
-        if (cursor_ir != &context.sem_ir()) {
-          // This uses AddImportIR in case it was indirectly found, which can
-          // happen with two or more steps of exports.
-          ir_id = AddImportIR(context, {.node_id = Parse::NodeId::Invalid,
-                                        .sem_ir = cursor_ir,
-                                        .is_export = false});
-        }
-        return {.ir_id = ir_id, .inst_id = cursor_inst_id};
-      }
-    }
-  }
-}
-
 // Adds an ImportRef for an entity, handling merging if needed.
 static auto AddImportRefOrMerge(Context& context, SemIR::ImportIRId ir_id,
                                 const SemIR::File& import_sem_ir,
@@ -275,17 +238,8 @@ static auto AddImportRefOrMerge(Context& context, SemIR::ImportIRId ir_id,
 
   auto prev_ir_inst =
       GetCanonicalImportIRInst(context, &context.sem_ir(), it->second);
-  auto new_ir_inst =
-      GetCanonicalImportIRInst(context, &import_sem_ir, import_inst_id);
-
-  // Diagnose if the imported instructions aren't equal. However, then we need
-  // to form an instruction for the duplicate diagnostic.
-  if (prev_ir_inst != new_ir_inst) {
-    auto conflict_id =
-        AddImportRef(context, {.ir_id = ir_id, .inst_id = import_inst_id},
-                     SemIR::BindNameId::Invalid);
-    context.DiagnoseDuplicateName(conflict_id, it->second);
-  }
+  VerifySameCanonicalImportIRInst(context, it->second, prev_ir_inst, ir_id,
+                                  &import_sem_ir, import_inst_id);
 }
 
 auto ImportLibrariesFromCurrentPackage(

+ 50 - 0
toolchain/check/import_ref.cpp

@@ -72,6 +72,56 @@ auto AddImportRef(Context& context, SemIR::ImportIRInst import_ir_inst,
   return import_ref_id;
 }
 
+auto GetCanonicalImportIRInst(Context& context, const SemIR::File* cursor_ir,
+                              SemIR::InstId cursor_inst_id)
+    -> SemIR::ImportIRInst {
+  while (true) {
+    auto inst = cursor_ir->insts().Get(cursor_inst_id);
+    CARBON_KIND_SWITCH(inst) {
+      case CARBON_KIND(SemIR::ExportDecl bind_export): {
+        cursor_inst_id = bind_export.value_id;
+        continue;
+      }
+      case SemIR::ImportRefLoaded::Kind:
+      case SemIR::ImportRefUnloaded::Kind: {
+        auto import_ref = inst.As<SemIR::AnyImportRef>();
+        auto import_ir_inst =
+            cursor_ir->import_ir_insts().Get(import_ref.import_ir_inst_id);
+        cursor_ir = cursor_ir->import_irs().Get(import_ir_inst.ir_id).sem_ir;
+        cursor_inst_id = import_ir_inst.inst_id;
+        continue;
+      }
+      default: {
+        auto ir_id = SemIR::ImportIRId::Invalid;
+        if (cursor_ir != &context.sem_ir()) {
+          // This uses AddImportIR in case it was indirectly found, which can
+          // happen with two or more steps of exports.
+          ir_id = AddImportIR(context, {.node_id = Parse::NodeId::Invalid,
+                                        .sem_ir = cursor_ir,
+                                        .is_export = false});
+        }
+        return {.ir_id = ir_id, .inst_id = cursor_inst_id};
+      }
+    }
+  }
+}
+
+auto VerifySameCanonicalImportIRInst(Context& context, SemIR::InstId prev_id,
+                                     SemIR::ImportIRInst prev_import_ir_inst,
+                                     SemIR::ImportIRId new_ir_id,
+                                     const SemIR::File* new_import_ir,
+                                     SemIR::InstId new_inst_id) -> void {
+  auto new_import_ir_inst =
+      GetCanonicalImportIRInst(context, new_import_ir, new_inst_id);
+  if (new_import_ir_inst == prev_import_ir_inst) {
+    return;
+  }
+  auto conflict_id =
+      AddImportRef(context, {.ir_id = new_ir_id, .inst_id = new_inst_id},
+                   SemIR::BindNameId::Invalid);
+  context.DiagnoseDuplicateName(conflict_id, prev_id);
+}
+
 // Resolves an instruction from an imported IR into a constant referring to the
 // current IR.
 //

+ 14 - 0
toolchain/check/import_ref.h

@@ -24,6 +24,20 @@ auto AddImportIR(Context& context, SemIR::ImportIR import_ir)
 auto AddImportRef(Context& context, SemIR::ImportIRInst import_ir_inst,
                   SemIR::BindNameId bind_name_id) -> SemIR::InstId;
 
+// Returns the canonical IR inst for an entity. Returns an invalid ir_id for the
+// current IR.
+auto GetCanonicalImportIRInst(Context& context, const SemIR::File* cursor_ir,
+                              SemIR::InstId cursor_inst_id)
+    -> SemIR::ImportIRInst;
+
+// Verifies a new instruction is the same as a previous instruction.
+// prev_import_ir_inst should come from GetCanonicalImportIRInst.
+auto VerifySameCanonicalImportIRInst(Context& context, SemIR::InstId prev_id,
+                                     SemIR::ImportIRInst prev_import_ir_inst,
+                                     SemIR::ImportIRId new_ir_id,
+                                     const SemIR::File* new_import_ir,
+                                     SemIR::InstId new_inst_id) -> void;
+
 // If the passed in instruction ID is an ImportRefUnloaded, turns it into an
 // ImportRefLoaded for use.
 auto LoadImportRef(Context& context, SemIR::InstId inst_id) -> void;

+ 168 - 97
toolchain/check/testdata/packages/no_prelude/cross_package_export.carbon

@@ -16,6 +16,12 @@ class C {
   var x: ();
 };
 
+// --- conflict.carbon
+
+package Other library "conflict";
+
+fn C() {}
+
 // --- export_import.carbon
 
 package Other library "export_import";
@@ -95,35 +101,13 @@ import Other library "export_name";
 
 var c: Other.C = {.x = ()};
 
-// --- fail_todo_use_export_name_with_copy.carbon
+// --- use_export_name_with_copy.carbon
 
 library "use_export_name_with_copy";
 
 import Other library "export_name";
-// CHECK:STDERR: fail_todo_use_export_name_with_copy.carbon:[[@LINE+18]]:1: In import.
-// CHECK:STDERR: import Other library "export_name_copy";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: export_name_copy.carbon:4:1: In import.
-// CHECK:STDERR: import library "base";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: base.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: class C {
-// CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: fail_todo_use_export_name_with_copy.carbon:[[@LINE-10]]:1: In import.
-// CHECK:STDERR: import Other library "export_name";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: export_name.carbon:4:1: In import.
-// CHECK:STDERR: import library "base";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: base.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: class C {
-// CHECK:STDERR: ^~~~~~~~~
 import Other library "export_name_copy";
 
-// CHECK:STDERR: fail_todo_use_export_name_with_copy.carbon:[[@LINE+4]]:8: In name lookup for `C`.
-// CHECK:STDERR: var c: Other.C = {.x = ()};
-// CHECK:STDERR:        ^~~~~~~
-// CHECK:STDERR:
 var c: Other.C = {.x = ()};
 
 // --- use_export_name_indirect.carbon
@@ -134,71 +118,70 @@ import Other library "export_name_indirect";
 
 var c: Other.C = {.x = ()};
 
-// --- fail_todo_use_export_all.carbon
+// --- use_export_all.carbon
 
 library "use_export_all";
 
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE+6]]:1: In import.
-// CHECK:STDERR: import Other library "export_import";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: base.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: class C {
-// CHECK:STDERR: ^~~~~~~~~
 import Other library "export_import";
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE+9]]:1: In import.
-// CHECK:STDERR: import Other library "export_name";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: export_name.carbon:4:1: In import.
-// CHECK:STDERR: import library "base";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: base.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: class C {
-// CHECK:STDERR: ^~~~~~~~~
 import Other library "export_name";
 import Other library "export_import_copy";
 import Other library "export_name_copy";
 import Other library "export_import_indirect";
 import Other library "export_name_indirect";
 
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE+50]]:8: In name lookup for `C`.
-// CHECK:STDERR: var c: Other.C = {.x = ()};
-// CHECK:STDERR:        ^~~~~~~
-// CHECK:STDERR:
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE-8]]:1: In import.
-// CHECK:STDERR: import Other library "export_name_copy";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: export_name_copy.carbon:4:1: In import.
-// CHECK:STDERR: import library "base";
+var c: Other.C = {.x = ()};
+
+// --- unused_conflict_on_export_import.carbon
+
+library "unused_conflict_on_export_import";
+
+import Other library "export_import";
+import Other library "conflict";
+
+// --- unused_conflict_on_export_name.carbon
+
+library "unused_conflict_on_export_name";
+
+import Other library "export_name";
+import Other library "conflict";
+
+// --- fail_conflict_on_export_import.carbon
+
+library "fail_conflict_on_export_import";
+
+// CHECK:STDERR: fail_conflict_on_export_import.carbon:[[@LINE+6]]:1: In import.
+// CHECK:STDERR: import Other library "export_import";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: base.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: class C {
 // CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE-19]]:1: In import.
-// CHECK:STDERR: import Other library "export_name";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: export_name.carbon:4:1: In import.
-// CHECK:STDERR: import library "base";
+import Other library "export_import";
+// CHECK:STDERR: fail_conflict_on_export_import.carbon:[[@LINE+6]]:1: In import.
+// CHECK:STDERR: import Other library "conflict";
 // CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: base.carbon:4:1: Name is previously declared here.
-// CHECK:STDERR: class C {
-// CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE+28]]:8: In name lookup for `C`.
-// CHECK:STDERR: var c: Other.C = {.x = ()};
-// CHECK:STDERR:        ^~~~~~~
+// CHECK:STDERR: conflict.carbon:4:1: Name is previously declared here.
+// CHECK:STDERR: fn C() {}
+// CHECK:STDERR: ^~~~~~~~
+import Other library "conflict";
+
+// CHECK:STDERR: fail_conflict_on_export_import.carbon:[[@LINE+4]]:11: In name lookup for `C`.
+// CHECK:STDERR: alias C = Other.C;
+// CHECK:STDERR:           ^~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE-28]]:1: In import.
-// CHECK:STDERR: import Other library "export_name_indirect";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: export_name_indirect.carbon:4:1: In import.
-// CHECK:STDERR: import library "export_name";
-// CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: export_name.carbon:4:1: In import.
-// CHECK:STDERR: import library "base";
+alias C = Other.C;
+
+// --- fail_conflict_on_export_name.carbon
+
+library "fail_conflict_on_export_name";
+
+import Other library "export_name";
+// CHECK:STDERR: fail_conflict_on_export_name.carbon:[[@LINE+15]]:1: In import.
+// CHECK:STDERR: import Other library "conflict";
 // CHECK:STDERR: ^~~~~~
-// CHECK:STDERR: base.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
-// CHECK:STDERR: class C {
-// CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE-44]]:1: In import.
+// CHECK:STDERR: conflict.carbon:4:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fn C() {}
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_conflict_on_export_name.carbon:[[@LINE-7]]:1: In import.
 // CHECK:STDERR: import Other library "export_name";
 // CHECK:STDERR: ^~~~~~
 // CHECK:STDERR: export_name.carbon:4:1: In import.
@@ -207,10 +190,12 @@ import Other library "export_name_indirect";
 // CHECK:STDERR: base.carbon:4:1: Name is previously declared here.
 // CHECK:STDERR: class C {
 // CHECK:STDERR: ^~~~~~~~~
-// CHECK:STDERR: fail_todo_use_export_all.carbon:[[@LINE+3]]:8: In name lookup for `C`.
-// CHECK:STDERR: var c: Other.C = {.x = ()};
-// CHECK:STDERR:        ^~~~~~~
-var c: Other.C = {.x = ()};
+import Other library "conflict";
+
+// CHECK:STDERR: fail_conflict_on_export_name.carbon:[[@LINE+3]]:11: In name lookup for `C`.
+// CHECK:STDERR: alias C = Other.C;
+// CHECK:STDERR:           ^~~~~~~
+alias C = Other.C;
 
 // CHECK:STDOUT: --- base.carbon
 // CHECK:STDOUT:
@@ -238,6 +223,26 @@ var c: Other.C = {.x = ()};
 // CHECK:STDOUT:   .x = %.loc5_8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- conflict.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = fn_type @C [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct: C = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: C = fn_decl @C [template = constants.%struct] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @C() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- export_import.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -517,7 +522,7 @@ var c: Other.C = {.x = ()};
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_use_export_name_with_copy.carbon
+// CHECK:STDOUT: --- use_export_name_with_copy.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
@@ -538,7 +543,6 @@ var c: Other.C = {.x = ()};
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+10, loaded [template = constants.%C]
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+8, unloaded
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+9, unloaded
-// CHECK:STDOUT:   %import_ref.4 = import_ref ir2, inst+10, unloaded
 // CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:   %c.var: ref C = var c
 // CHECK:STDOUT:   %c: ref C = bind_name c, %c.var
@@ -552,14 +556,14 @@ var c: Other.C = {.x = ()};
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc29_25.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc29_26.1: {.x: ()} = struct_literal (%.loc29_25.1)
-// CHECK:STDOUT:   %.loc29_26.2: ref () = class_element_access file.%c.var, element0
-// CHECK:STDOUT:   %.loc29_25.2: init () = tuple_init () to %.loc29_26.2 [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc29_26.3: init () = converted %.loc29_25.1, %.loc29_25.2 [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc29_26.4: init C = class_init (%.loc29_26.3), file.%c.var [template = constants.%struct]
-// CHECK:STDOUT:   %.loc29_27: init C = converted %.loc29_26.1, %.loc29_26.4 [template = constants.%struct]
-// CHECK:STDOUT:   assign file.%c.var, %.loc29_27
+// CHECK:STDOUT:   %.loc7_25.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc7_26.1: {.x: ()} = struct_literal (%.loc7_25.1)
+// CHECK:STDOUT:   %.loc7_26.2: ref () = class_element_access file.%c.var, element0
+// CHECK:STDOUT:   %.loc7_25.2: init () = tuple_init () to %.loc7_26.2 [template = constants.%tuple]
+// CHECK:STDOUT:   %.loc7_26.3: init () = converted %.loc7_25.1, %.loc7_25.2 [template = constants.%tuple]
+// CHECK:STDOUT:   %.loc7_26.4: init C = class_init (%.loc7_26.3), file.%c.var [template = constants.%struct]
+// CHECK:STDOUT:   %.loc7_27: init C = converted %.loc7_26.1, %.loc7_26.4 [template = constants.%struct]
+// CHECK:STDOUT:   assign file.%c.var, %.loc7_27
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -608,7 +612,7 @@ var c: Other.C = {.x = ()};
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_use_export_all.carbon
+// CHECK:STDOUT: --- use_export_all.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
@@ -629,9 +633,6 @@ var c: Other.C = {.x = ()};
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+10, loaded [template = constants.%C]
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir2, inst+8, unloaded
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir2, inst+9, unloaded
-// CHECK:STDOUT:   %import_ref.4 = import_ref ir4, inst+10, unloaded
-// CHECK:STDOUT:   %import_ref.5 = import_ref ir6, inst+10, unloaded
-// CHECK:STDOUT:   %import_ref.6 = import_ref ir7, inst+1, unloaded
 // CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:   %c.var: ref C = var c
 // CHECK:STDOUT:   %c: ref C = bind_name c, %c.var
@@ -645,14 +646,84 @@ var c: Other.C = {.x = ()};
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc76_25.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc76_26.1: {.x: ()} = struct_literal (%.loc76_25.1)
-// CHECK:STDOUT:   %.loc76_26.2: ref () = class_element_access file.%c.var, element0
-// CHECK:STDOUT:   %.loc76_25.2: init () = tuple_init () to %.loc76_26.2 [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc76_26.3: init () = converted %.loc76_25.1, %.loc76_25.2 [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc76_26.4: init C = class_init (%.loc76_26.3), file.%c.var [template = constants.%struct]
-// CHECK:STDOUT:   %.loc76_27: init C = converted %.loc76_26.1, %.loc76_26.4 [template = constants.%struct]
-// CHECK:STDOUT:   assign file.%c.var, %.loc76_27
+// CHECK:STDOUT:   %.loc11_25.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc11_26.1: {.x: ()} = struct_literal (%.loc11_25.1)
+// CHECK:STDOUT:   %.loc11_26.2: ref () = class_element_access file.%c.var, element0
+// CHECK:STDOUT:   %.loc11_25.2: init () = tuple_init () to %.loc11_26.2 [template = constants.%tuple]
+// CHECK:STDOUT:   %.loc11_26.3: init () = converted %.loc11_25.1, %.loc11_25.2 [template = constants.%tuple]
+// CHECK:STDOUT:   %.loc11_26.4: init C = class_init (%.loc11_26.3), file.%c.var [template = constants.%struct]
+// CHECK:STDOUT:   %.loc11_27: init C = converted %.loc11_26.1, %.loc11_26.4 [template = constants.%struct]
+// CHECK:STDOUT:   assign file.%c.var, %.loc11_27
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- unused_conflict_on_export_import.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Other = %Other
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Other: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- unused_conflict_on_export_name.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Other = %Other
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Other: <namespace> = namespace [template] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_conflict_on_export_import.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = fn_type @C [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct: C = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Other = %Other
+// CHECK:STDOUT:     .C = %C
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Other: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, %Other [template = %Other]
+// CHECK:STDOUT:   %import_ref.1: C = import_ref ir2, inst+1, loaded [template = constants.%struct]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir3, inst+1, unloaded
+// CHECK:STDOUT:   %C.ref: C = name_ref C, %import_ref.1 [template = constants.%struct]
+// CHECK:STDOUT:   %C: C = bind_alias C, %import_ref.1 [template = constants.%struct]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @C();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_conflict_on_export_name.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {.x: ()} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Other = %Other
+// CHECK:STDOUT:     .C = %C
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Other: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, %Other [template = %Other]
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+10, loaded [template = constants.%C]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+8, unloaded
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+9, unloaded
+// CHECK:STDOUT:   %import_ref.4 = import_ref ir2, inst+1, unloaded
+// CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C]
+// CHECK:STDOUT:   %C: type = bind_alias C, %import_ref.1 [template = constants.%C]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT:   .x = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT: