Эх сурвалжийг харах

Fix handling for indirectly imported entities. (#3977)

A member of an imported entity can be an ImportRefUnloaded in an
imported SemIR. To address this, find a loaded version of it for use.
Jon Ross-Perkins 1 жил өмнө
parent
commit
7b4b7abc6e

+ 51 - 8
toolchain/check/import_ref.cpp

@@ -12,6 +12,7 @@
 #include "toolchain/sem_ir/constant.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"
@@ -1265,20 +1266,58 @@ class ImportRefResolver {
   llvm::SmallVector<Work> work_stack_;
 };
 
+// Returns a list of ImportIRInsts equivalent to the ImportRef currently being
+// loaded (including the one pointed at directly by the ImportRef), and the
+// final instruction's type ID.
+//
+// This addresses cases where an ImportRefUnloaded may point at another
+// ImportRefUnloaded. The ImportRefResolver requires a SemIR with a
+// constant-evaluated version of the instruction to work with.
+static auto GetInstForLoad(Context& context,
+                           SemIR::ImportIRInstId import_ir_inst_id)
+    -> std::pair<llvm::SmallVector<SemIR::ImportIRInst>, SemIR::TypeId> {
+  std::pair<llvm::SmallVector<SemIR::ImportIRInst>, SemIR::TypeId> result = {
+      {}, SemIR::TypeId::Invalid};
+  auto& [import_ir_insts, type_id] = result;
+
+  auto import_ir_inst = context.import_ir_insts().Get(import_ir_inst_id);
+  // The first ImportIRInst is added directly because the IR doesn't need to be
+  // localized.
+  import_ir_insts.push_back(import_ir_inst);
+  const auto* cursor_ir = context.import_irs().Get(import_ir_inst.ir_id).sem_ir;
+
+  while (true) {
+    auto cursor_inst = cursor_ir->insts().Get(import_ir_inst.inst_id);
+
+    auto import_ref = cursor_inst.TryAs<SemIR::ImportRefUnloaded>();
+    if (!import_ref) {
+      type_id = cursor_inst.type_id();
+      return result;
+    }
+
+    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;
+    import_ir_insts.push_back({.ir_id = context.GetImportIRId(*cursor_ir),
+                               .inst_id = import_ir_inst.inst_id});
+  }
+}
+
 auto LoadImportRef(Context& context, SemIR::InstId inst_id) -> void {
   auto inst = context.insts().TryGetAs<SemIR::ImportRefUnloaded>(inst_id);
   if (!inst) {
     return;
   }
-  auto import_ir_inst = context.import_ir_insts().Get(inst->import_ir_inst_id);
 
-  const SemIR::File& import_ir =
-      *context.import_irs().Get(import_ir_inst.ir_id).sem_ir;
-  auto import_inst = import_ir.insts().Get(import_ir_inst.inst_id);
+  auto [indirect_insts, load_type_id] =
+      GetInstForLoad(context, inst->import_ir_inst_id);
 
-  ImportRefResolver resolver(context, import_ir_inst.ir_id);
-  auto type_id = resolver.ResolveType(import_inst.type_id());
-  auto constant_id = resolver.Resolve(import_ir_inst.inst_id);
+  // The last indirect instruction is the one to resolve. Pop it here because
+  // Resolve will assign the constant.
+  auto load_ir_inst = indirect_insts.pop_back_val();
+  ImportRefResolver resolver(context, load_ir_inst.ir_id);
+  auto type_id = resolver.ResolveType(load_type_id);
+  auto constant_id = resolver.Resolve(load_ir_inst.inst_id);
 
   // Replace the ImportRefUnloaded instruction with ImportRefLoaded. This
   // doesn't use ReplaceInstBeforeConstantUse because it would trigger
@@ -1289,8 +1328,12 @@ auto LoadImportRef(Context& context, SemIR::InstId inst_id) -> void {
                              .import_ir_inst_id = inst->import_ir_inst_id,
                              .bind_name_id = inst->bind_name_id});
 
-  // Store the constant for both the ImportRefLoaded and imported instruction.
+  // Store the constant for both the ImportRefLoaded and indirect instructions.
   context.constant_values().Set(inst_id, constant_id);
+  for (const auto& import_ir_inst : indirect_insts) {
+    context.import_ir_constant_values()[import_ir_inst.ir_id.index].Set(
+        import_ir_inst.inst_id, constant_id);
+  }
 }
 
 // Imports the impl `import_impl_id` from the imported IR `import_ir`.

+ 437 - 0
toolchain/check/testdata/class/no_prelude/indirect_import_member.carbon

@@ -0,0 +1,437 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+
+// ============================================================================
+// Setup files
+// ============================================================================
+
+// --- a.carbon
+
+library "a";
+
+class C {
+  fn F() {}
+}
+
+// --- b.carbon
+
+library "b";
+
+export import library "a";
+
+// --- c.carbon
+
+library "c";
+
+import library "b";
+
+export C;
+
+// --- d.carbon
+
+library "d";
+
+export import library "c";
+
+// --- e.carbon
+
+library "e";
+
+import library "c";
+
+class D {
+  alias C = C;
+}
+
+// --- f.carbon
+
+library "f";
+
+export import library "e";
+
+// ============================================================================
+// Test files
+// ============================================================================
+
+// --- use_b.carbon
+
+library "use_b";
+
+import library "b";
+
+var x: () = C.F();
+
+// --- use_c.carbon
+
+library "use_c";
+
+import library "c";
+
+var x: () = C.F();
+
+// --- use_d.carbon
+
+library "use_d";
+
+import library "d";
+
+var x: () = C.F();
+
+// --- use_e.carbon
+
+library "use_e";
+
+import library "e";
+
+var x: () = D.C.F();
+
+// --- use_f.carbon
+
+library "use_f";
+
+import library "f";
+
+var x: () = D.C.F();
+
+// CHECK:STDOUT: --- a.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %F: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct: F = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [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: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %F.decl: F = fn_decl @F [template = constants.%struct] {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT:   .F = %F.decl
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- b.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref = import_ref ir1, inst+1, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- c.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %C
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir2, inst+3, unloaded
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir2, inst+2, unloaded
+// CHECK:STDOUT:   %C: type = export C, %import_ref.1 [template = constants.%C]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = file.%import_ref.2
+// CHECK:STDOUT:   .Self = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- d.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref = import_ref ir1, inst+7, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- e.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: 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:     .D = %D.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+7, loaded [template = constants.%C]
+// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+6, unloaded
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+5, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D {
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%import_ref.1 [template = constants.%C]
+// CHECK:STDOUT:   %C: type = bind_alias C, file.%import_ref.1 [template = constants.%C]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%D
+// CHECK:STDOUT:   .C = %C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT:   .F = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- f.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .D = %import_ref
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref = import_ref ir1, inst+2, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- use_b.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %F: type = fn_type @F [template]
+// CHECK:STDOUT:   %struct: F = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .x = %x
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+1, loaded [template = constants.%C]
+// CHECK:STDOUT:   %.loc6_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %x.var: ref () = var x
+// CHECK:STDOUT:   %x: ref () = bind_name x, %x.var
+// CHECK:STDOUT:   %import_ref.2: F = import_ref ir2, inst+3, loaded [template = constants.%struct]
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir2, inst+2, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = file.%import_ref.2
+// CHECK:STDOUT:   .Self = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%import_ref.1 [template = constants.%C]
+// CHECK:STDOUT:   %F.ref: F = name_ref F, file.%import_ref.2 [template = constants.%struct]
+// CHECK:STDOUT:   %F.call: init () = call %F.ref()
+// CHECK:STDOUT:   assign file.%x.var, %F.call
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- use_c.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %F: type = fn_type @F [template]
+// CHECK:STDOUT:   %struct: F = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .x = %x
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+7, loaded [template = constants.%C]
+// CHECK:STDOUT:   %.loc6_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %x.var: ref () = var x
+// CHECK:STDOUT:   %x: ref () = bind_name x, %x.var
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+6, unloaded
+// CHECK:STDOUT:   %import_ref.3: F = import_ref ir1, inst+5, loaded [template = constants.%struct]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT:   .F = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%import_ref.1 [template = constants.%C]
+// CHECK:STDOUT:   %F.ref: F = name_ref F, file.%import_ref.3 [template = constants.%struct]
+// CHECK:STDOUT:   %F.call: init () = call %F.ref()
+// CHECK:STDOUT:   assign file.%x.var, %F.call
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- use_d.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %F: type = fn_type @F [template]
+// CHECK:STDOUT:   %struct: F = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .x = %x
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+7, loaded [template = constants.%C]
+// CHECK:STDOUT:   %.loc6_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %x.var: ref () = var x
+// CHECK:STDOUT:   %x: ref () = bind_name x, %x.var
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir2, inst+6, unloaded
+// CHECK:STDOUT:   %import_ref.3: F = import_ref ir2, inst+5, loaded [template = constants.%struct]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT:   .F = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%import_ref.1 [template = constants.%C]
+// CHECK:STDOUT:   %F.ref: F = name_ref F, file.%import_ref.3 [template = constants.%struct]
+// CHECK:STDOUT:   %F.call: init () = call %F.ref()
+// CHECK:STDOUT:   assign file.%x.var, %F.call
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- use_e.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %F: type = fn_type @F [template]
+// CHECK:STDOUT:   %struct: F = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .D = %import_ref.1
+// CHECK:STDOUT:     .x = %x
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+2, loaded [template = constants.%D]
+// CHECK:STDOUT:   %.loc6_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %x.var: ref () = var x
+// CHECK:STDOUT:   %x: ref () = bind_name x, %x.var
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unloaded
+// CHECK:STDOUT:   %import_ref.3: type = import_ref ir1, inst+10, loaded [template = constants.%C]
+// CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+7, unloaded
+// CHECK:STDOUT:   %import_ref.5: F = import_ref ir1, inst+8, loaded [template = constants.%struct]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT:   .C = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.4
+// CHECK:STDOUT:   .F = file.%import_ref.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %D.ref: type = name_ref D, file.%import_ref.1 [template = constants.%D]
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%import_ref.3 [template = constants.%C]
+// CHECK:STDOUT:   %F.ref: F = name_ref F, file.%import_ref.5 [template = constants.%struct]
+// CHECK:STDOUT:   %F.call: init () = call %F.ref()
+// CHECK:STDOUT:   assign file.%x.var, %F.call
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- use_f.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %F: type = fn_type @F [template]
+// CHECK:STDOUT:   %struct: F = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .D = %import_ref.1
+// CHECK:STDOUT:     .x = %x
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%D]
+// CHECK:STDOUT:   %.loc6_9.1: () = tuple_literal ()
+// CHECK:STDOUT:   %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1]
+// CHECK:STDOUT:   %x.var: ref () = var x
+// CHECK:STDOUT:   %x: ref () = bind_name x, %x.var
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir2, inst+3, unloaded
+// CHECK:STDOUT:   %import_ref.3: type = import_ref ir2, inst+10, loaded [template = constants.%C]
+// CHECK:STDOUT:   %import_ref.4 = import_ref ir2, inst+7, unloaded
+// CHECK:STDOUT:   %import_ref.5: F = import_ref ir2, inst+8, loaded [template = constants.%struct]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT:   .C = file.%import_ref.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.4
+// CHECK:STDOUT:   .F = file.%import_ref.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %D.ref: type = name_ref D, file.%import_ref.1 [template = constants.%D]
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%import_ref.3 [template = constants.%C]
+// CHECK:STDOUT:   %F.ref: F = name_ref F, file.%import_ref.5 [template = constants.%struct]
+// CHECK:STDOUT:   %F.call: init () = call %F.ref()
+// CHECK:STDOUT:   assign file.%x.var, %F.call
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT: