Bläddra i källkod

Error on non-constant parameters to a type. (#4215)

At present, this is a crash bug. I don't know whether this is the best
fix, but I figure it'll work until zygoloid has a chance to look.
Jon Ross-Perkins 1 år sedan
förälder
incheckning
a3a4c14960

+ 25 - 0
toolchain/check/generic.cpp

@@ -415,4 +415,29 @@ auto ResolveSpecificDefinition(Context& context, SemIR::SpecificId specific_id)
   return true;
 }
 
+auto RequireGenericParams(Context& context, SemIR::InstBlockId block_id)
+    -> void {
+  if (!block_id.is_valid() || block_id == SemIR::InstBlockId::Empty) {
+    return;
+  }
+
+  for (auto& inst_id : context.inst_blocks().Get(block_id)) {
+    if (!context.constant_values().Get(inst_id).is_constant()) {
+      CARBON_DIAGNOSTIC(GenericParamMustBeConstant, Error,
+                        "Parameters of generic types must be constant.");
+      context.emitter().Emit(inst_id, GenericParamMustBeConstant);
+
+      // Replace the parameter with an invalid instruction so that we don't try
+      // constructing a generic based on it. Note this is updating the param
+      // refs block, not the actual params block, so will not be directly
+      // reflected in SemIR output.
+      inst_id = context.AddInstInNoBlock(
+          SemIR::LocIdAndInst::ReusingLoc<SemIR::Param>(
+              context.insts().GetLocId(inst_id),
+              {.type_id = SemIR::TypeId::Error,
+               .name_id = SemIR::NameId::Base}));
+    }
+  }
+}
+
 }  // namespace Carbon::Check

+ 7 - 0
toolchain/check/generic.h

@@ -68,6 +68,13 @@ auto MakeSelfSpecific(Context& context, SemIR::GenericId generic_id)
 auto ResolveSpecificDefinition(Context& context, SemIR::SpecificId specific_id)
     -> bool;
 
+// Requires that a param block only contains generics. Diagnoses and updates the
+// block otherwise. This will typically be called once for each of implicit and
+// explicit parameters, and must occur before constant evaluation of the
+// parameterized instruction.
+auto RequireGenericParams(Context& context, SemIR::InstBlockId block_id)
+    -> void;
+
 }  // namespace Carbon::Check
 
 #endif  // CARBON_TOOLCHAIN_CHECK_GENERIC_H_

+ 3 - 0
toolchain/check/handle_class.cpp

@@ -224,6 +224,9 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
        .self_type_id = SemIR::TypeId::Invalid,
        .inheritance_kind = inheritance_kind}};
 
+  RequireGenericParams(context, class_info.implicit_param_refs_id);
+  RequireGenericParams(context, class_info.param_refs_id);
+
   MergeOrAddName(context, node_id, name_context, class_decl_id, class_decl,
                  class_info, is_definition, is_extern,
                  introducer.modifier_set.GetAccessKind());

+ 5 - 2
toolchain/check/handle_interface.cpp

@@ -53,6 +53,11 @@ static auto BuildInterfaceDecl(Context& context,
   auto interface_decl_id =
       context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, interface_decl));
 
+  SemIR::Interface interface_info = {name_context.MakeEntityWithParamsBase(
+      name, interface_decl_id, /*is_extern=*/false)};
+  RequireGenericParams(context, interface_info.implicit_param_refs_id);
+  RequireGenericParams(context, interface_info.param_refs_id);
+
   // Check whether this is a redeclaration.
   auto existing_id = context.decl_name_stack().LookupOrAddName(
       name_context, interface_decl_id, introducer.modifier_set.GetAccessKind());
@@ -103,8 +108,6 @@ static auto BuildInterfaceDecl(Context& context,
     // there was an error in the qualifier, we will have lost track of the
     // interface name here. We should keep track of it even if the name is
     // invalid.
-    SemIR::Interface interface_info = {name_context.MakeEntityWithParamsBase(
-        name, interface_decl_id, /*is_extern=*/false)};
     interface_info.generic_id = FinishGenericDecl(context, interface_decl_id);
     interface_decl.interface_id = context.interfaces().Add(interface_info);
     if (interface_info.has_parameters()) {

+ 154 - 50
toolchain/check/testdata/class/no_prelude/generic_vs_params.carbon

@@ -8,6 +8,10 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/generic_vs_params.carbon
 
+// --- params.carbon
+
+library "params";
+
 class NotGenericNoParams {}
 class NotGenericButParams() {}
 class GenericAndParams(T:! type) {}
@@ -25,7 +29,31 @@ var c: GenericAndParams(X) = {};
 var d: C(X).GenericNoParams = {};
 var e: C(X).GenericAndParams(X) = {};
 
-// CHECK:STDOUT: --- generic_vs_params.carbon
+// --- fail_non_generic_implicit_params.carbon
+
+library "non_generic_implicit_params";
+
+// CHECK:STDERR: fail_non_generic_implicit_params.carbon:[[@LINE+4]]:9: ERROR: Parameters of generic types must be constant.
+// CHECK:STDERR: class A[T: type]() {}
+// CHECK:STDERR:         ^
+// CHECK:STDERR:
+class A[T: type]() {}
+
+// --- fail_non_generic_params.carbon
+
+library "non_generic_params";
+
+// CHECK:STDERR: fail_non_generic_params.carbon:[[@LINE+3]]:9: ERROR: Parameters of generic types must be constant.
+// CHECK:STDERR: class A(T: type) {}
+// CHECK:STDERR:         ^
+class A(T: type) {}
+
+// This is testing a use of the invalid type.
+fn F(T:! type) {
+  A(T);
+}
+
+// CHECK:STDOUT: --- params.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %NotGenericNoParams: type = class_type @NotGenericNoParams [template]
@@ -75,44 +103,44 @@ var e: C(X).GenericAndParams(X) = {};
 // CHECK:STDOUT:   %NotGenericNoParams.decl: type = class_decl @NotGenericNoParams [template = constants.%NotGenericNoParams] {}
 // CHECK:STDOUT:   %NotGenericButParams.decl: %NotGenericButParams.type = class_decl @NotGenericButParams [template = constants.%NotGenericButParams.1] {}
 // CHECK:STDOUT:   %GenericAndParams.decl: %GenericAndParams.type.1 = class_decl @GenericAndParams.1 [template = constants.%GenericAndParams.1] {
-// CHECK:STDOUT:     %T.loc13_24.1: type = param T
-// CHECK:STDOUT:     %T.loc13_24.2: type = bind_symbolic_name T 0, %T.loc13_24.1 [symbolic = @GenericAndParams.1.%T (constants.%T)]
+// CHECK:STDOUT:     %T.loc6_24.1: type = param T
+// CHECK:STDOUT:     %T.loc6_24.2: type = bind_symbolic_name T 0, %T.loc6_24.1 [symbolic = @GenericAndParams.1.%T (constants.%T)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: %C.type = class_decl @C [template = constants.%C.1] {
-// CHECK:STDOUT:     %T.loc15_9.1: type = param T
-// CHECK:STDOUT:     %T.loc15_9.2: type = bind_symbolic_name T 0, %T.loc15_9.1 [symbolic = @C.%T (constants.%T)]
+// CHECK:STDOUT:     %T.loc8_9.1: type = param T
+// CHECK:STDOUT:     %T.loc8_9.2: type = bind_symbolic_name T 0, %T.loc8_9.1 [symbolic = @C.%T (constants.%T)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %X.decl: type = class_decl @X [template = constants.%X] {}
 // CHECK:STDOUT:   %NotGenericNoParams.ref: type = name_ref NotGenericNoParams, %NotGenericNoParams.decl [template = constants.%NotGenericNoParams]
 // CHECK:STDOUT:   %a.var: ref %NotGenericNoParams = var a
 // CHECK:STDOUT:   %a: ref %NotGenericNoParams = bind_name a, %a.var
 // CHECK:STDOUT:   %NotGenericButParams.ref: %NotGenericButParams.type = name_ref NotGenericButParams, %NotGenericButParams.decl [template = constants.%NotGenericButParams.1]
-// CHECK:STDOUT:   %.loc23_27: init type = call %NotGenericButParams.ref() [template = constants.%NotGenericButParams.2]
-// CHECK:STDOUT:   %.loc23_28.1: type = value_of_initializer %.loc23_27 [template = constants.%NotGenericButParams.2]
-// CHECK:STDOUT:   %.loc23_28.2: type = converted %.loc23_27, %.loc23_28.1 [template = constants.%NotGenericButParams.2]
+// CHECK:STDOUT:   %.loc16_27: init type = call %NotGenericButParams.ref() [template = constants.%NotGenericButParams.2]
+// CHECK:STDOUT:   %.loc16_28.1: type = value_of_initializer %.loc16_27 [template = constants.%NotGenericButParams.2]
+// CHECK:STDOUT:   %.loc16_28.2: type = converted %.loc16_27, %.loc16_28.1 [template = constants.%NotGenericButParams.2]
 // CHECK:STDOUT:   %b.var: ref %NotGenericButParams.2 = var b
 // CHECK:STDOUT:   %b: ref %NotGenericButParams.2 = bind_name b, %b.var
-// CHECK:STDOUT:   %GenericAndParams.ref.loc24: %GenericAndParams.type.1 = name_ref GenericAndParams, %GenericAndParams.decl [template = constants.%GenericAndParams.1]
-// CHECK:STDOUT:   %X.ref.loc24: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:   %.loc24_24: init type = call %GenericAndParams.ref.loc24(%X.ref.loc24) [template = constants.%GenericAndParams.5]
-// CHECK:STDOUT:   %.loc24_26.1: type = value_of_initializer %.loc24_24 [template = constants.%GenericAndParams.5]
-// CHECK:STDOUT:   %.loc24_26.2: type = converted %.loc24_24, %.loc24_26.1 [template = constants.%GenericAndParams.5]
+// CHECK:STDOUT:   %GenericAndParams.ref.loc17: %GenericAndParams.type.1 = name_ref GenericAndParams, %GenericAndParams.decl [template = constants.%GenericAndParams.1]
+// CHECK:STDOUT:   %X.ref.loc17: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:   %.loc17_24: init type = call %GenericAndParams.ref.loc17(%X.ref.loc17) [template = constants.%GenericAndParams.5]
+// CHECK:STDOUT:   %.loc17_26.1: type = value_of_initializer %.loc17_24 [template = constants.%GenericAndParams.5]
+// CHECK:STDOUT:   %.loc17_26.2: type = converted %.loc17_24, %.loc17_26.1 [template = constants.%GenericAndParams.5]
 // CHECK:STDOUT:   %c.var: ref %GenericAndParams.5 = var c
 // CHECK:STDOUT:   %c: ref %GenericAndParams.5 = bind_name c, %c.var
-// CHECK:STDOUT:   %C.ref.loc25: %C.type = name_ref C, %C.decl [template = constants.%C.1]
-// CHECK:STDOUT:   %X.ref.loc25: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:   %.loc25: init type = call %C.ref.loc25(%X.ref.loc25) [template = constants.%C.3]
+// CHECK:STDOUT:   %C.ref.loc18: %C.type = name_ref C, %C.decl [template = constants.%C.1]
+// CHECK:STDOUT:   %X.ref.loc18: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:   %.loc18: init type = call %C.ref.loc18(%X.ref.loc18) [template = constants.%C.3]
 // CHECK:STDOUT:   %GenericNoParams.ref: type = name_ref GenericNoParams, @C.%GenericNoParams.decl [template = constants.%GenericNoParams.1]
 // CHECK:STDOUT:   %d.var: ref %GenericNoParams.1 = var d
 // CHECK:STDOUT:   %d: ref %GenericNoParams.1 = bind_name d, %d.var
-// CHECK:STDOUT:   %C.ref.loc26: %C.type = name_ref C, %C.decl [template = constants.%C.1]
-// CHECK:STDOUT:   %X.ref.loc26_10: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:   %.loc26_9: init type = call %C.ref.loc26(%X.ref.loc26_10) [template = constants.%C.3]
-// CHECK:STDOUT:   %GenericAndParams.ref.loc26: %GenericAndParams.type.2 = name_ref GenericAndParams, @C.%GenericAndParams.decl [template = constants.%GenericAndParams.3]
-// CHECK:STDOUT:   %X.ref.loc26_30: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:   %.loc26_29: init type = call %GenericAndParams.ref.loc26(%X.ref.loc26_30) [template = constants.%GenericAndParams.6]
-// CHECK:STDOUT:   %.loc26_31.1: type = value_of_initializer %.loc26_29 [template = constants.%GenericAndParams.6]
-// CHECK:STDOUT:   %.loc26_31.2: type = converted %.loc26_29, %.loc26_31.1 [template = constants.%GenericAndParams.6]
+// CHECK:STDOUT:   %C.ref.loc19: %C.type = name_ref C, %C.decl [template = constants.%C.1]
+// CHECK:STDOUT:   %X.ref.loc19_10: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:   %.loc19_9: init type = call %C.ref.loc19(%X.ref.loc19_10) [template = constants.%C.3]
+// CHECK:STDOUT:   %GenericAndParams.ref.loc19: %GenericAndParams.type.2 = name_ref GenericAndParams, @C.%GenericAndParams.decl [template = constants.%GenericAndParams.3]
+// CHECK:STDOUT:   %X.ref.loc19_30: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:   %.loc19_29: init type = call %GenericAndParams.ref.loc19(%X.ref.loc19_30) [template = constants.%GenericAndParams.6]
+// CHECK:STDOUT:   %.loc19_31.1: type = value_of_initializer %.loc19_29 [template = constants.%GenericAndParams.6]
+// CHECK:STDOUT:   %.loc19_31.2: type = converted %.loc19_29, %.loc19_31.1 [template = constants.%GenericAndParams.6]
 // CHECK:STDOUT:   %e.var: ref %GenericAndParams.6 = var e
 // CHECK:STDOUT:   %e: ref %GenericAndParams.6 = bind_name e, %e.var
 // CHECK:STDOUT: }
@@ -127,7 +155,7 @@ var e: C(X).GenericAndParams(X) = {};
 // CHECK:STDOUT:   .Self = constants.%NotGenericButParams.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic class @GenericAndParams.1(file.%T.loc13_24.2: type) {
+// CHECK:STDOUT: generic class @GenericAndParams.1(file.%T.loc6_24.2: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
@@ -138,7 +166,7 @@ var e: C(X).GenericAndParams(X) = {};
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic class @C(file.%T.loc15_9.2: type) {
+// CHECK:STDOUT: generic class @C(file.%T.loc8_9.2: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
@@ -146,8 +174,8 @@ var e: C(X).GenericAndParams(X) = {};
 // CHECK:STDOUT:   class {
 // CHECK:STDOUT:     %GenericNoParams.decl: type = class_decl @GenericNoParams [template = constants.%GenericNoParams.1] {}
 // CHECK:STDOUT:     %GenericAndParams.decl: %GenericAndParams.type.2 = class_decl @GenericAndParams.2 [template = constants.%GenericAndParams.3] {
-// CHECK:STDOUT:       %U.loc17_26.1: type = param U
-// CHECK:STDOUT:       %U.loc17_26.2: type = bind_symbolic_name U 1, %U.loc17_26.1 [symbolic = @GenericAndParams.2.%U (constants.%U)]
+// CHECK:STDOUT:       %U.loc10_26.1: type = param U
+// CHECK:STDOUT:       %U.loc10_26.2: type = bind_symbolic_name U 1, %U.loc10_26.1 [symbolic = @GenericAndParams.2.%U (constants.%U)]
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:
 // CHECK:STDOUT:   !members:
@@ -157,7 +185,7 @@ var e: C(X).GenericAndParams(X) = {};
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic class @GenericNoParams(file.%T.loc15_9.2: type) {
+// CHECK:STDOUT: generic class @GenericNoParams(file.%T.loc8_9.2: type) {
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
@@ -166,7 +194,7 @@ var e: C(X).GenericAndParams(X) = {};
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic class @GenericAndParams.2(file.%T.loc15_9.2: type, @C.%U.loc17_26.2: type) {
+// CHECK:STDOUT: generic class @GenericAndParams.2(file.%T.loc8_9.2: type, @C.%U.loc10_26.2: type) {
 // CHECK:STDOUT:   %U: type = bind_symbolic_name U 1 [symbolic = %U (constants.%U)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
@@ -184,26 +212,26 @@ var e: C(X).GenericAndParams(X) = {};
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc22_30.1: %.1 = struct_literal ()
-// CHECK:STDOUT:   %.loc22_30.2: init %NotGenericNoParams = class_init (), file.%a.var [template = constants.%struct.1]
-// CHECK:STDOUT:   %.loc22_31: init %NotGenericNoParams = converted %.loc22_30.1, %.loc22_30.2 [template = constants.%struct.1]
-// CHECK:STDOUT:   assign file.%a.var, %.loc22_31
-// CHECK:STDOUT:   %.loc23_33.1: %.1 = struct_literal ()
-// CHECK:STDOUT:   %.loc23_33.2: init %NotGenericButParams.2 = class_init (), file.%b.var [template = constants.%struct.2]
-// CHECK:STDOUT:   %.loc23_34: init %NotGenericButParams.2 = converted %.loc23_33.1, %.loc23_33.2 [template = constants.%struct.2]
-// CHECK:STDOUT:   assign file.%b.var, %.loc23_34
-// CHECK:STDOUT:   %.loc24_31.1: %.1 = struct_literal ()
-// CHECK:STDOUT:   %.loc24_31.2: init %GenericAndParams.5 = class_init (), file.%c.var [template = constants.%struct.3]
-// CHECK:STDOUT:   %.loc24_32: init %GenericAndParams.5 = converted %.loc24_31.1, %.loc24_31.2 [template = constants.%struct.3]
-// CHECK:STDOUT:   assign file.%c.var, %.loc24_32
-// CHECK:STDOUT:   %.loc25_32.1: %.1 = struct_literal ()
-// CHECK:STDOUT:   %.loc25_32.2: init %GenericNoParams.1 = class_init (), file.%d.var [template = constants.%struct.4]
-// CHECK:STDOUT:   %.loc25_33: init %GenericNoParams.1 = converted %.loc25_32.1, %.loc25_32.2 [template = constants.%struct.4]
-// CHECK:STDOUT:   assign file.%d.var, %.loc25_33
-// CHECK:STDOUT:   %.loc26_36.1: %.1 = struct_literal ()
-// CHECK:STDOUT:   %.loc26_36.2: init %GenericAndParams.6 = class_init (), file.%e.var [template = constants.%struct.5]
-// CHECK:STDOUT:   %.loc26_37: init %GenericAndParams.6 = converted %.loc26_36.1, %.loc26_36.2 [template = constants.%struct.5]
-// CHECK:STDOUT:   assign file.%e.var, %.loc26_37
+// CHECK:STDOUT:   %.loc15_30.1: %.1 = struct_literal ()
+// CHECK:STDOUT:   %.loc15_30.2: init %NotGenericNoParams = class_init (), file.%a.var [template = constants.%struct.1]
+// CHECK:STDOUT:   %.loc15_31: init %NotGenericNoParams = converted %.loc15_30.1, %.loc15_30.2 [template = constants.%struct.1]
+// CHECK:STDOUT:   assign file.%a.var, %.loc15_31
+// CHECK:STDOUT:   %.loc16_33.1: %.1 = struct_literal ()
+// CHECK:STDOUT:   %.loc16_33.2: init %NotGenericButParams.2 = class_init (), file.%b.var [template = constants.%struct.2]
+// CHECK:STDOUT:   %.loc16_34: init %NotGenericButParams.2 = converted %.loc16_33.1, %.loc16_33.2 [template = constants.%struct.2]
+// CHECK:STDOUT:   assign file.%b.var, %.loc16_34
+// CHECK:STDOUT:   %.loc17_31.1: %.1 = struct_literal ()
+// CHECK:STDOUT:   %.loc17_31.2: init %GenericAndParams.5 = class_init (), file.%c.var [template = constants.%struct.3]
+// CHECK:STDOUT:   %.loc17_32: init %GenericAndParams.5 = converted %.loc17_31.1, %.loc17_31.2 [template = constants.%struct.3]
+// CHECK:STDOUT:   assign file.%c.var, %.loc17_32
+// CHECK:STDOUT:   %.loc18_32.1: %.1 = struct_literal ()
+// CHECK:STDOUT:   %.loc18_32.2: init %GenericNoParams.1 = class_init (), file.%d.var [template = constants.%struct.4]
+// CHECK:STDOUT:   %.loc18_33: init %GenericNoParams.1 = converted %.loc18_32.1, %.loc18_32.2 [template = constants.%struct.4]
+// CHECK:STDOUT:   assign file.%d.var, %.loc18_33
+// CHECK:STDOUT:   %.loc19_36.1: %.1 = struct_literal ()
+// CHECK:STDOUT:   %.loc19_36.2: init %GenericAndParams.6 = class_init (), file.%e.var [template = constants.%struct.5]
+// CHECK:STDOUT:   %.loc19_37: init %GenericAndParams.6 = converted %.loc19_36.1, %.loc19_36.2 [template = constants.%struct.5]
+// CHECK:STDOUT:   assign file.%e.var, %.loc19_37
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -239,3 +267,79 @@ var e: C(X).GenericAndParams(X) = {};
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_non_generic_implicit_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %A.type: type = generic_class_type @A [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %A.1: %A.type = struct_value () [template]
+// CHECK:STDOUT:   %A.2: type = class_type @A [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %A.decl: %A.type = class_decl @A [template = constants.%A.1] {
+// CHECK:STDOUT:     %T.loc8_9.1: type = param T
+// CHECK:STDOUT:     %T.loc8_9.2: type = bind_name T, %T.loc8_9.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @A {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%A.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_non_generic_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %A.type: type = generic_class_type @A [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %A.1: %A.type = struct_value () [template]
+// CHECK:STDOUT:   %A.2: type = class_type @A [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %A.decl: %A.type = class_decl @A [template = constants.%A.1] {
+// CHECK:STDOUT:     %T.loc7_9.1: type = param T
+// CHECK:STDOUT:     %T.loc7_9.2: type = bind_name T, %T.loc7_9.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
+// CHECK:STDOUT:     %T.loc10_6.1: type = param T
+// CHECK:STDOUT:     @F.%T.loc10: type = bind_symbolic_name T 0, %T.loc10_6.1 [symbolic = @F.%T.1 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @A {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%A.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @F(%T.loc10: type) {
+// CHECK:STDOUT:   %T.1: type = bind_symbolic_name T 0 [symbolic = %T.1 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%T.loc10: type) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %A.ref: %A.type = name_ref A, file.%A.decl [template = constants.%A.1]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc10 [symbolic = %T.1 (constants.%T)]
+// CHECK:STDOUT:     %.loc11: init type = call %A.ref(<invalid>) [template = <error>]
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(constants.%T) {
+// CHECK:STDOUT:   %T.1 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 343 - 146
toolchain/check/testdata/class/no_prelude/syntactic_merge.carbon

@@ -15,11 +15,11 @@ library "basic";
 class C {}
 alias D = C;
 
-class Foo(a: C);
-class Foo(a: C) {}
+class Foo(a:! C);
+class Foo(a:! C) {}
 
-class Bar(a: D);
-class Bar(a: D) {}
+class Bar(a:! D);
+class Bar(a:! D) {}
 
 // --- spacing.carbon
 
@@ -27,8 +27,8 @@ library "spacing";
 
 class C {}
 
-class Foo [ ] ( a : C );
-class Foo[](a: C) {}
+class Foo [ ] ( a :! C );
+class Foo[](a:! C) {}
 
 // --- fail_parens.carbon
 
@@ -36,15 +36,15 @@ library "parens";
 
 class C {}
 
-class Foo(a: C);
-// CHECK:STDERR: fail_parens.carbon:[[@LINE+7]]:14: ERROR: Redeclaration syntax differs here.
-// CHECK:STDERR: class Foo(a: (C)) {}
-// CHECK:STDERR:              ^
-// CHECK:STDERR: fail_parens.carbon:[[@LINE-4]]:14: Comparing with previous declaration here.
-// CHECK:STDERR: class Foo(a: C);
-// CHECK:STDERR:              ^
+class Foo(a:! C);
+// CHECK:STDERR: fail_parens.carbon:[[@LINE+7]]:15: ERROR: Redeclaration syntax differs here.
+// CHECK:STDERR: class Foo(a:! (C)) {}
+// CHECK:STDERR:               ^
+// CHECK:STDERR: fail_parens.carbon:[[@LINE-4]]:15: Comparing with previous declaration here.
+// CHECK:STDERR: class Foo(a:! C);
+// CHECK:STDERR:               ^
 // CHECK:STDERR:
-class Foo(a: (C)) {}
+class Foo(a:! (C)) {}
 
 // --- todo_fail_raw_identifier.carbon
 
@@ -52,8 +52,8 @@ library "raw_identifier";
 
 class C {}
 
-class Foo(a: C);
-class Foo(a: r#C) {}
+class Foo(a:! C);
+class Foo(a:! r#C) {}
 
 // --- two_file.carbon
 
@@ -62,15 +62,15 @@ library "two_file";
 class C {}
 alias D = C;
 
-class Foo(a: C);
-class Bar(a: D);
+class Foo(a:! C);
+class Bar(a:! D);
 
 // --- two_file.impl.carbon
 
 impl library "two_file";
 
-class Foo(a: C) {}
-class Bar(a: D) {}
+class Foo(a:! C) {}
+class Bar(a:! D) {}
 
 // --- fail_name_mismatch.carbon
 
@@ -79,15 +79,15 @@ library "name_mismatch";
 class C {}
 alias D = C;
 
-class Foo(a: C);
+class Foo(a:! C);
 // CHECK:STDERR: fail_name_mismatch.carbon:[[@LINE+7]]:11: ERROR: Redeclaration differs at parameter 1.
-// CHECK:STDERR: class Foo(b: D) {}
+// CHECK:STDERR: class Foo(b:! D) {}
 // CHECK:STDERR:           ^
 // CHECK:STDERR: fail_name_mismatch.carbon:[[@LINE-4]]:11: Previous declaration's corresponding parameter here.
-// CHECK:STDERR: class Foo(a: C);
+// CHECK:STDERR: class Foo(a:! C);
 // CHECK:STDERR:           ^
 // CHECK:STDERR:
-class Foo(b: D) {}
+class Foo(b:! D) {}
 
 // --- fail_alias.carbon
 
@@ -96,15 +96,15 @@ library "alias";
 class C {}
 alias D = C;
 
-class Foo(a: C);
-// CHECK:STDERR: fail_alias.carbon:[[@LINE+7]]:14: ERROR: Redeclaration syntax differs here.
-// CHECK:STDERR: class Foo(a: D) {}
-// CHECK:STDERR:              ^
-// CHECK:STDERR: fail_alias.carbon:[[@LINE-4]]:14: Comparing with previous declaration here.
-// CHECK:STDERR: class Foo(a: C);
-// CHECK:STDERR:              ^
+class Foo(a:! C);
+// CHECK:STDERR: fail_alias.carbon:[[@LINE+7]]:15: ERROR: Redeclaration syntax differs here.
+// CHECK:STDERR: class Foo(a:! D) {}
+// CHECK:STDERR:               ^
+// CHECK:STDERR: fail_alias.carbon:[[@LINE-4]]:15: Comparing with previous declaration here.
+// CHECK:STDERR: class Foo(a:! C);
+// CHECK:STDERR:               ^
 // CHECK:STDERR:
-class Foo(a: D) {}
+class Foo(a:! D) {}
 
 // --- fail_deduced_alias.carbon
 
@@ -113,15 +113,15 @@ library "deduced_alias";
 class C {}
 alias D = C;
 
-class Foo[a: C]();
-// CHECK:STDERR: fail_deduced_alias.carbon:[[@LINE+7]]:14: ERROR: Redeclaration syntax differs here.
-// CHECK:STDERR: class Foo[a: D]() {}
-// CHECK:STDERR:              ^
-// CHECK:STDERR: fail_deduced_alias.carbon:[[@LINE-4]]:14: Comparing with previous declaration here.
-// CHECK:STDERR: class Foo[a: C]();
-// CHECK:STDERR:              ^
+class Foo[a:! C]();
+// CHECK:STDERR: fail_deduced_alias.carbon:[[@LINE+7]]:15: ERROR: Redeclaration syntax differs here.
+// CHECK:STDERR: class Foo[a:! D]() {}
+// CHECK:STDERR:               ^
+// CHECK:STDERR: fail_deduced_alias.carbon:[[@LINE-4]]:15: Comparing with previous declaration here.
+// CHECK:STDERR: class Foo[a:! C]();
+// CHECK:STDERR:               ^
 // CHECK:STDERR:
-class Foo[a: D]() {}
+class Foo[a:! D]() {}
 
 // --- alias_two_file.carbon
 
@@ -129,7 +129,7 @@ library "alias_two_file";
 
 class C {}
 
-class Foo(a: C);
+class Foo(a:! C);
 
 // --- todo_fail_alias_two_file.impl.carbon
 
@@ -137,7 +137,7 @@ impl library "alias_two_file";
 
 alias D = C;
 
-class Foo(a: D) {}
+class Foo(a:! D) {}
 
 // --- fail_repeat_const.carbon
 
@@ -145,19 +145,19 @@ library "repeat_const";
 
 class C {}
 
-class Foo(a: const C);
-// CHECK:STDERR: fail_repeat_const.carbon:[[@LINE+11]]:14: WARNING: `const` applied repeatedly to the same type has no additional effect.
-// CHECK:STDERR: class Foo(a: const (const C)) {}
-// CHECK:STDERR:              ^~~~~~~~~~~~~~~
+class Foo(a:! const C);
+// CHECK:STDERR: fail_repeat_const.carbon:[[@LINE+11]]:15: WARNING: `const` applied repeatedly to the same type has no additional effect.
+// CHECK:STDERR: class Foo(a:! const (const C)) {}
+// CHECK:STDERR:               ^~~~~~~~~~~~~~~
 // CHECK:STDERR:
-// CHECK:STDERR: fail_repeat_const.carbon:[[@LINE+7]]:20: ERROR: Redeclaration syntax differs here.
-// CHECK:STDERR: class Foo(a: const (const C)) {}
-// CHECK:STDERR:                    ^
-// CHECK:STDERR: fail_repeat_const.carbon:[[@LINE-8]]:20: Comparing with previous declaration here.
-// CHECK:STDERR: class Foo(a: const C);
-// CHECK:STDERR:                    ^
+// CHECK:STDERR: fail_repeat_const.carbon:[[@LINE+7]]:21: ERROR: Redeclaration syntax differs here.
+// CHECK:STDERR: class Foo(a:! const (const C)) {}
+// CHECK:STDERR:                     ^
+// CHECK:STDERR: fail_repeat_const.carbon:[[@LINE-8]]:21: Comparing with previous declaration here.
+// CHECK:STDERR: class Foo(a:! const C);
+// CHECK:STDERR:                     ^
 // CHECK:STDERR:
-class Foo(a: const (const C)) {}
+class Foo(a:! const (const C)) {}
 
 // --- fail_self_type.carbon
 
@@ -184,13 +184,14 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT:   %Bar.type: type = generic_class_type @Bar [template]
 // CHECK:STDOUT:   %Bar.1: %Bar.type = struct_value () [template]
-// CHECK:STDOUT:   %Bar.2: type = class_type @Bar [template]
+// CHECK:STDOUT:   %Bar.2: type = class_type @Bar, @Bar(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -206,22 +207,22 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl.loc7: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc7: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc7_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_name a, %a.loc7_11.1
+// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_symbolic_name a 0, %a.loc7_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Foo.decl.loc8: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc8_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc8_11.2: %C = bind_name a, %a.loc8_11.1
+// CHECK:STDOUT:     %a.loc8_11.2: %C = bind_symbolic_name a 0, %a.loc8_11.1 [symbolic = constants.%a]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Bar.decl.loc10: %Bar.type = class_decl @Bar [template = constants.%Bar.1] {
 // CHECK:STDOUT:     %D.ref.loc10: type = name_ref D, %D [template = constants.%C]
 // CHECK:STDOUT:     %a.loc10_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc10_11.2: %C = bind_name a, %a.loc10_11.1
+// CHECK:STDOUT:     %a.loc10_11.2: %C = bind_symbolic_name a 0, %a.loc10_11.1 [symbolic = @Bar.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Bar.decl.loc11: %Bar.type = class_decl @Bar [template = constants.%Bar.1] {
 // CHECK:STDOUT:     %D.ref.loc11: type = name_ref D, %D [template = constants.%C]
 // CHECK:STDOUT:     %a.loc11_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc11_11.2: %C = bind_name a, %a.loc11_11.1
+// CHECK:STDOUT:     %a.loc11_11.2: %C = bind_symbolic_name a 0, %a.loc11_11.1 [symbolic = constants.%a]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -230,14 +231,34 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%Foo.2
+// CHECK:STDOUT: generic class @Foo(file.%a.loc7_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%Foo.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Bar {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%Bar.2
+// CHECK:STDOUT: generic class @Bar(file.%a.loc10_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%Bar.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Bar(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- spacing.carbon
@@ -245,10 +266,11 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -260,12 +282,12 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl.loc6: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc6: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc6_17.1: %C = param a
-// CHECK:STDOUT:     %a.loc6_17.2: %C = bind_name a, %a.loc6_17.1
+// CHECK:STDOUT:     %a.loc6_17.2: %C = bind_symbolic_name a 0, %a.loc6_17.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Foo.decl.loc7: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc7: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc7_13.1: %C = param a
-// CHECK:STDOUT:     %a.loc7_13.2: %C = bind_name a, %a.loc7_13.1
+// CHECK:STDOUT:     %a.loc7_13.2: %C = bind_symbolic_name a 0, %a.loc7_13.1 [symbolic = constants.%a]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -274,9 +296,19 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%Foo.2
+// CHECK:STDOUT: generic class @Foo(file.%a.loc6_17.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%Foo.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_parens.carbon
@@ -284,13 +316,14 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT:   %.type: type = generic_class_type @.1 [template]
 // CHECK:STDOUT:   %.3: %.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.4: type = class_type @.1, @.1(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -302,12 +335,12 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc6: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc6_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc6_11.2: %C = bind_name a, %a.loc6_11.1
+// CHECK:STDOUT:     %a.loc6_11.2: %C = bind_symbolic_name a 0, %a.loc6_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.decl: %.type = class_decl @.1 [template = constants.%.3] {
 // CHECK:STDOUT:     %C.ref.loc14: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc14_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc14_11.2: %C = bind_name a, %a.loc14_11.1
+// CHECK:STDOUT:     %a.loc14_11.2: %C = bind_symbolic_name a 0, %a.loc14_11.1 [symbolic = @.1.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -316,11 +349,29 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo;
+// CHECK:STDOUT: generic class @Foo(file.%a.loc6_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%.4
+// CHECK:STDOUT:   class;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @.1(file.%a.loc14_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%.4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @.1(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- todo_fail_raw_identifier.carbon
@@ -328,10 +379,11 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -343,12 +395,12 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl.loc6: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc6: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc6_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc6_11.2: %C = bind_name a, %a.loc6_11.1
+// CHECK:STDOUT:     %a.loc6_11.2: %C = bind_symbolic_name a 0, %a.loc6_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Foo.decl.loc7: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc7: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc7_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_name a, %a.loc7_11.1
+// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_symbolic_name a 0, %a.loc7_11.1 [symbolic = constants.%a]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -357,9 +409,19 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%Foo.2
+// CHECK:STDOUT: generic class @Foo(file.%a.loc6_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%Foo.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- two_file.carbon
@@ -367,13 +429,14 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT:   %Bar.type: type = generic_class_type @Bar [template]
 // CHECK:STDOUT:   %Bar.1: %Bar.type = struct_value () [template]
-// CHECK:STDOUT:   %Bar.2: type = class_type @Bar [template]
+// CHECK:STDOUT:   %Bar.2: type = class_type @Bar, @Bar(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -389,12 +452,12 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc7: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc7_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_name a, %a.loc7_11.1
+// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_symbolic_name a 0, %a.loc7_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Bar.decl: %Bar.type = class_decl @Bar [template = constants.%Bar.1] {
 // CHECK:STDOUT:     %D.ref: type = name_ref D, %D [template = constants.%C]
 // CHECK:STDOUT:     %a.loc8_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc8_11.2: %C = bind_name a, %a.loc8_11.1
+// CHECK:STDOUT:     %a.loc8_11.2: %C = bind_symbolic_name a 0, %a.loc8_11.1 [symbolic = @Bar.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -403,29 +466,46 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo;
+// CHECK:STDOUT: generic class @Foo(file.%a.loc7_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @Bar(file.%a.loc8_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Bar;
+// CHECK:STDOUT: specific @Bar(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- two_file.impl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT:   %Bar.type: type = generic_class_type @Bar [template]
 // CHECK:STDOUT:   %Bar.1: %Bar.type = struct_value () [template]
-// CHECK:STDOUT:   %Bar.2: type = class_type @Bar [template]
+// CHECK:STDOUT:   %Bar.2: type = class_type @Bar, @Bar(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %import_ref.1: type = import_ref Main//two_file, inst+1, loaded [template = constants.%C]
 // CHECK:STDOUT:   %import_ref.2: type = import_ref Main//two_file, inst+5, loaded [template = constants.%C]
-// CHECK:STDOUT:   %import_ref.3: %Foo.type = import_ref Main//two_file, inst+9, loaded [template = constants.%Foo.1]
-// CHECK:STDOUT:   %import_ref.4: %Bar.type = import_ref Main//two_file, inst+17, loaded [template = constants.%Bar.1]
+// CHECK:STDOUT:   %import_ref.3: %Foo.type = import_ref Main//two_file, inst+10, loaded [template = constants.%Foo.1]
+// CHECK:STDOUT:   %import_ref.4: %Bar.type = import_ref Main//two_file, inst+19, loaded [template = constants.%Bar.1]
 // CHECK:STDOUT:   %import_ref.5 = import_ref Main//two_file, inst+2, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -441,12 +521,12 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref: type = name_ref C, imports.%import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:     %a.loc4_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc4_11.2: %C = bind_name a, %a.loc4_11.1
+// CHECK:STDOUT:     %a.loc4_11.2: %C = bind_symbolic_name a 0, %a.loc4_11.1 [symbolic = constants.%a]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Bar.decl: %Bar.type = class_decl @Bar [template = constants.%Bar.1] {
 // CHECK:STDOUT:     %D.ref: type = name_ref D, imports.%import_ref.2 [template = constants.%C]
 // CHECK:STDOUT:     %a.loc5_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc5_11.2: %C = bind_name a, %a.loc5_11.1
+// CHECK:STDOUT:     %a.loc5_11.2: %C = bind_symbolic_name a 0, %a.loc5_11.1 [symbolic = constants.%a]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -455,14 +535,34 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = imports.%import_ref.5
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%Foo.2
+// CHECK:STDOUT: generic class @Foo(constants.%a: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%Foo.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Bar {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%Bar.2
+// CHECK:STDOUT: generic class @Bar(constants.%a: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%Bar.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Bar(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_name_mismatch.carbon
@@ -470,13 +570,15 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
+// CHECK:STDOUT:   %b: %C = bind_symbolic_name b 0 [symbolic]
 // CHECK:STDOUT:   %.type: type = generic_class_type @.1 [template]
 // CHECK:STDOUT:   %.3: %.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.4: type = class_type @.1, @.1(%b) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -491,12 +593,12 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc7: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc7_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_name a, %a.loc7_11.1
+// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_symbolic_name a 0, %a.loc7_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.decl: %.type = class_decl @.1 [template = constants.%.3] {
 // CHECK:STDOUT:     %D.ref: type = name_ref D, %D [template = constants.%C]
 // CHECK:STDOUT:     %b.loc15_11.1: %C = param b
-// CHECK:STDOUT:     %b.loc15_11.2: %C = bind_name b, %b.loc15_11.1
+// CHECK:STDOUT:     %b.loc15_11.2: %C = bind_symbolic_name b 0, %b.loc15_11.1 [symbolic = @.1.%b (constants.%b)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -505,11 +607,29 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo;
+// CHECK:STDOUT: generic class @Foo(file.%a.loc7_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%.4
+// CHECK:STDOUT:   class;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @.1(file.%b.loc15_11.2: %C) {
+// CHECK:STDOUT:   %b: %C = bind_symbolic_name b 0 [symbolic = %b (constants.%b)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%.4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @.1(constants.%b) {
+// CHECK:STDOUT:   %b => constants.%b
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_alias.carbon
@@ -517,13 +637,14 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT:   %.type: type = generic_class_type @.1 [template]
 // CHECK:STDOUT:   %.3: %.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.4: type = class_type @.1, @.1(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -538,12 +659,12 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc7: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc7_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_name a, %a.loc7_11.1
+// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_symbolic_name a 0, %a.loc7_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.decl: %.type = class_decl @.1 [template = constants.%.3] {
 // CHECK:STDOUT:     %D.ref: type = name_ref D, %D [template = constants.%C]
 // CHECK:STDOUT:     %a.loc15_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc15_11.2: %C = bind_name a, %a.loc15_11.1
+// CHECK:STDOUT:     %a.loc15_11.2: %C = bind_symbolic_name a 0, %a.loc15_11.1 [symbolic = @.1.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -552,11 +673,29 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo;
+// CHECK:STDOUT: generic class @Foo(file.%a.loc7_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%.4
+// CHECK:STDOUT:   class;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @.1(file.%a.loc15_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%.4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @.1(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_deduced_alias.carbon
@@ -564,13 +703,14 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT:   %.type: type = generic_class_type @.1 [template]
 // CHECK:STDOUT:   %.3: %.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.4: type = class_type @.1, @.1(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -585,12 +725,12 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref.loc7: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc7_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_name a, %a.loc7_11.1
+// CHECK:STDOUT:     %a.loc7_11.2: %C = bind_symbolic_name a 0, %a.loc7_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.decl: %.type = class_decl @.1 [template = constants.%.3] {
 // CHECK:STDOUT:     %D.ref: type = name_ref D, %D [template = constants.%C]
 // CHECK:STDOUT:     %a.loc15_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc15_11.2: %C = bind_name a, %a.loc15_11.1
+// CHECK:STDOUT:     %a.loc15_11.2: %C = bind_symbolic_name a 0, %a.loc15_11.1 [symbolic = @.1.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -599,11 +739,29 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo;
+// CHECK:STDOUT: generic class @Foo(file.%a.loc7_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%.4
+// CHECK:STDOUT:   class;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @.1(file.%a.loc15_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%.4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @.1(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- alias_two_file.carbon
@@ -611,10 +769,11 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -626,7 +785,7 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %C.ref: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %a.loc6_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc6_11.2: %C = bind_name a, %a.loc6_11.1
+// CHECK:STDOUT:     %a.loc6_11.2: %C = bind_symbolic_name a 0, %a.loc6_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -635,22 +794,31 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo;
+// CHECK:STDOUT: generic class @Foo(file.%a.loc6_11.2: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- todo_fail_alias_two_file.impl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %import_ref.1: type = import_ref Main//alias_two_file, inst+1, loaded [template = constants.%C]
-// CHECK:STDOUT:   %import_ref.2: %Foo.type = import_ref Main//alias_two_file, inst+7, loaded [template = constants.%Foo.1]
+// CHECK:STDOUT:   %import_ref.2: %Foo.type = import_ref Main//alias_two_file, inst+8, loaded [template = constants.%Foo.1]
 // CHECK:STDOUT:   %import_ref.3 = import_ref Main//alias_two_file, inst+2, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -667,7 +835,7 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %Foo.decl: %Foo.type = class_decl @Foo [template = constants.%Foo.1] {
 // CHECK:STDOUT:     %D.ref: type = name_ref D, %D [template = constants.%C]
 // CHECK:STDOUT:     %a.loc6_11.1: %C = param a
-// CHECK:STDOUT:     %a.loc6_11.2: %C = bind_name a, %a.loc6_11.1
+// CHECK:STDOUT:     %a.loc6_11.2: %C = bind_symbolic_name a 0, %a.loc6_11.1 [symbolic = constants.%a]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -676,9 +844,19 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = imports.%import_ref.3
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%Foo.2
+// CHECK:STDOUT: generic class @Foo(constants.%a: %C) {
+// CHECK:STDOUT:   %a: %C = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%Foo.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_repeat_const.carbon
@@ -687,13 +865,14 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %.1: type = struct_type {} [template]
 // CHECK:STDOUT:   %.2: type = const_type %C [template]
+// CHECK:STDOUT:   %a: %.2 = bind_symbolic_name a 0 [symbolic]
 // CHECK:STDOUT:   %Foo.type: type = generic_class_type @Foo [template]
 // CHECK:STDOUT:   %.3: type = tuple_type () [template]
 // CHECK:STDOUT:   %Foo.1: %Foo.type = struct_value () [template]
-// CHECK:STDOUT:   %Foo.2: type = class_type @Foo [template]
+// CHECK:STDOUT:   %Foo.2: type = class_type @Foo, @Foo(%a) [symbolic]
 // CHECK:STDOUT:   %.type: type = generic_class_type @.1 [template]
 // CHECK:STDOUT:   %.4: %.type = struct_value () [template]
-// CHECK:STDOUT:   %.5: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.5: type = class_type @.1, @.1(%a) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -706,14 +885,14 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:     %C.ref.loc6: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %.loc6: type = const_type %C [template = constants.%.2]
 // CHECK:STDOUT:     %a.loc6_11.1: %.2 = param a
-// CHECK:STDOUT:     %a.loc6_11.2: %.2 = bind_name a, %a.loc6_11.1
+// CHECK:STDOUT:     %a.loc6_11.2: %.2 = bind_symbolic_name a 0, %a.loc6_11.1 [symbolic = @Foo.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.decl: %.type = class_decl @.1 [template = constants.%.4] {
 // CHECK:STDOUT:     %C.ref.loc18: type = name_ref C, %C.decl [template = constants.%C]
-// CHECK:STDOUT:     %.loc18_21: type = const_type %C [template = constants.%.2]
-// CHECK:STDOUT:     %.loc18_14: type = const_type %.2 [template = constants.%.2]
+// CHECK:STDOUT:     %.loc18_22: type = const_type %C [template = constants.%.2]
+// CHECK:STDOUT:     %.loc18_15: type = const_type %.2 [template = constants.%.2]
 // CHECK:STDOUT:     %a.loc18_11.1: %.2 = param a
-// CHECK:STDOUT:     %a.loc18_11.2: %.2 = bind_name a, %a.loc18_11.1
+// CHECK:STDOUT:     %a.loc18_11.2: %.2 = bind_symbolic_name a 0, %a.loc18_11.1 [symbolic = @.1.%a (constants.%a)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -722,11 +901,29 @@ fn Base.F[addr self: Base*]() {
 // CHECK:STDOUT:   .Self = constants.%C
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Foo;
+// CHECK:STDOUT: generic class @Foo(file.%a.loc6_11.2: %.2) {
+// CHECK:STDOUT:   %a: %.2 = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%.5
+// CHECK:STDOUT:   class;
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @.1(file.%a.loc18_11.2: %.2) {
+// CHECK:STDOUT:   %a: %.2 = bind_symbolic_name a 0 [symbolic = %a (constants.%a)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%.5
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Foo(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @.1(constants.%a) {
+// CHECK:STDOUT:   %a => constants.%a
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_self_type.carbon

+ 114 - 35
toolchain/check/testdata/interface/no_prelude/generic_vs_params.carbon

@@ -8,6 +8,10 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interface/no_prelude/generic_vs_params.carbon
 
+// --- params.carbon
+
+library "params";
+
 interface NotGenericNoParams {}
 interface NotGenericButParams() {}
 interface GenericAndParams(T:! type) {}
@@ -24,7 +28,26 @@ impl X as GenericAndParams(X) {}
 impl X as C(X).GenericNoParams {}
 impl X as C(X).GenericAndParams(X) {}
 
-// CHECK:STDOUT: --- generic_vs_params.carbon
+// --- fail_non_generic_implicit_params.carbon
+
+library "non_generic_implicit_params";
+
+// CHECK:STDERR: fail_non_generic_implicit_params.carbon:[[@LINE+4]]:13: ERROR: Parameters of generic types must be constant.
+// CHECK:STDERR: interface A[T: type]() {}
+// CHECK:STDERR:             ^
+// CHECK:STDERR:
+interface A[T: type]() {}
+
+// --- fail_non_generic_params.carbon
+
+library "non_generic_params";
+
+// CHECK:STDERR: fail_non_generic_params.carbon:[[@LINE+3]]:13: ERROR: Parameters of generic types must be constant.
+// CHECK:STDERR: interface A(T: type) {}
+// CHECK:STDERR:             ^
+interface A(T: type) {}
+
+// CHECK:STDOUT: --- params.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %.1: type = interface_type @NotGenericNoParams [template]
@@ -71,50 +94,50 @@ impl X as C(X).GenericAndParams(X) {}
 // CHECK:STDOUT:   %NotGenericNoParams.decl: type = interface_decl @NotGenericNoParams [template = constants.%.1] {}
 // CHECK:STDOUT:   %NotGenericButParams.decl: %NotGenericButParams.type = interface_decl @NotGenericButParams [template = constants.%NotGenericButParams] {}
 // CHECK:STDOUT:   %GenericAndParams.decl: %GenericAndParams.type.1 = interface_decl @GenericAndParams.1 [template = constants.%GenericAndParams.1] {
-// CHECK:STDOUT:     %T.loc13_28.1: type = param T
-// CHECK:STDOUT:     %T.loc13_28.2: type = bind_symbolic_name T 0, %T.loc13_28.1 [symbolic = @GenericAndParams.1.%T (constants.%T)]
+// CHECK:STDOUT:     %T.loc6_28.1: type = param T
+// CHECK:STDOUT:     %T.loc6_28.2: type = bind_symbolic_name T 0, %T.loc6_28.1 [symbolic = @GenericAndParams.1.%T (constants.%T)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: %C.type = class_decl @C [template = constants.%C.1] {
-// CHECK:STDOUT:     %T.loc15_9.1: type = param T
-// CHECK:STDOUT:     %T.loc15_9.2: type = bind_symbolic_name T 0, %T.loc15_9.1 [symbolic = @C.%T (constants.%T)]
+// CHECK:STDOUT:     %T.loc8_9.1: type = param T
+// CHECK:STDOUT:     %T.loc8_9.2: type = bind_symbolic_name T 0, %T.loc8_9.1 [symbolic = @C.%T (constants.%T)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %X.decl: type = class_decl @X [template = constants.%X] {}
 // CHECK:STDOUT:   impl_decl @impl.1 {
-// CHECK:STDOUT:     %X.ref.loc21: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %X.ref.loc14: type = name_ref X, %X.decl [template = constants.%X]
 // CHECK:STDOUT:     %NotGenericNoParams.ref: type = name_ref NotGenericNoParams, %NotGenericNoParams.decl [template = constants.%.1]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc22_31.1: type = value_of_initializer %.loc22_30 [template = constants.%.3]
-// CHECK:STDOUT:   %.loc22_31.2: type = converted %.loc22_30, %.loc22_31.1 [template = constants.%.3]
+// CHECK:STDOUT:   %.loc15_31.1: type = value_of_initializer %.loc15_30 [template = constants.%.3]
+// CHECK:STDOUT:   %.loc15_31.2: type = converted %.loc15_30, %.loc15_31.1 [template = constants.%.3]
 // CHECK:STDOUT:   impl_decl @impl.2 {
-// CHECK:STDOUT:     %X.ref.loc22: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %X.ref.loc15: type = name_ref X, %X.decl [template = constants.%X]
 // CHECK:STDOUT:     %NotGenericButParams.ref: %NotGenericButParams.type = name_ref NotGenericButParams, %NotGenericButParams.decl [template = constants.%NotGenericButParams]
-// CHECK:STDOUT:     %.loc22_30: init type = call %NotGenericButParams.ref() [template = constants.%.3]
+// CHECK:STDOUT:     %.loc15_30: init type = call %NotGenericButParams.ref() [template = constants.%.3]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc23_29.1: type = value_of_initializer %.loc23_27 [template = constants.%.10]
-// CHECK:STDOUT:   %.loc23_29.2: type = converted %.loc23_27, %.loc23_29.1 [template = constants.%.10]
+// CHECK:STDOUT:   %.loc16_29.1: type = value_of_initializer %.loc16_27 [template = constants.%.10]
+// CHECK:STDOUT:   %.loc16_29.2: type = converted %.loc16_27, %.loc16_29.1 [template = constants.%.10]
 // CHECK:STDOUT:   impl_decl @impl.3 {
-// CHECK:STDOUT:     %X.ref.loc23_6: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:     %GenericAndParams.ref.loc23: %GenericAndParams.type.1 = name_ref GenericAndParams, %GenericAndParams.decl [template = constants.%GenericAndParams.1]
-// CHECK:STDOUT:     %X.ref.loc23_28: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:     %.loc23_27: init type = call %GenericAndParams.ref.loc23(%X.ref.loc23_28) [template = constants.%.10]
+// CHECK:STDOUT:     %X.ref.loc16_6: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %GenericAndParams.ref.loc16: %GenericAndParams.type.1 = name_ref GenericAndParams, %GenericAndParams.decl [template = constants.%GenericAndParams.1]
+// CHECK:STDOUT:     %X.ref.loc16_28: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %.loc16_27: init type = call %GenericAndParams.ref.loc16(%X.ref.loc16_28) [template = constants.%.10]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   impl_decl @impl.4 {
-// CHECK:STDOUT:     %X.ref.loc24_6: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:     %C.ref.loc24: %C.type = name_ref C, %C.decl [template = constants.%C.1]
-// CHECK:STDOUT:     %X.ref.loc24_13: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:     %.loc24: init type = call %C.ref.loc24(%X.ref.loc24_13) [template = constants.%C.3]
+// CHECK:STDOUT:     %X.ref.loc17_6: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %C.ref.loc17: %C.type = name_ref C, %C.decl [template = constants.%C.1]
+// CHECK:STDOUT:     %X.ref.loc17_13: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %.loc17: init type = call %C.ref.loc17(%X.ref.loc17_13) [template = constants.%C.3]
 // CHECK:STDOUT:     %GenericNoParams.ref: type = name_ref GenericNoParams, @C.%GenericNoParams.decl [template = constants.%.5]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc25_34.1: type = value_of_initializer %.loc25_32 [template = constants.%.12]
-// CHECK:STDOUT:   %.loc25_34.2: type = converted %.loc25_32, %.loc25_34.1 [template = constants.%.12]
+// CHECK:STDOUT:   %.loc18_34.1: type = value_of_initializer %.loc18_32 [template = constants.%.12]
+// CHECK:STDOUT:   %.loc18_34.2: type = converted %.loc18_32, %.loc18_34.1 [template = constants.%.12]
 // CHECK:STDOUT:   impl_decl @impl.5 {
-// CHECK:STDOUT:     %X.ref.loc25_6: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:     %C.ref.loc25: %C.type = name_ref C, %C.decl [template = constants.%C.1]
-// CHECK:STDOUT:     %X.ref.loc25_13: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:     %.loc25_12: init type = call %C.ref.loc25(%X.ref.loc25_13) [template = constants.%C.3]
-// CHECK:STDOUT:     %GenericAndParams.ref.loc25: %GenericAndParams.type.2 = name_ref GenericAndParams, @C.%GenericAndParams.decl [template = constants.%GenericAndParams.2]
-// CHECK:STDOUT:     %X.ref.loc25_33: type = name_ref X, %X.decl [template = constants.%X]
-// CHECK:STDOUT:     %.loc25_32: init type = call %GenericAndParams.ref.loc25(%X.ref.loc25_33) [template = constants.%.12]
+// CHECK:STDOUT:     %X.ref.loc18_6: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %C.ref.loc18: %C.type = name_ref C, %C.decl [template = constants.%C.1]
+// CHECK:STDOUT:     %X.ref.loc18_13: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %.loc18_12: init type = call %C.ref.loc18(%X.ref.loc18_13) [template = constants.%C.3]
+// CHECK:STDOUT:     %GenericAndParams.ref.loc18: %GenericAndParams.type.2 = name_ref GenericAndParams, @C.%GenericAndParams.decl [template = constants.%GenericAndParams.2]
+// CHECK:STDOUT:     %X.ref.loc18_33: type = name_ref X, %X.decl [template = constants.%X]
+// CHECK:STDOUT:     %.loc18_32: init type = call %GenericAndParams.ref.loc18(%X.ref.loc18_33) [template = constants.%.12]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -134,7 +157,7 @@ impl X as C(X).GenericAndParams(X) {}
 // CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic interface @GenericAndParams.1(file.%T.loc13_28.2: type) {
+// CHECK:STDOUT: generic interface @GenericAndParams.1(file.%T.loc6_28.2: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
@@ -150,7 +173,7 @@ impl X as C(X).GenericAndParams(X) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic interface @GenericNoParams(file.%T.loc15_9.2: type) {
+// CHECK:STDOUT: generic interface @GenericNoParams(file.%T.loc8_9.2: type) {
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:   %.1: type = interface_type @GenericNoParams, @GenericNoParams(%T) [symbolic = %.1 (constants.%.6)]
@@ -165,7 +188,7 @@ impl X as C(X).GenericAndParams(X) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic interface @GenericAndParams.2(file.%T.loc15_9.2: type, @C.%U.loc17_30.2: type) {
+// CHECK:STDOUT: generic interface @GenericAndParams.2(file.%T.loc8_9.2: type, @C.%U.loc10_30.2: type) {
 // CHECK:STDOUT:   %U: type = bind_symbolic_name U 1 [symbolic = %U (constants.%U)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
@@ -217,7 +240,7 @@ impl X as C(X).GenericAndParams(X) {}
 // CHECK:STDOUT:   witness = %.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic class @C(file.%T.loc15_9.2: type) {
+// CHECK:STDOUT: generic class @C(file.%T.loc8_9.2: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
@@ -225,8 +248,8 @@ impl X as C(X).GenericAndParams(X) {}
 // CHECK:STDOUT:   class {
 // CHECK:STDOUT:     %GenericNoParams.decl: type = interface_decl @GenericNoParams [template = constants.%.5] {}
 // CHECK:STDOUT:     %GenericAndParams.decl: %GenericAndParams.type.2 = interface_decl @GenericAndParams.2 [template = constants.%GenericAndParams.2] {
-// CHECK:STDOUT:       %U.loc17_30.1: type = param U
-// CHECK:STDOUT:       %U.loc17_30.2: type = bind_symbolic_name U 1, %U.loc17_30.1 [symbolic = @GenericAndParams.2.%U (constants.%U)]
+// CHECK:STDOUT:       %U.loc10_30.1: type = param U
+// CHECK:STDOUT:       %U.loc10_30.2: type = bind_symbolic_name U 1, %U.loc10_30.1 [symbolic = @GenericAndParams.2.%U (constants.%U)]
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:
 // CHECK:STDOUT:   !members:
@@ -292,3 +315,59 @@ impl X as C(X).GenericAndParams(X) {}
 // CHECK:STDOUT:   %U => constants.%U
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_non_generic_implicit_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %A.type: type = generic_interface_type @A [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %A: %A.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = interface_type @A [template]
+// CHECK:STDOUT:   %Self: %.2 = bind_symbolic_name Self 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %A.decl: %A.type = interface_decl @A [template = constants.%A] {
+// CHECK:STDOUT:     %T.loc8_13.1: type = param T
+// CHECK:STDOUT:     %T.loc8_13.2: type = bind_name T, %T.loc8_13.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @A {
+// CHECK:STDOUT:   %Self: %.2 = bind_symbolic_name Self 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_non_generic_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %A.type: type = generic_interface_type @A [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %A: %A.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = interface_type @A [template]
+// CHECK:STDOUT:   %Self: %.2 = bind_symbolic_name Self 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %A.decl: %A.type = interface_decl @A [template = constants.%A] {
+// CHECK:STDOUT:     %T.loc7_13.1: type = param T
+// CHECK:STDOUT:     %T.loc7_13.2: type = bind_name T, %T.loc7_13.1
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @A {
+// CHECK:STDOUT:   %Self: %.2 = bind_symbolic_name Self 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 414 - 170
toolchain/check/testdata/interface/no_prelude/syntactic_merge.carbon


+ 1 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -177,6 +177,7 @@ CARBON_DIAGNOSTIC_KIND(InCallToFunction)
 CARBON_DIAGNOSTIC_KIND(InCallToFunctionParam)
 CARBON_DIAGNOSTIC_KIND(InCallToFunctionSelf)
 CARBON_DIAGNOSTIC_KIND(MissingObjectInMethodCall)
+CARBON_DIAGNOSTIC_KIND(GenericParamMustBeConstant)
 
 // Function declaration checking.
 CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypeDiffers)

Vissa filer visades inte eftersom för många filer har ändrats