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

Fix use-after-free printing the name of an interface that might have been invalidated by lazy import. (#4509)

While here, also change the diagnostic emission to pass the interface
type rather than the interface name. This prepares us to include the
arguments in the diagnostic.
Richard Smith 1 жил өмнө
parent
commit
de9b7d282a

+ 6 - 0
toolchain/check/context.cpp

@@ -1299,6 +1299,12 @@ auto Context::GetGenericInterfaceType(SemIR::InterfaceId interface_id,
       *this, interface_id, enclosing_specific_id);
 }
 
+auto Context::GetInterfaceType(SemIR::InterfaceId interface_id,
+                               SemIR::SpecificId specific_id) -> SemIR::TypeId {
+  return GetTypeImpl<SemIR::FacetType>(
+      *this, FacetTypeFromInterface(interface_id, specific_id).facet_type_id);
+}
+
 auto Context::GetPointerType(SemIR::TypeId pointee_type_id) -> SemIR::TypeId {
   return GetTypeImpl<SemIR::PointerType>(*this, pointee_type_id);
 }

+ 4 - 0
toolchain/check/context.h

@@ -397,6 +397,10 @@ class Context {
                                SemIR::SpecificId enclosing_specific_id)
       -> SemIR::TypeId;
 
+  // Gets the facet type corresponding to a particular interface.
+  auto GetInterfaceType(SemIR::InterfaceId interface_id,
+                        SemIR::SpecificId specific_id) -> SemIR::TypeId;
+
   // Returns a pointer type whose pointee type is `pointee_type_id`.
   auto GetPointerType(SemIR::TypeId pointee_type_id) -> SemIR::TypeId;
 

+ 8 - 7
toolchain/check/member_access.cpp

@@ -193,28 +193,29 @@ static auto PerformImplLookup(
     return SemIR::InstId::BuiltinError;
   }
 
-  auto& interface = context.interfaces().Get(interface_type->interface_id);
   auto witness_id =
       LookupInterfaceWitness(context, loc_id, type_const_id,
                              assoc_type.interface_type_id.AsConstantId());
   if (!witness_id.is_valid()) {
+    auto interface_type_id = context.GetInterfaceType(
+        interface_type->interface_id, interface_type->specific_id);
     if (missing_impl_diagnoser) {
       // TODO: Pass in the expression whose type we are printing.
       CARBON_DIAGNOSTIC(MissingImplInMemberAccessNote, Note,
-                        "type {1} does not implement interface `{0}`",
-                        SemIR::NameId, SemIR::TypeId);
+                        "type {1} does not implement interface {0}",
+                        SemIR::TypeId, SemIR::TypeId);
       missing_impl_diagnoser()
-          .Note(loc_id, MissingImplInMemberAccessNote, interface.name_id,
+          .Note(loc_id, MissingImplInMemberAccessNote, interface_type_id,
                 context.GetTypeIdForTypeConstant(type_const_id))
           .Emit();
     } else {
       // TODO: Pass in the expression whose type we are printing.
       CARBON_DIAGNOSTIC(MissingImplInMemberAccess, Error,
-                        "cannot access member of interface `{0}` in type {1} "
+                        "cannot access member of interface {0} in type {1} "
                         "that does not implement that interface",
-                        SemIR::NameId, SemIR::TypeId);
+                        SemIR::TypeId, SemIR::TypeId);
       context.emitter().Emit(loc_id, MissingImplInMemberAccess,
-                             interface.name_id,
+                             interface_type_id,
                              context.GetTypeIdForTypeConstant(type_const_id));
     }
     return SemIR::InstId::BuiltinError;

+ 433 - 0
toolchain/check/testdata/impl/lookup/no_prelude/import.carbon

@@ -71,6 +71,40 @@ fn TestDG(d: PackageB.D) {
   d.(PackageB.HasG.G)();
 }
 
+// --- has_extra_interfaces.carbon
+
+package HasExtraInterfaces;
+
+interface Extra1 {}
+interface Extra2 {}
+interface Extra3 {}
+interface Extra4 {}
+interface Extra5 {}
+interface Extra6 {}
+interface Extra7 {}
+interface Extra8 {}
+
+class C(T:! type) {}
+interface I { fn F(); }
+
+impl C((Extra1, Extra2, Extra3, Extra4, Extra5, Extra6, Extra7, Extra8)) as I {
+  fn F() {}
+}
+
+// --- fail_use_has_extra_interfaces.carbon
+
+package UseHasExtraInterfaces;
+import HasExtraInterfaces;
+
+fn Test(c: HasExtraInterfaces.C(type)) {
+  // This triggers the import of a bunch more interfaces, which reallocates the
+  // interface ValueStore. Ensure that doesn't result in a use-after-free crash.
+  // CHECK:STDERR: fail_use_has_extra_interfaces.carbon:[[@LINE+3]]:3: error: cannot access member of interface `I` in type `C` that does not implement that interface [MissingImplInMemberAccess]
+  // CHECK:STDERR:   c.(HasExtraInterfaces.I.F)();
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~
+  c.(HasExtraInterfaces.I.F)();
+}
+
 // CHECK:STDOUT: --- package_a.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -544,3 +578,402 @@ fn TestDG(d: PackageB.D) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @G.1(constants.%Self.2) {}
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- has_extra_interfaces.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Extra1.type: type = facet_type <@Extra1> [template]
+// CHECK:STDOUT:   %Self.1: %Extra1.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra2.type: type = facet_type <@Extra2> [template]
+// CHECK:STDOUT:   %Self.2: %Extra2.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra3.type: type = facet_type <@Extra3> [template]
+// CHECK:STDOUT:   %Self.3: %Extra3.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra4.type: type = facet_type <@Extra4> [template]
+// CHECK:STDOUT:   %Self.4: %Extra4.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra5.type: type = facet_type <@Extra5> [template]
+// CHECK:STDOUT:   %Self.5: %Extra5.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra6.type: type = facet_type <@Extra6> [template]
+// CHECK:STDOUT:   %Self.6: %Extra6.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra7.type: type = facet_type <@Extra7> [template]
+// CHECK:STDOUT:   %Self.7: %Extra7.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra8.type: type = facet_type <@Extra8> [template]
+// CHECK:STDOUT:   %Self.8: %Extra8.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic]
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C, @C(%T) [symbolic]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: <witness> = complete_type_witness %.1 [template]
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [template]
+// CHECK:STDOUT:   %Self.9: %I.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %F.type.1: type = fn_type @F.1 [template]
+// CHECK:STDOUT:   %F.1: %F.type.1 = struct_value () [template]
+// CHECK:STDOUT:   %.3: type = assoc_entity_type %I.type, %F.type.1 [template]
+// CHECK:STDOUT:   %.4: %.3 = assoc_entity element0, @I.%F.decl [template]
+// CHECK:STDOUT:   %tuple.type.1: type = tuple_type (type, type, type, type, type, type, type, type) [template]
+// CHECK:STDOUT:   %tuple.type.2: type = tuple_type (%Extra1.type, %Extra2.type, %Extra3.type, %Extra4.type, %Extra5.type, %Extra6.type, %Extra7.type, %Extra8.type) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, @C(%tuple.type.2) [template]
+// CHECK:STDOUT:   %F.type.2: type = fn_type @F.2 [template]
+// CHECK:STDOUT:   %F.2: %F.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.5: <witness> = interface_witness (%F.2) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Extra1 = %Extra1.decl
+// CHECK:STDOUT:     .Extra2 = %Extra2.decl
+// CHECK:STDOUT:     .Extra3 = %Extra3.decl
+// CHECK:STDOUT:     .Extra4 = %Extra4.decl
+// CHECK:STDOUT:     .Extra5 = %Extra5.decl
+// CHECK:STDOUT:     .Extra6 = %Extra6.decl
+// CHECK:STDOUT:     .Extra7 = %Extra7.decl
+// CHECK:STDOUT:     .Extra8 = %Extra8.decl
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Extra1.decl: type = interface_decl @Extra1 [template = constants.%Extra1.type] {} {}
+// CHECK:STDOUT:   %Extra2.decl: type = interface_decl @Extra2 [template = constants.%Extra2.type] {} {}
+// CHECK:STDOUT:   %Extra3.decl: type = interface_decl @Extra3 [template = constants.%Extra3.type] {} {}
+// CHECK:STDOUT:   %Extra4.decl: type = interface_decl @Extra4 [template = constants.%Extra4.type] {} {}
+// CHECK:STDOUT:   %Extra5.decl: type = interface_decl @Extra5 [template = constants.%Extra5.type] {} {}
+// CHECK:STDOUT:   %Extra6.decl: type = interface_decl @Extra6 [template = constants.%Extra6.type] {} {}
+// CHECK:STDOUT:   %Extra7.decl: type = interface_decl @Extra7 [template = constants.%Extra7.type] {} {}
+// CHECK:STDOUT:   %Extra8.decl: type = interface_decl @Extra8 [template = constants.%Extra8.type] {} {}
+// CHECK:STDOUT:   %C.decl: %C.type = class_decl @C [template = constants.%C.1] {
+// CHECK:STDOUT:     %T.patt.loc13_9.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc13_9.2 (constants.%T.patt)]
+// CHECK:STDOUT:     %T.param_patt: type = value_param_pattern %T.patt.loc13_9.1, runtime_param<invalid> [symbolic = %T.patt.loc13_9.2 (constants.%T.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %T.param: type = value_param runtime_param<invalid>
+// CHECK:STDOUT:     %T.loc13_9.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc13_9.2 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %I.decl: type = interface_decl @I [template = constants.%I.type] {} {}
+// CHECK:STDOUT:   impl_decl @impl [template] {} {
+// CHECK:STDOUT:     %C.ref: %C.type = name_ref C, file.%C.decl [template = constants.%C.1]
+// CHECK:STDOUT:     %Extra1.ref: type = name_ref Extra1, file.%Extra1.decl [template = constants.%Extra1.type]
+// CHECK:STDOUT:     %Extra2.ref: type = name_ref Extra2, file.%Extra2.decl [template = constants.%Extra2.type]
+// CHECK:STDOUT:     %Extra3.ref: type = name_ref Extra3, file.%Extra3.decl [template = constants.%Extra3.type]
+// CHECK:STDOUT:     %Extra4.ref: type = name_ref Extra4, file.%Extra4.decl [template = constants.%Extra4.type]
+// CHECK:STDOUT:     %Extra5.ref: type = name_ref Extra5, file.%Extra5.decl [template = constants.%Extra5.type]
+// CHECK:STDOUT:     %Extra6.ref: type = name_ref Extra6, file.%Extra6.decl [template = constants.%Extra6.type]
+// CHECK:STDOUT:     %Extra7.ref: type = name_ref Extra7, file.%Extra7.decl [template = constants.%Extra7.type]
+// CHECK:STDOUT:     %Extra8.ref: type = name_ref Extra8, file.%Extra8.decl [template = constants.%Extra8.type]
+// CHECK:STDOUT:     %.loc16_71: %tuple.type.1 = tuple_literal (%Extra1.ref, %Extra2.ref, %Extra3.ref, %Extra4.ref, %Extra5.ref, %Extra6.ref, %Extra7.ref, %Extra8.ref)
+// CHECK:STDOUT:     %.loc16_7: type = converted %.loc16_71, constants.%tuple.type.2 [template = constants.%tuple.type.2]
+// CHECK:STDOUT:     %C: type = class_type @C, @C(constants.%tuple.type.2) [template = constants.%C.3]
+// CHECK:STDOUT:     %I.ref: type = name_ref I, file.%I.decl [template = constants.%I.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra1 {
+// CHECK:STDOUT:   %Self: %Extra1.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra2 {
+// CHECK:STDOUT:   %Self: %Extra2.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra3 {
+// CHECK:STDOUT:   %Self: %Extra3.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra4 {
+// CHECK:STDOUT:   %Self: %Extra4.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra5 {
+// CHECK:STDOUT:   %Self: %Extra5.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.5]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra6 {
+// CHECK:STDOUT:   %Self: %Extra6.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra7 {
+// CHECK:STDOUT:   %Self: %Extra7.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.7]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra8 {
+// CHECK:STDOUT:   %Self: %Extra8.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.8]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @I {
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.9]
+// CHECK:STDOUT:   %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {} {}
+// CHECK:STDOUT:   %.loc14: %.3 = assoc_entity element0, %F.decl [template = constants.%.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .F = %.loc14
+// CHECK:STDOUT:   witness = (%F.decl)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: %C as %I.ref {
+// CHECK:STDOUT:   %F.decl: %F.type.2 = fn_decl @F.2 [template = constants.%F.2] {} {}
+// CHECK:STDOUT:   %.loc16_79: <witness> = interface_witness (%F.decl) [template = constants.%.5]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F.decl
+// CHECK:STDOUT:   witness = %.loc16_79
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @C(%T.loc13_9.1: type) {
+// CHECK:STDOUT:   %T.loc13_9.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc13_9.2 (constants.%T)]
+// CHECK:STDOUT:   %T.patt.loc13_9.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc13_9.2 (constants.%T.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %.loc13: <witness> = complete_type_witness %.1 [template = constants.%.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%C.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @F.1(@I.%Self: %I.type) {
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn();
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%T) {
+// CHECK:STDOUT:   %T.loc13_9.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc13_9.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F.1(constants.%Self.9) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%tuple.type.2) {
+// CHECK:STDOUT:   %T.loc13_9.2 => constants.%tuple.type.2
+// CHECK:STDOUT:   %T.patt.loc13_9.2 => constants.%tuple.type.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F.1(constants.%C.3) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_use_has_extra_interfaces.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: <witness> = complete_type_witness %.1 [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %C.2: type = class_type @C, @C(%T) [symbolic]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic]
+// CHECK:STDOUT:   %C.3: type = class_type @C, @C(type) [template]
+// CHECK:STDOUT:   %Test.type: type = fn_type @Test [template]
+// CHECK:STDOUT:   %Test: %Test.type = struct_value () [template]
+// CHECK:STDOUT:   %.3: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [template]
+// CHECK:STDOUT:   %Self.1: %I.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = assoc_entity_type %I.type, %F.type [template]
+// CHECK:STDOUT:   %.5: %.4 = assoc_entity element0, imports.%import_ref.7 [template]
+// CHECK:STDOUT:   %Extra8.type: type = facet_type <@Extra8> [template]
+// CHECK:STDOUT:   %Self.2: %Extra8.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra7.type: type = facet_type <@Extra7> [template]
+// CHECK:STDOUT:   %Self.3: %Extra7.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra6.type: type = facet_type <@Extra6> [template]
+// CHECK:STDOUT:   %Self.4: %Extra6.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra5.type: type = facet_type <@Extra5> [template]
+// CHECK:STDOUT:   %Self.5: %Extra5.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra4.type: type = facet_type <@Extra4> [template]
+// CHECK:STDOUT:   %Self.6: %Extra4.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra3.type: type = facet_type <@Extra3> [template]
+// CHECK:STDOUT:   %Self.7: %Extra3.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra2.type: type = facet_type <@Extra2> [template]
+// CHECK:STDOUT:   %Self.8: %Extra2.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Extra1.type: type = facet_type <@Extra1> [template]
+// CHECK:STDOUT:   %Self.9: %Extra1.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %tuple.type: type = tuple_type (%Extra1.type, %Extra2.type, %Extra3.type, %Extra4.type, %Extra5.type, %Extra6.type, %Extra7.type, %Extra8.type) [template]
+// CHECK:STDOUT:   %C.4: type = class_type @C, @C(%tuple.type) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %HasExtraInterfaces: <namespace> = namespace file.%HasExtraInterfaces.import, [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .I = %import_ref.3
+// CHECK:STDOUT:     import HasExtraInterfaces//default
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: %C.type = import_ref HasExtraInterfaces//default, inst+39, loaded [template = constants.%C.1]
+// CHECK:STDOUT:   %import_ref.2 = import_ref HasExtraInterfaces//default, inst+45, unloaded
+// CHECK:STDOUT:   %import_ref.3: type = import_ref HasExtraInterfaces//default, inst+49, loaded [template = constants.%I.type]
+// CHECK:STDOUT:   %import_ref.4 = import_ref HasExtraInterfaces//default, inst+51, unloaded
+// CHECK:STDOUT:   %import_ref.5: %.4 = import_ref HasExtraInterfaces//default, inst+57, loaded [template = constants.%.5]
+// CHECK:STDOUT:   %import_ref.6 = import_ref HasExtraInterfaces//default, inst+53, unloaded
+// CHECK:STDOUT:   %import_ref.7 = import_ref HasExtraInterfaces//default, inst+53, unloaded
+// CHECK:STDOUT:   %import_ref.8 = import_ref HasExtraInterfaces//default, inst+31, unloaded
+// CHECK:STDOUT:   %import_ref.9 = import_ref HasExtraInterfaces//default, inst+27, unloaded
+// CHECK:STDOUT:   %import_ref.10 = import_ref HasExtraInterfaces//default, inst+23, unloaded
+// CHECK:STDOUT:   %import_ref.11 = import_ref HasExtraInterfaces//default, inst+19, unloaded
+// CHECK:STDOUT:   %import_ref.12 = import_ref HasExtraInterfaces//default, inst+15, unloaded
+// CHECK:STDOUT:   %import_ref.13 = import_ref HasExtraInterfaces//default, inst+11, unloaded
+// CHECK:STDOUT:   %import_ref.14 = import_ref HasExtraInterfaces//default, inst+7, unloaded
+// CHECK:STDOUT:   %import_ref.15 = import_ref HasExtraInterfaces//default, inst+3, unloaded
+// CHECK:STDOUT:   %import_ref.16: type = import_ref HasExtraInterfaces//default, inst+72, loaded [template = constants.%C.4]
+// CHECK:STDOUT:   %import_ref.17: type = import_ref HasExtraInterfaces//default, inst+74, loaded [template = constants.%I.type]
+// CHECK:STDOUT:   %import_ref.18 = import_ref HasExtraInterfaces//default, inst+79, unloaded
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .HasExtraInterfaces = imports.%HasExtraInterfaces
+// CHECK:STDOUT:     .Test = %Test.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %HasExtraInterfaces.import = import HasExtraInterfaces
+// CHECK:STDOUT:   %Test.decl: %Test.type = fn_decl @Test [template = constants.%Test] {
+// CHECK:STDOUT:     %c.patt: %C.3 = binding_pattern c
+// CHECK:STDOUT:     %c.param_patt: %C.3 = value_param_pattern %c.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %HasExtraInterfaces.ref.loc5: <namespace> = name_ref HasExtraInterfaces, imports.%HasExtraInterfaces [template = imports.%HasExtraInterfaces]
+// CHECK:STDOUT:     %C.ref: %C.type = name_ref C, imports.%import_ref.1 [template = constants.%C.1]
+// CHECK:STDOUT:     %C: type = class_type @C, @C(type) [template = constants.%C.3]
+// CHECK:STDOUT:     %c.param: %C.3 = value_param runtime_param0
+// CHECK:STDOUT:     %c: %C.3 = bind_name c, %c.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @I {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.4
+// CHECK:STDOUT:   .F = imports.%import_ref.5
+// CHECK:STDOUT:   witness = (imports.%import_ref.6)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra8 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.8
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra7 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.9
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra6 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.10
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra5 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.11
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra4 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.12
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra3 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.13
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra2 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.14
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Extra1 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%import_ref.15
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: imports.%import_ref.16 as imports.%import_ref.17 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = imports.%import_ref.18
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @C(constants.%T: type) {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt (constants.%T.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%import_ref.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Test(%c.param_patt: %C.3) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %c.ref: %C.3 = name_ref c, %c
+// CHECK:STDOUT:   %HasExtraInterfaces.ref.loc11: <namespace> = name_ref HasExtraInterfaces, imports.%HasExtraInterfaces [template = imports.%HasExtraInterfaces]
+// CHECK:STDOUT:   %I.ref: type = name_ref I, imports.%import_ref.3 [template = constants.%I.type]
+// CHECK:STDOUT:   %F.ref: %.4 = name_ref F, imports.%import_ref.5 [template = constants.%.5]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @F(constants.%Self.1: %I.type) {
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn();
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %T.patt => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(type) {
+// CHECK:STDOUT:   %T => type
+// CHECK:STDOUT:   %T.patt => type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(constants.%Self.1) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%tuple.type) {
+// CHECK:STDOUT:   %T => constants.%tuple.type
+// CHECK:STDOUT:   %T.patt => constants.%tuple.type
+// CHECK:STDOUT: }
+// CHECK:STDOUT: