Explorar el Código

Distinguish between whether an entity has its own parameter lists and whether it is generic. (#4191)

It's actually possible to get into all four combinations of having
parameter lists versus being generic:

- An entity nested within a generic, such as a member class, can be
generic even if it has no parameters.

- As a corner case, an entity with an *empty* parameter list has
parameter lists, but isn't a generic because it doesn't have any generic
parameters.
Richard Smith hace 1 año
padre
commit
f6ff5b11b5

+ 9 - 8
toolchain/check/eval.cpp

@@ -1109,7 +1109,7 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc,
       eval_context.insts().Get(call.callee_id).type_id());
   CARBON_KIND_SWITCH(type_inst) {
     case CARBON_KIND(SemIR::GenericClassType generic_class): {
-      auto specific_id = MakeSpecific(
+      auto specific_id = MakeSpecificIfGeneric(
           eval_context.context(),
           eval_context.classes().Get(generic_class.class_id).generic_id,
           call.args_id);
@@ -1121,11 +1121,12 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc,
           phase);
     }
     case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): {
-      auto specific_id = MakeSpecific(eval_context.context(),
-                                      eval_context.interfaces()
-                                          .Get(generic_interface.interface_id)
-                                          .generic_id,
-                                      call.args_id);
+      auto specific_id =
+          MakeSpecificIfGeneric(eval_context.context(),
+                                eval_context.interfaces()
+                                    .Get(generic_interface.interface_id)
+                                    .generic_id,
+                                call.args_id);
       return MakeConstantResult(
           eval_context.context(),
           SemIR::InterfaceType{.type_id = call.type_id,
@@ -1286,7 +1287,7 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id,
     case CARBON_KIND(SemIR::ClassDecl class_decl): {
       // If the class has generic parameters, we don't produce a class type, but
       // a callable whose return value is a class type.
-      if (eval_context.classes().Get(class_decl.class_id).is_generic()) {
+      if (eval_context.classes().Get(class_decl.class_id).has_parameters()) {
         return TransformIfFieldsAreConstant(
             eval_context, class_decl,
             [&](SemIR::ClassDecl result) {
@@ -1309,7 +1310,7 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id,
       // type, but a callable whose return value is an interface type.
       if (eval_context.interfaces()
               .Get(interface_decl.interface_id)
-              .is_generic()) {
+              .has_parameters()) {
         return TransformIfFieldsAreConstant(
             eval_context, interface_decl,
             [&](SemIR::InterfaceDecl result) {

+ 9 - 0
toolchain/check/generic.h

@@ -47,6 +47,15 @@ auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
 auto MakeSpecific(Context& context, SemIR::GenericId generic_id,
                   SemIR::InstBlockId args_id) -> SemIR::SpecificId;
 
+// Builds a new specific if the given generic is valid. Otherwise returns an
+// invalid specific.
+inline auto MakeSpecificIfGeneric(Context& context, SemIR::GenericId generic_id,
+                                  SemIR::InstBlockId args_id)
+    -> SemIR::SpecificId {
+  return generic_id.is_valid() ? MakeSpecific(context, generic_id, args_id)
+                               : SemIR::SpecificId::Invalid;
+}
+
 // Builds the specific that describes how the generic should refer to itself.
 // For example, for a generic `G(T:! type)`, this is the specific `G(T)`. For an
 // invalid `generic_id`, returns an invalid specific ID.

+ 8 - 12
toolchain/check/handle_class.cpp

@@ -236,7 +236,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
     // here. We should keep track of it even if the name is invalid.
     class_info.generic_id = FinishGenericDecl(context, class_decl_id);
     class_decl.class_id = context.classes().Add(class_info);
-    if (class_info.is_generic()) {
+    if (class_info.has_parameters()) {
       class_decl.type_id = context.GetGenericClassType(class_decl.class_id);
     }
   } else {
@@ -251,17 +251,13 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
     // TODO: Form this as part of building the definition, not as part of the
     // declaration.
     auto& class_info = context.classes().Get(class_decl.class_id);
-    if (class_info.is_generic()) {
-      auto specific_id =
-          context.generics().GetSelfSpecific(class_info.generic_id);
-      class_info.self_type_id = context.GetTypeIdForTypeConstant(
-          TryEvalInst(context, SemIR::InstId::Invalid,
-                      SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
-                                       .class_id = class_decl.class_id,
-                                       .specific_id = specific_id}));
-    } else {
-      class_info.self_type_id = context.GetTypeIdForTypeInst(class_decl_id);
-    }
+    auto specific_id =
+        context.generics().GetSelfSpecific(class_info.generic_id);
+    class_info.self_type_id = context.GetTypeIdForTypeConstant(
+        TryEvalInst(context, SemIR::InstId::Invalid,
+                    SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
+                                     .class_id = class_decl.class_id,
+                                     .specific_id = specific_id}));
   }
 
   if (!is_definition && context.IsImplFile() && !is_extern) {

+ 14 - 17
toolchain/check/handle_interface.cpp

@@ -97,7 +97,7 @@ static auto BuildInterfaceDecl(Context& context,
          .param_refs_id = name.params_id,
          .decl_id = interface_decl_id}};
     interface_decl.interface_id = context.interfaces().Add(interface_info);
-    if (interface_info.is_generic()) {
+    if (interface_info.has_parameters()) {
       interface_decl.type_id =
           context.GetGenericInterfaceType(interface_decl.interface_id);
     }
@@ -141,10 +141,9 @@ auto HandleParseNode(Context& context,
                                   interface_info.parent_scope_id);
   }
 
-  // Enter the interface scope.
-  context.scope_stack().Push(
-      interface_decl_id, interface_info.scope_id,
-      context.generics().GetSelfSpecific(interface_info.generic_id));
+  auto self_specific_id =
+      context.generics().GetSelfSpecific(interface_info.generic_id);
+
   StartGenericDefinition(context);
 
   context.inst_block_stack().Push();
@@ -155,18 +154,12 @@ auto HandleParseNode(Context& context,
 
   // Declare and introduce `Self`.
   if (!interface_info.is_defined()) {
-    SemIR::TypeId self_type_id = SemIR::TypeId::Invalid;
-    if (interface_info.is_generic()) {
-      auto specific_id =
-          context.generics().GetSelfSpecific(interface_info.generic_id);
-      self_type_id = context.GetTypeIdForTypeConstant(
-          TryEvalInst(context, SemIR::InstId::Invalid,
-                      SemIR::InterfaceType{.type_id = SemIR::TypeId::TypeType,
-                                           .interface_id = interface_id,
-                                           .specific_id = specific_id}));
-    } else {
-      self_type_id = context.GetTypeIdForTypeInst(interface_decl_id);
-    }
+    auto interface_type =
+        SemIR::InterfaceType{.type_id = SemIR::TypeId::TypeType,
+                             .interface_id = interface_id,
+                             .specific_id = self_specific_id};
+    SemIR::TypeId self_type_id = context.GetTypeIdForTypeConstant(
+        TryEvalInst(context, SemIR::InstId::Invalid, interface_type));
 
     // We model `Self` as a symbolic binding whose type is the interface.
     // Because there is no equivalent non-symbolic value, we use `Invalid` as
@@ -185,6 +178,10 @@ auto HandleParseNode(Context& context,
                                           interface_info.self_param_id);
   }
 
+  // Enter the interface scope.
+  context.scope_stack().Push(interface_decl_id, interface_info.scope_id,
+                             self_specific_id);
+
   // TODO: Handle the case where there's control flow in the interface body. For
   // example:
   //

+ 2 - 2
toolchain/check/import_ref.cpp

@@ -1137,7 +1137,7 @@ class ImportRefResolver {
          {.self_type_id = SemIR::TypeId::Invalid,
           .inheritance_kind = import_class.inheritance_kind}});
 
-    if (import_class.is_generic()) {
+    if (import_class.has_parameters()) {
       class_decl.type_id = context_.GetGenericClassType(class_decl.class_id);
     }
 
@@ -1498,7 +1498,7 @@ class ImportRefResolver {
         {GetIncompleteLocalEntityBase(interface_decl_id, import_interface),
          {}});
 
-    if (import_interface.is_generic()) {
+    if (import_interface.has_parameters()) {
       interface_decl.type_id =
           context_.GetGenericInterfaceType(interface_decl.interface_id);
     }

+ 241 - 0
toolchain/check/testdata/class/no_prelude/generic_vs_params.carbon

@@ -0,0 +1,241 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/no_prelude/generic_vs_params.carbon
+// 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
+
+class NotGenericNoParams {}
+class NotGenericButParams() {}
+class GenericAndParams(T:! type) {}
+
+class C(T:! type) {
+  class GenericNoParams {}
+  class GenericAndParams(U:! type) {}
+}
+
+class X {}
+
+var a: NotGenericNoParams = {};
+var b: NotGenericButParams() = {};
+var c: GenericAndParams(X) = {};
+var d: C(X).GenericNoParams = {};
+var e: C(X).GenericAndParams(X) = {};
+
+// CHECK:STDOUT: --- generic_vs_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %NotGenericNoParams: type = class_type @NotGenericNoParams [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %NotGenericButParams.type: type = generic_class_type @NotGenericButParams [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %NotGenericButParams.1: %NotGenericButParams.type = struct_value () [template]
+// CHECK:STDOUT:   %NotGenericButParams.2: type = class_type @NotGenericButParams [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %GenericAndParams.type.1: type = generic_class_type @GenericAndParams.1 [template]
+// CHECK:STDOUT:   %GenericAndParams.1: %GenericAndParams.type.1 = struct_value () [template]
+// CHECK:STDOUT:   %GenericAndParams.2: type = class_type @GenericAndParams.1, @GenericAndParams.1(%T) [symbolic]
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C, @C(%T) [symbolic]
+// CHECK:STDOUT:   %GenericNoParams.1: type = class_type @GenericNoParams [template]
+// CHECK:STDOUT:   %GenericNoParams.2: type = class_type @GenericNoParams, @GenericNoParams(%T) [symbolic]
+// CHECK:STDOUT:   %U: type = bind_symbolic_name U 1 [symbolic]
+// CHECK:STDOUT:   %GenericAndParams.type.2: type = generic_class_type @GenericAndParams.2 [template]
+// CHECK:STDOUT:   %GenericAndParams.3: %GenericAndParams.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %GenericAndParams.4: type = class_type @GenericAndParams.2, @GenericAndParams.2(%T, %U) [symbolic]
+// CHECK:STDOUT:   %X: type = class_type @X [template]
+// CHECK:STDOUT:   %.3: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %struct.1: %NotGenericNoParams = struct_value () [template]
+// CHECK:STDOUT:   %struct.2: %NotGenericButParams.2 = struct_value () [template]
+// CHECK:STDOUT:   %GenericAndParams.5: type = class_type @GenericAndParams.1, @GenericAndParams.1(%X) [template]
+// CHECK:STDOUT:   %struct.3: %GenericAndParams.5 = struct_value () [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, @C(%X) [template]
+// CHECK:STDOUT:   %struct.4: %GenericNoParams.1 = struct_value () [template]
+// CHECK:STDOUT:   %GenericAndParams.6: type = class_type @GenericAndParams.2, @GenericAndParams.2(%X) [template]
+// CHECK:STDOUT:   %struct.5: %GenericAndParams.6 = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .NotGenericNoParams = %NotGenericNoParams.decl
+// CHECK:STDOUT:     .NotGenericButParams = %NotGenericButParams.decl
+// CHECK:STDOUT:     .GenericAndParams = %GenericAndParams.decl
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .X = %X.decl
+// CHECK:STDOUT:     .a = %a
+// CHECK:STDOUT:     .b = %b
+// CHECK:STDOUT:     .c = %c
+// CHECK:STDOUT:     .d = %d
+// CHECK:STDOUT:     .e = %e
+// CHECK:STDOUT:   }
+// 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:   }
+// 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:   }
+// 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:   %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:   %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:   %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:   %e.var: ref %GenericAndParams.6 = var e
+// CHECK:STDOUT:   %e: ref %GenericAndParams.6 = bind_name e, %e.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @NotGenericNoParams {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%NotGenericNoParams
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @NotGenericButParams {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%NotGenericButParams.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @GenericAndParams.1(file.%T.loc13_24.2: type) {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%GenericAndParams.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @C(file.%T.loc15_9.2: type) {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// 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:     }
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%C.2
+// CHECK:STDOUT:     .GenericNoParams = %GenericNoParams.decl
+// CHECK:STDOUT:     .GenericAndParams = %GenericAndParams.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @GenericNoParams(file.%T.loc15_9.2: type) {
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%GenericNoParams.2
+// 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:   %U: type = bind_symbolic_name U 1 [symbolic = %U (constants.%U)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%GenericAndParams.4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @X {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%X
+// CHECK:STDOUT: }
+// 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:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.1(constants.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericNoParams(constants.%T) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.2(constants.%T, constants.%U) {
+// CHECK:STDOUT:   %U => constants.%U
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.1(constants.%X) {
+// CHECK:STDOUT:   %T => constants.%X
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%X) {
+// CHECK:STDOUT:   %T => constants.%X
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.2(constants.%X) {
+// CHECK:STDOUT:   %U => constants.%U
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 5 - 4
toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon

@@ -25,7 +25,8 @@ interface I {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %.1: type = interface_type @I [template]
 // CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 0 [symbolic]
-// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %C.1: type = class_type @C [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C, @C(%Self) [symbolic]
 // CHECK:STDOUT:   %.2: type = struct_type {} [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -52,7 +53,7 @@ interface I {
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @I {
 // CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 0 [symbolic = constants.%Self]
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C.1] {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = %Self
@@ -60,7 +61,7 @@ interface I {
 // CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl @impl: %C as %.1;
+// CHECK:STDOUT: impl @impl: %C.2 as %.1;
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic class @C(@I.%Self: %.1) {
 // CHECK:STDOUT: !definition:
@@ -71,7 +72,7 @@ interface I {
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:
 // CHECK:STDOUT:   !members:
-// CHECK:STDOUT:     .Self = constants.%C
+// CHECK:STDOUT:     .Self = constants.%C.2
 // CHECK:STDOUT:     has_error
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }

+ 19 - 10
toolchain/check/testdata/interface/fail_todo_define_default_fn_out_of_line.carbon

@@ -193,7 +193,8 @@ fn Interface.C.F[self: Self](U:! type, u: U) -> U { return u; }
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %.1: type = interface_type @Interface [template]
 // CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 0 [symbolic]
-// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %C.1: type = class_type @C [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C, @C(%Self) [symbolic]
 // CHECK:STDOUT:   %U: type = bind_symbolic_name U 1 [symbolic]
 // CHECK:STDOUT:   %F.type: type = fn_type @F, @C(%Self) [symbolic]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
@@ -223,9 +224,10 @@ fn Interface.C.F[self: Self](U:! type, u: U) -> U { return u; }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %Interface.decl: type = interface_decl @Interface [template = constants.%.1] {}
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [symbolic = constants.%F.1] {
-// CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%C [template = constants.%C]
-// CHECK:STDOUT:     %self.loc20_18.1: %C = param self
-// CHECK:STDOUT:     @F.%self: %C = bind_name self, %self.loc20_18.1
+// CHECK:STDOUT:     %.loc20: type = specific_constant constants.%C.2, @C(constants.%Self) [symbolic = constants.%C.2]
+// CHECK:STDOUT:     %Self.ref: type = name_ref Self, %.loc20 [symbolic = constants.%C.2]
+// CHECK:STDOUT:     %self.loc20_18.1: %C.2 = param self
+// CHECK:STDOUT:     @F.%self: %C.2 = bind_name self, %self.loc20_18.1
 // CHECK:STDOUT:     %U.loc20_30.1: type = param U
 // CHECK:STDOUT:     @F.%U.loc20: type = bind_symbolic_name U 1, %U.loc20_30.1 [symbolic = constants.%U]
 // CHECK:STDOUT:     %U.ref.loc20_43: type = name_ref U, @F.%U.loc20 [symbolic = constants.%U]
@@ -238,7 +240,7 @@ fn Interface.C.F[self: Self](U:! type, u: U) -> U { return u; }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @Interface {
 // CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 0 [symbolic = constants.%Self]
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C.1] {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = %Self
@@ -254,9 +256,10 @@ fn Interface.C.F[self: Self](U:! type, u: U) -> U { return u; }
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
 // CHECK:STDOUT:     %F.decl: @C.%F.type (%F.type) = fn_decl @F [symbolic = %F (constants.%F.1)] {
-// CHECK:STDOUT:       %Self.ref: type = name_ref Self, constants.%C [template = constants.%C]
-// CHECK:STDOUT:       %self.loc14_10.1: %C = param self
-// CHECK:STDOUT:       %self.loc14_10.2: %C = bind_name self, %self.loc14_10.1
+// CHECK:STDOUT:       %.loc14: type = specific_constant constants.%C.2, @C(constants.%Self) [symbolic = @F.%C (constants.%C.2)]
+// CHECK:STDOUT:       %Self.ref: type = name_ref Self, %.loc14 [symbolic = @F.%C (constants.%C.2)]
+// CHECK:STDOUT:       %self.loc14_10.1: @F.%C (%C.2) = param self
+// CHECK:STDOUT:       %self.loc14_10.2: @F.%C (%C.2) = bind_name self, %self.loc14_10.1
 // CHECK:STDOUT:       %U.loc14_22.1: type = param U
 // CHECK:STDOUT:       %U.loc14_22.2: type = bind_symbolic_name U 1, %U.loc14_22.1 [symbolic = @F.%U.1 (constants.%U)]
 // CHECK:STDOUT:       %U.ref.loc14_35: type = name_ref U, %U.loc14_22.2 [symbolic = @F.%U.1 (constants.%U)]
@@ -267,17 +270,19 @@ fn Interface.C.F[self: Self](U:! type, u: U) -> U { return u; }
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:
 // CHECK:STDOUT:   !members:
-// CHECK:STDOUT:     .Self = constants.%C
+// CHECK:STDOUT:     .Self = constants.%C.2
 // CHECK:STDOUT:     .F = %F.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @F(@Interface.%Self: %.1, @C.%U.loc14_22.2: type) {
+// CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 0 [symbolic = %Self (constants.%Self)]
+// CHECK:STDOUT:   %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C.2)]
 // CHECK:STDOUT:   %U.1: type = bind_symbolic_name U 1 [symbolic = %U.1 (constants.%U)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%self: %C](%U.loc20: type, %u: %U) -> %U {
+// CHECK:STDOUT:   fn[%self: %C.2](%U.loc20: type, %u: %U) -> %U {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %u.ref: @F.%U.1 (%U) = name_ref u, %u
 // CHECK:STDOUT:     return %u.ref
@@ -291,7 +296,11 @@ fn Interface.C.F[self: Self](U:! type, u: U) -> U { return u; }
 // CHECK:STDOUT:   %F => constants.%F.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(@F.%Self) {}
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%Self, constants.%U) {
+// CHECK:STDOUT:   %Self => constants.%Self
+// CHECK:STDOUT:   %C => constants.%C.2
 // CHECK:STDOUT:   %U.1 => constants.%U
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 17 - 14
toolchain/check/testdata/interface/no_prelude/fail_add_member_outside_definition.carbon

@@ -42,10 +42,11 @@ interface Outer {
 // CHECK:STDOUT:   %.3: type = interface_type @Outer [template]
 // CHECK:STDOUT:   %Self.2: %.3 = bind_symbolic_name Self 0 [symbolic]
 // CHECK:STDOUT:   %.4: type = interface_type @Inner [template]
-// CHECK:STDOUT:   %Self.3: %.4 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %.5: type = interface_type @Inner, @Inner(%Self.2) [symbolic]
+// CHECK:STDOUT:   %Self.3: %.5 = bind_symbolic_name Self 1 [symbolic]
 // CHECK:STDOUT:   %.type: type = fn_type @.1, @Inner(%Self.2) [symbolic]
-// CHECK:STDOUT:   %.5: %.type = struct_value () [symbolic]
 // CHECK:STDOUT:   %.6: %.type = struct_value () [symbolic]
+// CHECK:STDOUT:   %.7: %.type = struct_value () [symbolic]
 // CHECK:STDOUT:   %F.type.2: type = fn_type @F.2, @Inner(%Self.2) [symbolic]
 // CHECK:STDOUT:   %F.2: %F.type.2 = struct_value () [symbolic]
 // CHECK:STDOUT: }
@@ -82,14 +83,15 @@ interface Outer {
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic interface @Inner(@Outer.%Self: %.3) {
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %Self.2: %.4 = bind_symbolic_name Self 1 [symbolic = %Self.2 (constants.%Self.3)]
-// CHECK:STDOUT:   %Self.3: %.3 = bind_symbolic_name Self 0 [symbolic = %Self.3 (constants.%Self.2)]
-// CHECK:STDOUT:   %.type: type = fn_type @.1, @Inner(%Self.3) [symbolic = %.type (constants.%.type)]
-// CHECK:STDOUT:   %.1: @Inner.%.type (%.type) = struct_value () [symbolic = %.1 (constants.%.5)]
+// CHECK:STDOUT:   %Self.2: %.3 = bind_symbolic_name Self 0 [symbolic = %Self.2 (constants.%Self.2)]
+// CHECK:STDOUT:   %.1: type = interface_type @Inner, @Inner(%Self.2) [symbolic = %.1 (constants.%.5)]
+// CHECK:STDOUT:   %Self.3: %.5 = bind_symbolic_name Self 1 [symbolic = %Self.3 (constants.%Self.3)]
+// CHECK:STDOUT:   %.type: type = fn_type @.1, @Inner(%Self.2) [symbolic = %.type (constants.%.type)]
+// CHECK:STDOUT:   %.2: @Inner.%.type (%.type) = struct_value () [symbolic = %.2 (constants.%.6)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   interface {
-// CHECK:STDOUT:     %Self.1: %.4 = bind_symbolic_name Self 1 [symbolic = %Self.2 (constants.%Self.3)]
-// CHECK:STDOUT:     %.decl: @Inner.%.type (%.type) = fn_decl @.1 [symbolic = %.1 (constants.%.5)] {}
+// CHECK:STDOUT:     %Self.1: @Inner.%.1 (%.5) = bind_symbolic_name Self 1 [symbolic = %Self.3 (constants.%Self.3)]
+// CHECK:STDOUT:     %.decl: @Inner.%.type (%.type) = fn_decl @.1 [symbolic = %.2 (constants.%.6)] {}
 // CHECK:STDOUT:
 // CHECK:STDOUT:   !members:
 // CHECK:STDOUT:     .Self = %Self.1
@@ -107,12 +109,12 @@ interface Outer {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic fn @.1(@Outer.%Self: %.3, @Inner.%Self.1: %.4) {
+// CHECK:STDOUT: generic fn @.1(@Outer.%Self: %.3, @Inner.%Self.1: @Inner.%.1 (%.5)) {
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn();
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic fn @F.2(@Outer.%Self: %.3, @Inner.%Self.1: %.4) {
+// CHECK:STDOUT: generic fn @F.2(@Outer.%Self: %.3, @Inner.%Self.1: @Inner.%.1 (%.5)) {
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn();
 // CHECK:STDOUT: }
@@ -121,15 +123,16 @@ interface Outer {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Inner(constants.%Self.2) {
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %Self.2 => constants.%Self.3
-// CHECK:STDOUT:   %Self.3 => constants.%Self.2
+// CHECK:STDOUT:   %Self.2 => constants.%Self.2
+// CHECK:STDOUT:   %.1 => constants.%.5
+// CHECK:STDOUT:   %Self.3 => constants.%Self.3
 // CHECK:STDOUT:   %.type => constants.%.type
-// CHECK:STDOUT:   %.1 => constants.%.6
+// CHECK:STDOUT:   %.2 => constants.%.7
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @.1(constants.%Self.2, constants.%Self.3) {}
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @Inner(@Inner.%Self.3) {}
+// CHECK:STDOUT: specific @Inner(@Inner.%Self.2) {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F.2(constants.%Self.2, constants.%Self.3) {}
 // CHECK:STDOUT:

+ 280 - 0
toolchain/check/testdata/interface/no_prelude/generic_vs_params.carbon

@@ -0,0 +1,280 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interface/no_prelude/generic_vs_params.carbon
+// 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
+
+interface NotGenericNoParams {}
+interface NotGenericButParams() {}
+interface GenericAndParams(T:! type) {}
+
+class C(T:! type) {
+  interface GenericNoParams {}
+  interface GenericAndParams(U:! type) {}
+}
+
+class X {}
+impl X as NotGenericNoParams {}
+impl X as NotGenericButParams() {}
+impl X as GenericAndParams(X) {}
+impl X as C(X).GenericNoParams {}
+impl X as C(X).GenericAndParams(X) {}
+
+// CHECK:STDOUT: --- generic_vs_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.1: type = interface_type @NotGenericNoParams [template]
+// CHECK:STDOUT:   %Self.1: %.1 = bind_symbolic_name Self 0 [symbolic]
+// CHECK:STDOUT:   %NotGenericButParams.type: type = generic_interface_type @NotGenericButParams [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %NotGenericButParams: %NotGenericButParams.type = struct_value () [template]
+// CHECK:STDOUT:   %.3: type = interface_type @NotGenericButParams [template]
+// CHECK:STDOUT:   %Self.2: %.3 = bind_symbolic_name Self 0 [symbolic]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %GenericAndParams.type.1: type = generic_interface_type @GenericAndParams.1 [template]
+// CHECK:STDOUT:   %GenericAndParams.1: %GenericAndParams.type.1 = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = interface_type @GenericAndParams.1, @GenericAndParams.1(%T) [symbolic]
+// CHECK:STDOUT:   %Self.3: %.4 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %C.type: type = generic_class_type @C [template]
+// CHECK:STDOUT:   %C.1: %C.type = struct_value () [template]
+// CHECK:STDOUT:   %C.2: type = class_type @C, @C(%T) [symbolic]
+// CHECK:STDOUT:   %.5: type = interface_type @GenericNoParams [template]
+// CHECK:STDOUT:   %.6: type = interface_type @GenericNoParams, @GenericNoParams(%T) [symbolic]
+// CHECK:STDOUT:   %Self.4: %.6 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %U: type = bind_symbolic_name U 1 [symbolic]
+// CHECK:STDOUT:   %GenericAndParams.type.2: type = generic_interface_type @GenericAndParams.2 [template]
+// CHECK:STDOUT:   %GenericAndParams.2: %GenericAndParams.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.7: type = interface_type @GenericAndParams.2, @GenericAndParams.2(%T, %U) [symbolic]
+// CHECK:STDOUT:   %Self.5: %.7 = bind_symbolic_name Self 2 [symbolic]
+// CHECK:STDOUT:   %.8: type = struct_type {} [template]
+// CHECK:STDOUT:   %X: type = class_type @X [template]
+// CHECK:STDOUT:   %.9: <witness> = interface_witness () [template]
+// CHECK:STDOUT:   %.10: type = interface_type @GenericAndParams.1, @GenericAndParams.1(%X) [template]
+// CHECK:STDOUT:   %C.3: type = class_type @C, @C(%X) [template]
+// CHECK:STDOUT:   %.11: type = ptr_type %.8 [template]
+// CHECK:STDOUT:   %.12: type = interface_type @GenericAndParams.2, @GenericAndParams.2(%X) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .NotGenericNoParams = %NotGenericNoParams.decl
+// CHECK:STDOUT:     .NotGenericButParams = %NotGenericButParams.decl
+// CHECK:STDOUT:     .GenericAndParams = %GenericAndParams.decl
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .X = %X.decl
+// CHECK:STDOUT:   }
+// 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:   }
+// 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:   }
+// 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:     %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:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %X.ref.loc22: 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:   }
+// 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:   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:   }
+// 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:     %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:   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:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @NotGenericNoParams {
+// CHECK:STDOUT:   %Self: %.1 = 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 @NotGenericButParams {
+// CHECK:STDOUT:   %Self: %.3 = 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: generic interface @GenericAndParams.1(file.%T.loc13_28.2: type) {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.1: type = interface_type @GenericAndParams.1, @GenericAndParams.1(%T) [symbolic = %.1 (constants.%.4)]
+// CHECK:STDOUT:   %Self.2: %.4 = bind_symbolic_name Self 1 [symbolic = %Self.2 (constants.%Self.3)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:     %Self.1: @GenericAndParams.1.%.1 (%.4) = bind_symbolic_name Self 1 [symbolic = %Self.2 (constants.%Self.3)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = %Self.1
+// CHECK:STDOUT:     witness = ()
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @GenericNoParams(file.%T.loc15_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)]
+// CHECK:STDOUT:   %Self.2: %.6 = bind_symbolic_name Self 1 [symbolic = %Self.2 (constants.%Self.4)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:     %Self.1: @GenericNoParams.%.1 (%.6) = bind_symbolic_name Self 1 [symbolic = %Self.2 (constants.%Self.4)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = %Self.1
+// CHECK:STDOUT:     witness = ()
+// 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:   %U: type = bind_symbolic_name U 1 [symbolic = %U (constants.%U)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:   %.1: type = interface_type @GenericAndParams.2, @GenericAndParams.2(%T, %U) [symbolic = %.1 (constants.%.7)]
+// CHECK:STDOUT:   %Self.2: %.7 = bind_symbolic_name Self 2 [symbolic = %Self.2 (constants.%Self.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:     %Self.1: @GenericAndParams.2.%.1 (%.7) = bind_symbolic_name Self 2 [symbolic = %Self.2 (constants.%Self.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = %Self.1
+// CHECK:STDOUT:     witness = ()
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: %X as %.1 {
+// CHECK:STDOUT:   %.1: <witness> = interface_witness () [template = constants.%.9]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: %X as %.3 {
+// CHECK:STDOUT:   %.1: <witness> = interface_witness () [template = constants.%.9]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.3: %X as %.10 {
+// CHECK:STDOUT:   %.1: <witness> = interface_witness () [template = constants.%.9]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.4: %X as %.5 {
+// CHECK:STDOUT:   %.1: <witness> = interface_witness () [template = constants.%.9]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.5: %X as %.12 {
+// CHECK:STDOUT:   %.1: <witness> = interface_witness () [template = constants.%.9]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @C(file.%T.loc15_9.2: type) {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// 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:     }
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%C.2
+// CHECK:STDOUT:     .GenericNoParams = %GenericNoParams.decl
+// CHECK:STDOUT:     .GenericAndParams = %GenericAndParams.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @X {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%X
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.1(constants.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.1(@GenericAndParams.1.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericNoParams(constants.%T) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericNoParams(@GenericNoParams.%T) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.2(constants.%T, constants.%U) {
+// CHECK:STDOUT:   %U => constants.%U
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.2(@GenericAndParams.2.%T, @GenericAndParams.2.%U) {
+// CHECK:STDOUT:   %U => constants.%U
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.1(constants.%X) {
+// CHECK:STDOUT:   %T => constants.%X
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @C(constants.%X) {
+// CHECK:STDOUT:   %T => constants.%X
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericAndParams.2(constants.%X) {
+// CHECK:STDOUT:   %U => constants.%U
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 2 - 2
toolchain/sem_ir/entity_with_params_base.h

@@ -54,8 +54,8 @@ struct EntityWithParamsBase {
     return definition_id.is_valid() ? definition_id : decl_id;
   }
 
-  // Determines whether this is a generic entity.
-  auto is_generic() const -> bool {
+  // Determines whether this entity has any parameter lists.
+  auto has_parameters() const -> bool {
     return implicit_param_refs_id.is_valid() || param_refs_id.is_valid();
   }