Forráskód Böngészése

Build Generic objects for generic classes and interfaces. (#4086)

In `ClassType`s and `InterfaceType`s, track a `GenericInstanceId` for
the instance rather than just the argument list.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Richard Smith 1 éve
szülő
commit
19c5596fd8
78 módosított fájl, 453 hozzáadás és 180 törlés
  1. 2 0
      toolchain/check/BUILD
  2. 52 10
      toolchain/check/eval.cpp
  3. 43 0
      toolchain/check/generic.cpp
  4. 37 0
      toolchain/check/generic.h
  5. 14 2
      toolchain/check/handle_class.cpp
  6. 11 9
      toolchain/check/handle_function.cpp
  7. 13 3
      toolchain/check/handle_interface.cpp
  8. 52 7
      toolchain/check/import_ref.cpp
  9. 1 1
      toolchain/check/testdata/array/generic_empty.carbon
  10. 1 1
      toolchain/check/testdata/builtins/int/make_type_signed.carbon
  11. 1 1
      toolchain/check/testdata/builtins/int/make_type_unsigned.carbon
  12. 4 3
      toolchain/check/testdata/class/fail_generic_method.carbon
  13. 4 3
      toolchain/check/testdata/class/generic/basic.carbon
  14. 8 4
      toolchain/check/testdata/class/generic/call.carbon
  15. 3 2
      toolchain/check/testdata/class/generic/fail_todo_use.carbon
  16. 7 4
      toolchain/check/testdata/class/generic/import.carbon
  17. 3 2
      toolchain/check/testdata/class/generic/member_inline.carbon
  18. 20 14
      toolchain/check/testdata/class/generic/member_out_of_line.carbon
  19. 20 10
      toolchain/check/testdata/class/generic/redeclare.carbon
  20. 3 2
      toolchain/check/testdata/class/generic_method.carbon
  21. 1 1
      toolchain/check/testdata/eval/fail_symbolic.carbon
  22. 1 1
      toolchain/check/testdata/eval/symbolic.carbon
  23. 1 1
      toolchain/check/testdata/function/builtin/method.carbon
  24. 1 1
      toolchain/check/testdata/function/builtin/no_prelude/call_from_operator.carbon
  25. 1 1
      toolchain/check/testdata/function/generic/fail_todo_param_in_type.carbon
  26. 1 1
      toolchain/check/testdata/function/generic/no_prelude/fail_type_param_mismatch.carbon
  27. 1 1
      toolchain/check/testdata/function/generic/no_prelude/type_param.carbon
  28. 1 1
      toolchain/check/testdata/function/generic/no_prelude/type_param_scope.carbon
  29. 7 7
      toolchain/check/testdata/function/generic/redeclare.carbon
  30. 2 2
      toolchain/check/testdata/impl/compound.carbon
  31. 1 1
      toolchain/check/testdata/impl/extend_impl.carbon
  32. 1 1
      toolchain/check/testdata/impl/fail_call_invalid.carbon
  33. 4 3
      toolchain/check/testdata/impl/fail_extend_impl_forall.carbon
  34. 2 1
      toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon
  35. 1 1
      toolchain/check/testdata/impl/fail_impl_as_scope.carbon
  36. 3 3
      toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon
  37. 1 1
      toolchain/check/testdata/impl/impl_as.carbon
  38. 2 2
      toolchain/check/testdata/impl/impl_forall.carbon
  39. 1 1
      toolchain/check/testdata/impl/lookup/alias.carbon
  40. 1 1
      toolchain/check/testdata/impl/lookup/fail_alias_impl_not_found.carbon
  41. 1 1
      toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon
  42. 1 1
      toolchain/check/testdata/impl/lookup/import.carbon
  43. 1 1
      toolchain/check/testdata/impl/lookup/instance_method.carbon
  44. 1 1
      toolchain/check/testdata/impl/lookup/no_prelude/import.carbon
  45. 1 1
      toolchain/check/testdata/impl/no_prelude/basic.carbon
  46. 1 1
      toolchain/check/testdata/impl/no_prelude/import_self.carbon
  47. 1 1
      toolchain/check/testdata/impl/no_prelude/self_in_class.carbon
  48. 2 2
      toolchain/check/testdata/impl/no_prelude/self_in_signature.carbon
  49. 2 2
      toolchain/check/testdata/interface/fail_todo_define_default_fn_inline.carbon
  50. 2 2
      toolchain/check/testdata/interface/fail_todo_define_default_fn_out_of_line.carbon
  51. 1 1
      toolchain/check/testdata/interface/no_prelude/as_type_of_type.carbon
  52. 1 1
      toolchain/check/testdata/interface/no_prelude/basic.carbon
  53. 1 1
      toolchain/check/testdata/interface/no_prelude/default_fn.carbon
  54. 4 3
      toolchain/check/testdata/interface/no_prelude/fail_add_member_outside_definition.carbon
  55. 8 4
      toolchain/check/testdata/interface/no_prelude/fail_generic_redeclaration.carbon
  56. 2 2
      toolchain/check/testdata/interface/no_prelude/fail_lookup_undefined.carbon
  57. 1 1
      toolchain/check/testdata/interface/no_prelude/fail_member_lookup.carbon
  58. 2 2
      toolchain/check/testdata/interface/no_prelude/fail_redeclare_member.carbon
  59. 3 3
      toolchain/check/testdata/interface/no_prelude/fail_todo_facet_lookup.carbon
  60. 4 3
      toolchain/check/testdata/interface/no_prelude/fail_todo_generic_default_fn.carbon
  61. 2 2
      toolchain/check/testdata/interface/no_prelude/fail_todo_modifiers.carbon
  62. 13 9
      toolchain/check/testdata/interface/no_prelude/generic.carbon
  63. 2 2
      toolchain/check/testdata/interface/no_prelude/generic_binding_after_assoc_const.carbon
  64. 3 2
      toolchain/check/testdata/interface/no_prelude/generic_import.carbon
  65. 2 2
      toolchain/check/testdata/interface/no_prelude/import.carbon
  66. 1 1
      toolchain/check/testdata/interface/no_prelude/self.carbon
  67. 2 2
      toolchain/check/testdata/interface/todo_define_not_default.carbon
  68. 1 1
      toolchain/check/testdata/namespace/fail_params.carbon
  69. 4 2
      toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon
  70. 1 1
      toolchain/check/testdata/return/fail_let_in_type.carbon
  71. 2 1
      toolchain/check/testdata/struct/import.carbon
  72. 2 1
      toolchain/check/testdata/tuples/import.carbon
  73. 2 0
      toolchain/sem_ir/class.h
  74. 26 5
      toolchain/sem_ir/formatter.cpp
  75. 10 0
      toolchain/sem_ir/generic.h
  76. 3 3
      toolchain/sem_ir/id_kind.h
  77. 2 0
      toolchain/sem_ir/interface.h
  78. 2 2
      toolchain/sem_ir/typed_insts.h

+ 2 - 0
toolchain/check/BUILD

@@ -20,6 +20,7 @@ cc_library(
         "decl_name_stack.cpp",
         "eval.cpp",
         "function.cpp",
+        "generic.cpp",
         "import_ref.cpp",
         "inst_block_stack.cpp",
         "merge.cpp",
@@ -36,6 +37,7 @@ cc_library(
         "diagnostic_helpers.h",
         "eval.h",
         "function.h",
+        "generic.h",
         "import_ref.h",
         "inst_block_stack.h",
         "keyword_modifier_set.h",

+ 52 - 10
toolchain/check/eval.cpp

@@ -15,6 +15,14 @@
 
 namespace Carbon::Check {
 
+static auto MakeGenericInstance(Context& context, SemIR::GenericId generic_id,
+                                SemIR::InstBlockId args_id)
+    -> SemIR::GenericInstanceId {
+  auto instance_id = context.generic_instances().GetOrAdd(generic_id, args_id);
+  // TODO: Perform substitution into the generic declaration if needed.
+  return instance_id;
+}
+
 namespace {
 // The evaluation phase for an expression, computed by evaluation. These are
 // ordered so that the phase of an expression is the numerically highest phase
@@ -177,6 +185,27 @@ static auto GetConstantValue(Context& context, SemIR::TypeBlockId type_block_id,
   return type_block_id;
 }
 
+// The constant value of a generic instance is the generic instance with the
+// corresponding constant values for its arguments.
+static auto GetConstantValue(Context& context,
+                             SemIR::GenericInstanceId instance_id, Phase* phase)
+    -> SemIR::GenericInstanceId {
+  if (!instance_id.is_valid()) {
+    return SemIR::GenericInstanceId::Invalid;
+  }
+
+  const auto& instance = context.generic_instances().Get(instance_id);
+  auto args_id = GetConstantValue(context, instance.args_id, phase);
+  if (!args_id.is_valid()) {
+    return SemIR::GenericInstanceId::Invalid;
+  }
+
+  if (args_id == instance.args_id) {
+    return instance_id;
+  }
+  return MakeGenericInstance(context, instance.generic_id, args_id);
+}
+
 // Replaces the specified field of the given typed instruction with its constant
 // value, if it has constant phase. Returns true on success, false if the value
 // has runtime phase.
@@ -900,22 +929,32 @@ static auto MakeConstantForCall(Context& context, SemIRLoc loc,
   auto type_inst =
       context.types().GetAsInst(context.insts().Get(call.callee_id).type_id());
   CARBON_KIND_SWITCH(type_inst) {
-    case CARBON_KIND(SemIR::GenericClassType generic_class):
+    case CARBON_KIND(SemIR::GenericClassType generic_class): {
+      auto instance_id = MakeGenericInstance(
+          context, context.classes().Get(generic_class.class_id).generic_id,
+          call.args_id);
       return MakeConstantResult(
           context,
           SemIR::ClassType{.type_id = call.type_id,
                            .class_id = generic_class.class_id,
-                           .args_id = call.args_id},
+                           .instance_id = instance_id},
           phase);
-    case CARBON_KIND(SemIR::GenericInterfaceType generic_interface):
+    }
+    case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): {
+      auto instance_id = MakeGenericInstance(
+          context,
+          context.interfaces().Get(generic_interface.interface_id).generic_id,
+          call.args_id);
       return MakeConstantResult(
           context,
           SemIR::InterfaceType{.type_id = call.type_id,
                                .interface_id = generic_interface.interface_id,
-                               .args_id = call.args_id},
+                               .instance_id = instance_id},
           phase);
-    default:
+    }
+    default: {
       return SemIR::ConstantId::NotConstant;
+    }
   }
 }
 
@@ -975,10 +1014,10 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
                                         &SemIR::BoundMethod::function_id);
     case SemIR::ClassType::Kind:
       return RebuildIfFieldsAreConstant(context, inst,
-                                        &SemIR::ClassType::args_id);
+                                        &SemIR::ClassType::instance_id);
     case SemIR::InterfaceType::Kind:
       return RebuildIfFieldsAreConstant(context, inst,
-                                        &SemIR::InterfaceType::args_id);
+                                        &SemIR::InterfaceType::instance_id);
     case SemIR::InterfaceWitness::Kind:
       return RebuildIfFieldsAreConstant(context, inst,
                                         &SemIR::InterfaceWitness::elements_id);
@@ -1065,7 +1104,8 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
       return MakeConstantResult(
           context,
           SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
-                           .class_id = class_decl.class_id},
+                           .class_id = class_decl.class_id,
+                           .instance_id = SemIR::GenericInstanceId::Invalid},
           Phase::Template);
     }
     case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
@@ -1081,8 +1121,10 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
       // A non-generic interface declaration evaluates to the interface type.
       return MakeConstantResult(
           context,
-          SemIR::InterfaceType{.type_id = SemIR::TypeId::TypeType,
-                               .interface_id = interface_decl.interface_id},
+          SemIR::InterfaceType{
+              .type_id = SemIR::TypeId::TypeType,
+              .interface_id = interface_decl.interface_id,
+              .instance_id = SemIR::GenericInstanceId::Invalid},
           Phase::Template);
     }
 

+ 43 - 0
toolchain/check/generic.cpp

@@ -0,0 +1,43 @@
+// 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
+
+#include "toolchain/check/generic.h"
+
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+auto StartGenericDecl(Context& /*context*/) -> void {
+  // TODO: Start tracking the contents of this declaration.
+}
+
+auto StartGenericDefinition(Context& /*context*/,
+                            SemIR::GenericId /*generic_id*/) -> void {
+  // TODO: Start tracking the contents of this definition.
+}
+
+auto FinishGenericDecl(Context& context, SemIR::InstId decl_id)
+    -> SemIR::GenericId {
+  if (context.scope_stack().compile_time_binding_stack().empty()) {
+    return SemIR::GenericId::Invalid;
+  }
+
+  auto bindings_id = context.inst_blocks().Add(
+      context.scope_stack().compile_time_binding_stack());
+  return context.generics().Add(
+      SemIR::Generic{.decl_id = decl_id, .bindings_id = bindings_id});
+}
+
+auto FinishGenericRedecl(Context& /*context*/, SemIR::InstId /*decl_id*/,
+                         SemIR::GenericId /*generic_id*/) -> void {
+  // TODO: Compare contents of this declaration with the existing one on the
+  // generic.
+}
+
+auto FinishGenericDefinition(Context& /*context*/,
+                             SemIR::GenericId /*generic_id*/) -> void {
+  // TODO: Track contents of this generic definition.
+}
+
+}  // namespace Carbon::Check

+ 37 - 0
toolchain/check/generic.h

@@ -0,0 +1,37 @@
+// 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
+
+#ifndef CARBON_TOOLCHAIN_CHECK_GENERIC_H_
+#define CARBON_TOOLCHAIN_CHECK_GENERIC_H_
+
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+// Start processing a declaration or definition that might be a generic entity.
+auto StartGenericDecl(Context& /*context*/) -> void;
+
+// Start processing a declaration or definition that might be a generic entity.
+auto StartGenericDefinition(Context& /*context*/,
+                            SemIR::GenericId /*generic_id*/) -> void;
+
+// Finish processing a potentially generic declaration and produce a
+// corresponding generic object. Returns SemIR::GenericId::Invalid if this
+// declaration is not actually generic.
+auto FinishGenericDecl(Context& context, SemIR::InstId decl_id)
+    -> SemIR::GenericId;
+
+// Merge a redeclaration of an entity that might be a generic into the original
+// declaration.
+auto FinishGenericRedecl(Context& context, SemIR::InstId decl_id,
+                         SemIR::GenericId generic_id) -> void;
+
+// Finish processing a potentially generic definition.
+auto FinishGenericDefinition(Context& context, SemIR::GenericId generic_id)
+    -> void;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_GENERIC_H_

+ 14 - 2
toolchain/check/handle_class.cpp

@@ -7,6 +7,7 @@
 #include "toolchain/check/convert.h"
 #include "toolchain/check/decl_name_stack.h"
 #include "toolchain/check/eval.h"
+#include "toolchain/check/generic.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/merge.h"
 #include "toolchain/check/modifiers.h"
@@ -38,6 +39,8 @@ auto HandleClassIntroducer(Context& context, Parse::ClassIntroducerId node_id)
   // Optional modifiers and the name follow.
   context.decl_introducer_state_stack().Push<Lex::TokenKind::Class>();
   context.decl_name_stack().PushScopeAndStartName();
+  // This class is potentially generic.
+  StartGenericDecl(context);
   return true;
 }
 
@@ -221,6 +224,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
   SemIR::Class class_info = {
       .name_id = name_context.name_id_for_new_inst(),
       .parent_scope_id = name_context.parent_scope_id_for_new_inst(),
+      .generic_id = SemIR::GenericId::Invalid,
       .implicit_param_refs_id = name.implicit_params_id,
       .param_refs_id = name.params_id,
       // `.self_type_id` depends on the ClassType, so is set below.
@@ -238,10 +242,13 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
     // TODO: If this is an invalid redeclaration of a non-class entity or there
     // was an error in the qualifier, we will have lost track of the class name
     // 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()) {
       class_decl.type_id = context.GetGenericClassType(class_decl.class_id);
     }
+  } else {
+    FinishGenericRedecl(context, class_decl_id, class_info.generic_id);
   }
 
   // Write the class ID into the ClassDecl.
@@ -253,11 +260,13 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
     // declaration.
     auto& class_info = context.classes().Get(class_decl.class_id);
     if (class_info.is_generic()) {
-      // TODO: Pass in the generic arguments once we can represent them.
+      // TODO: Build generic arguments representing the parameters.
+      auto instance_id = SemIR::GenericInstanceId::Invalid;
       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}));
+                                       .class_id = class_decl.class_id,
+                                       .instance_id = instance_id}));
     } else {
       class_info.self_type_id = context.GetTypeIdForTypeInst(class_decl_id);
     }
@@ -291,6 +300,7 @@ auto HandleClassDefinitionStart(Context& context,
 
   // Enter the class scope.
   context.scope_stack().Push(class_decl_id, class_info.scope_id);
+  StartGenericDefinition(context, class_info.generic_id);
 
   // Introduce `Self`.
   context.name_scopes().AddRequiredName(
@@ -608,6 +618,8 @@ auto HandleClassDefinition(Context& context,
     class_info.object_repr_id = context.GetStructType(fields_id);
   }
 
+  FinishGenericDefinition(context, class_info.generic_id);
+
   // The decl_name_stack and scopes are popped by `ProcessNodeIds`.
   return true;
 }

+ 11 - 9
toolchain/check/handle_function.cpp

@@ -8,6 +8,7 @@
 #include "toolchain/check/decl_introducer_state.h"
 #include "toolchain/check/decl_name_stack.h"
 #include "toolchain/check/function.h"
+#include "toolchain/check/generic.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/interface.h"
 #include "toolchain/check/merge.h"
@@ -16,7 +17,6 @@
 #include "toolchain/sem_ir/builtin_function_kind.h"
 #include "toolchain/sem_ir/entry_point.h"
 #include "toolchain/sem_ir/function.h"
-#include "toolchain/sem_ir/generic.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
@@ -32,6 +32,8 @@ auto HandleFunctionIntroducer(Context& context,
   // Optional modifiers and the name follow.
   context.decl_introducer_state_stack().Push<Lex::TokenKind::Fn>();
   context.decl_name_stack().PushScopeAndStartName();
+  // The function is potentially generic.
+  StartGenericDecl(context);
   return true;
 }
 
@@ -264,16 +266,10 @@ static auto BuildFunctionDecl(Context& context,
 
   // Create a new function if this isn't a valid redeclaration.
   if (!function_decl.function_id.is_valid()) {
-    // For a generic function, build the corresponding Generic entity.
-    if (!context.scope_stack().compile_time_binding_stack().empty()) {
-      function_info.generic_id = context.generics().Add(SemIR::Generic{
-          .decl_id = decl_id,
-          .bindings_id = context.inst_blocks().Add(
-              context.scope_stack().compile_time_binding_stack())});
-    }
-
+    function_info.generic_id = FinishGenericDecl(context, decl_id);
     function_decl.function_id = context.functions().Add(function_info);
   } else {
+    FinishGenericRedecl(context, decl_id, function_info.generic_id);
     // TODO: Validate that the redeclaration doesn't set an access modifier.
   }
   function_decl.type_id = context.GetFunctionType(function_decl.function_id);
@@ -342,6 +338,7 @@ static auto HandleFunctionDefinitionAfterSignature(
   context.return_scope_stack().push_back({.decl_id = decl_id});
   context.inst_block_stack().Push();
   context.scope_stack().Push(decl_id);
+  StartGenericDefinition(context, function.generic_id);
   context.AddCurrentCodeBlockToFunction();
 
   // Check the return type is complete.
@@ -426,6 +423,11 @@ auto HandleFunctionDefinition(Context& context,
   context.inst_block_stack().Pop();
   context.return_scope_stack().pop_back();
   context.decl_name_stack().PopScope();
+
+  // If this is a generic function, collect information about the definition.
+  auto& function = context.functions().Get(function_id);
+  FinishGenericDefinition(context, function.generic_id);
+
   return true;
 }
 

+ 13 - 3
toolchain/check/handle_interface.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "toolchain/check/context.h"
+#include "toolchain/check/generic.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/interface.h"
 #include "toolchain/check/merge.h"
@@ -22,6 +23,8 @@ auto HandleInterfaceIntroducer(Context& context,
   // Optional modifiers and the name follow.
   context.decl_introducer_state_stack().Push<Lex::TokenKind::Interface>();
   context.decl_name_stack().PushScopeAndStartName();
+  // This interface is potentially generic.
+  StartGenericDecl(context);
   return true;
 }
 
@@ -78,6 +81,7 @@ static auto BuildInterfaceDecl(Context& context,
 
   // Create a new interface if this isn't a valid redeclaration.
   if (!interface_decl.interface_id.is_valid()) {
+    auto generic_id = FinishGenericDecl(context, interface_decl_id);
     // TODO: If this is an invalid redeclaration of a non-interface entity or
     // 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
@@ -85,6 +89,7 @@ static auto BuildInterfaceDecl(Context& context,
     SemIR::Interface interface_info = {
         .name_id = name_context.name_id_for_new_inst(),
         .parent_scope_id = name_context.parent_scope_id_for_new_inst(),
+        .generic_id = generic_id,
         .implicit_param_refs_id = name.implicit_params_id,
         .param_refs_id = name.params_id,
         .decl_id = interface_decl_id};
@@ -93,11 +98,12 @@ static auto BuildInterfaceDecl(Context& context,
       interface_decl.type_id =
           context.GetGenericInterfaceType(interface_decl.interface_id);
     }
+  } else {
+    FinishGenericRedecl(
+        context, interface_decl_id,
+        context.interfaces().Get(interface_decl.interface_id).generic_id);
   }
 
-  // TODO: For a generic interface declaration, set the `type_id` to a suitable
-  // generic interface type rather than `type`.
-
   // Write the interface ID into the InterfaceDecl.
   context.ReplaceInstBeforeConstantUse(interface_decl_id, interface_decl);
 
@@ -136,6 +142,7 @@ auto HandleInterfaceDefinitionStart(Context& context,
 
   // Enter the interface scope.
   context.scope_stack().Push(interface_decl_id, interface_info.scope_id);
+  StartGenericDefinition(context, interface_info.generic_id);
 
   context.inst_block_stack().Push();
   context.node_stack().Push(node_id, interface_id);
@@ -193,6 +200,9 @@ auto HandleInterfaceDefinition(Context& context,
   if (!interface_info.associated_entities_id.is_valid()) {
     interface_info.associated_entities_id = associated_entities_id;
   }
+
+  FinishGenericDefinition(context, interface_info.generic_id);
+
   // The decl_name_stack and scopes are popped by `ProcessNodeIds`.
   return true;
 }

+ 52 - 7
toolchain/check/import_ref.cpp

@@ -413,6 +413,45 @@ class ImportRefResolver {
     return context_.inst_blocks().AddCanonical(contents);
   }
 
+  // Gets a local version of an imported generic.
+  auto GetLocalGeneric(SemIR::GenericId generic_id) -> SemIR::GenericId {
+    if (!generic_id.is_valid()) {
+      return SemIR::GenericId::Invalid;
+    }
+
+    // TODO: Support importing generics. Note that this comes up in the prelude,
+    // so for now we fall back to producing `Invalid` and treating imported
+    // generics as non-generic.
+    return SemIR::GenericId::Invalid;
+  }
+
+  // Gets a local argument list corresponding to the arguments of an imported
+  // generic instance.
+  auto GetLocalGenericInstanceArgs(SemIR::GenericInstanceId instance_id)
+      -> llvm::SmallVector<SemIR::InstId> {
+    if (!instance_id.is_valid()) {
+      return {};
+    }
+    return GetLocalInstBlockContents(
+        import_ir_.generic_instances().Get(instance_id).args_id);
+  }
+
+  // Gets a local generic instance whose arguments were already imported by
+  // GetLocalGenericInstanceArgs. Does not add any new work.
+  auto GetLocalGenericInstance(SemIR::GenericInstanceId instance_id,
+                               llvm::ArrayRef<SemIR::InstId> args)
+      -> SemIR::GenericInstanceId {
+    if (!instance_id.is_valid()) {
+      return SemIR::GenericInstanceId::Invalid;
+    }
+    const auto& instance = import_ir_.generic_instances().Get(instance_id);
+    // TODO: Import the generic.
+    auto generic_id = SemIR::GenericId::Invalid;
+    auto args_id = GetLocalCanonicalInstBlockId(instance.args_id, args);
+    // TODO: Also import the generic instance.
+    return context_.generic_instances().GetOrAdd(generic_id, args_id);
+  }
+
   // Returns the ConstantId for each parameter's type. Adds unresolved constants
   // to work_stack_.
   auto GetLocalParamConstantIds(SemIR::InstBlockId param_refs_id)
@@ -842,6 +881,8 @@ class ImportRefResolver {
                                    .decl_block_id = SemIR::InstBlockId::Empty};
     auto class_decl_id = context_.AddPlaceholderInstInNoBlock(
         SemIR::LocIdAndInst(AddImportIRInst(import_class.decl_id), class_decl));
+    // TODO: Support for importing generics.
+    auto generic_id = GetLocalGeneric(import_class.generic_id);
     // Regardless of whether ClassDecl is a complete type, we first need an
     // incomplete type so that any references have something to point at.
     class_decl.class_id = context_.classes().Add({
@@ -852,6 +893,7 @@ class ImportRefResolver {
         // TODO: Add a better way to represent a generic `Class` prior to
         // importing the parameters.
         .parent_scope_id = SemIR::NameScopeId::Invalid,
+        .generic_id = generic_id,
         .implicit_param_refs_id = import_class.implicit_param_refs_id.is_valid()
                                       ? SemIR::InstBlockId::Empty
                                       : SemIR::InstBlockId::Invalid,
@@ -979,7 +1021,7 @@ class ImportRefResolver {
     CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
     auto class_const_id =
         GetLocalConstantId(import_ir_.classes().Get(inst.class_id).decl_id);
-    auto args = GetLocalInstBlockContents(inst.args_id);
+    auto args = GetLocalGenericInstanceArgs(inst.instance_id);
     if (HasNewWork(initial_work)) {
       return ResolveResult::Retry();
     }
@@ -994,11 +1036,11 @@ class ImportRefResolver {
     } else {
       auto generic_class_type = context_.types().GetAs<SemIR::GenericClassType>(
           class_const_inst.type_id());
-      auto args_id = GetLocalCanonicalInstBlockId(inst.args_id, args);
+      auto instance_id = GetLocalGenericInstance(inst.instance_id, args);
       return ResolveAs<SemIR::ClassType>(
           {.type_id = SemIR::TypeId::TypeType,
            .class_id = generic_class_type.class_id,
-           .args_id = args_id});
+           .instance_id = instance_id});
     }
   }
 
@@ -1069,7 +1111,7 @@ class ImportRefResolver {
     auto function_decl_id = context_.AddPlaceholderInstInNoBlock(
         SemIR::LocIdAndInst(import_ir_inst_id, function_decl));
     // TODO: Implement import for generics.
-    auto generic_id = SemIR::GenericId::Invalid;
+    auto generic_id = GetLocalGeneric(function.generic_id);
 
     auto new_return_storage = SemIR::InstId::Invalid;
     if (function.return_storage_id.is_valid()) {
@@ -1180,6 +1222,8 @@ class ImportRefResolver {
         context_.AddPlaceholderInstInNoBlock(SemIR::LocIdAndInst(
             AddImportIRInst(import_interface.decl_id), interface_decl));
 
+    // TODO: Support for importing generics.
+    auto generic_id = GetLocalGeneric(import_interface.generic_id);
     // Start with an incomplete interface.
     interface_decl.interface_id = context_.interfaces().Add({
         .name_id = GetLocalNameId(import_interface.name_id),
@@ -1189,6 +1233,7 @@ class ImportRefResolver {
         // TODO: Add a better way to represent a generic `Interface` prior to
         // importing the parameters.
         .parent_scope_id = SemIR::NameScopeId::Invalid,
+        .generic_id = generic_id,
         .implicit_param_refs_id =
             import_interface.implicit_param_refs_id.is_valid()
                 ? SemIR::InstBlockId::Empty
@@ -1293,7 +1338,7 @@ class ImportRefResolver {
     CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
     auto interface_const_id = GetLocalConstantId(
         import_ir_.interfaces().Get(inst.interface_id).decl_id);
-    auto args = GetLocalInstBlockContents(inst.args_id);
+    auto args = GetLocalGenericInstanceArgs(inst.instance_id);
     if (HasNewWork(initial_work)) {
       return ResolveResult::Retry();
     }
@@ -1310,11 +1355,11 @@ class ImportRefResolver {
       auto generic_interface_type =
           context_.types().GetAs<SemIR::GenericInterfaceType>(
               interface_const_inst.type_id());
-      auto args_id = GetLocalCanonicalInstBlockId(inst.args_id, args);
+      auto instance_id = GetLocalGenericInstance(inst.instance_id, args);
       return ResolveAs<SemIR::InterfaceType>(
           {.type_id = SemIR::TypeId::TypeType,
            .interface_id = generic_interface_type.interface_id,
-           .args_id = args_id});
+           .instance_id = instance_id});
     }
   }
 

+ 1 - 1
toolchain/check/testdata/array/generic_empty.carbon

@@ -39,7 +39,7 @@ fn G(T:! type) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(%T: type)
-// CHECK:STDOUT:   generic [%T: type] {
+// CHECK:STDOUT:     generic [%T: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %T.ref: type = name_ref T, %T [symbolic = constants.%T]
 // CHECK:STDOUT:   %.loc13_16: i32 = int_literal 0 [template = constants.%.2]

+ 1 - 1
toolchain/check/testdata/builtins/int/make_type_signed.carbon

@@ -202,7 +202,7 @@ var m: Int(1000000000);
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Symbolic(%N: i32, %x: %.6) -> %.6
-// CHECK:STDOUT:   generic [%N: i32] {
+// CHECK:STDOUT:     generic [%N: i32] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %x.ref: %.6 = name_ref x, %x
 // CHECK:STDOUT:   return %x.ref

+ 1 - 1
toolchain/check/testdata/builtins/int/make_type_unsigned.carbon

@@ -202,7 +202,7 @@ var m: UInt(1000000000);
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Symbolic(%N: i32, %x: %.6) -> %.6
-// CHECK:STDOUT:   generic [%N: i32] {
+// CHECK:STDOUT:     generic [%N: i32] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %x.ref: %.6 = name_ref x, %x
 // CHECK:STDOUT:   return %x.ref

+ 4 - 3
toolchain/check/testdata/class/fail_generic_method.carbon

@@ -76,7 +76,8 @@ fn Class(N:! i32).F[self: Self](n: T) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT:   %T.ref.loc12: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T]
 // CHECK:STDOUT:   %.loc12: %.2 = field_decl a, element0 [template]
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
@@ -95,12 +96,12 @@ fn Class(N:! i32).F[self: Self](n: T) {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F[@Class.%self.loc13_8.2: %Class.2](@Class.%n.loc13_20.2: %T)
-// CHECK:STDOUT:   generic [file.%T.loc11_13.2: type];
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1[%self: <error>](%n: <error>)
-// CHECK:STDOUT:   generic [file.%N.loc32_10.2: i32] {
+// CHECK:STDOUT:     generic [file.%N.loc32_10.2: i32] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 4 - 3
toolchain/check/testdata/class/generic/basic.carbon

@@ -53,7 +53,8 @@ class Class(T:! type) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT:   %GetAddr.decl: %GetAddr.type = fn_decl @GetAddr [template = constants.%GetAddr] {
 // CHECK:STDOUT:     %Self.ref.loc12: type = name_ref Self, constants.%Class.2 [template = constants.%Class.2]
 // CHECK:STDOUT:     %.loc12_29: type = ptr_type %Class.2 [template = constants.%.2]
@@ -82,7 +83,7 @@ class Class(T:! type) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @GetAddr[addr @Class.%self.loc12_19.3: %.2]() -> %.3
-// CHECK:STDOUT:   generic [file.%T.loc11_13.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %self.ref: %.2 = name_ref self, @Class.%self.loc12_19.3
 // CHECK:STDOUT:   %.loc13_17.1: ref %Class.2 = deref %self.ref
@@ -93,7 +94,7 @@ class Class(T:! type) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @GetValue[@Class.%self.loc17_15.2: %Class.2]() -> %T
-// CHECK:STDOUT:   generic [file.%T.loc11_13.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %self.ref: %Class.2 = name_ref self, @Class.%self.loc17_15.2
 // CHECK:STDOUT:   %k.ref: %.4 = name_ref k, @Class.%.loc21 [template = @Class.%.loc21]

+ 8 - 4
toolchain/check/testdata/class/generic/call.carbon

@@ -124,7 +124,8 @@ var a: Class(5, i32*);
 // CHECK:STDOUT:   %b: ref %Class.4 = bind_name b, %b.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc4_13.2: type, file.%N.loc4_23.2: i32] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%Class.2
 // CHECK:STDOUT: }
@@ -176,7 +177,8 @@ var a: Class(5, i32*);
 // CHECK:STDOUT:   %a: ref <error> = bind_name a, %a.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc4_13.2: type, file.%N.loc4_23.2: i32] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%Class.2
 // CHECK:STDOUT: }
@@ -232,7 +234,8 @@ var a: Class(5, i32*);
 // CHECK:STDOUT:   %a: ref <error> = bind_name a, %a.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc4_13.2: type, file.%N.loc4_23.2: i32] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%Class.2
 // CHECK:STDOUT: }
@@ -286,7 +289,8 @@ var a: Class(5, i32*);
 // CHECK:STDOUT:   %a: ref <error> = bind_name a, %a.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc4_13.2: type, file.%N.loc4_23.2: i32] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%Class.2
 // CHECK:STDOUT: }

+ 3 - 2
toolchain/check/testdata/class/generic/fail_todo_use.carbon

@@ -82,7 +82,8 @@ fn Run() -> i32 {
 // CHECK:STDOUT:   %import_ref.2: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT:   %Get.decl: %Get.type = fn_decl @Get [template = constants.%Get] {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%Class.2 [template = constants.%Class.2]
 // CHECK:STDOUT:     %.loc12_25: type = ptr_type %Class.2 [template = constants.%.2]
@@ -103,7 +104,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Get[addr @Class.%self.loc12_15.3: %.2]() -> %.3
-// CHECK:STDOUT:   generic [file.%T.loc11_13.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %self.ref: %.2 = name_ref self, @Class.%self.loc12_15.3
 // CHECK:STDOUT:   %.loc13_17.1: ref %Class.2 = deref %self.ref

+ 7 - 4
toolchain/check/testdata/class/generic/import.carbon

@@ -142,9 +142,11 @@ class Class(U:! type) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class;
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc4_13.2: type];
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @CompleteClass {
+// CHECK:STDOUT: class @CompleteClass
+// CHECK:STDOUT:     generic [file.%T.loc6_21.2: type] {
 // CHECK:STDOUT:   %int.make_type_32.loc7: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc7_10.1: type = value_of_initializer %int.make_type_32.loc7 [template = i32]
 // CHECK:STDOUT:   %.loc7_10.2: type = converted %int.make_type_32.loc7, %.loc7_10.1 [template = i32]
@@ -165,7 +167,7 @@ class Class(U:! type) {
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1() -> i32
-// CHECK:STDOUT:   generic [file.%T.loc6_21.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc6_21.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %.loc8: i32 = int_literal 0 [template = constants.%.4]
 // CHECK:STDOUT:   return %.loc8
@@ -519,7 +521,8 @@ class Class(U:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Class;
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: class @.1
+// CHECK:STDOUT:     generic [file.%U.loc9_13.2: type] {
 // CHECK:STDOUT:   %T.ref: <error> = name_ref T, <error> [template = <error>]
 // CHECK:STDOUT:   %.loc13: <error> = field_decl x, element0 [template]
 // CHECK:STDOUT:

+ 3 - 2
toolchain/check/testdata/class/generic/member_inline.carbon

@@ -39,7 +39,8 @@ class Class(T:! type) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
 // CHECK:STDOUT:     %T.ref.loc12_11: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T]
 // CHECK:STDOUT:     %n.loc12_8.1: %T = param n
@@ -54,7 +55,7 @@ class Class(T:! type) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(@Class.%n.loc12_8.2: %T) -> %T
-// CHECK:STDOUT:   generic [file.%T.loc11_13.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %n.ref: %T = name_ref n, @Class.%n.loc12_8.2
 // CHECK:STDOUT:   return %n.ref

+ 20 - 14
toolchain/check/testdata/class/generic/member_out_of_line.carbon

@@ -133,7 +133,8 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc4_13.2: type] {
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
 // CHECK:STDOUT:     %T.ref.loc5_11: type = name_ref T, file.%T.loc4_13.2 [symbolic = constants.%T]
 // CHECK:STDOUT:     %n.loc5_8.1: %T = param n
@@ -148,7 +149,7 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%n: %T) -> %T
-// CHECK:STDOUT:   generic [file.%T.loc4_13.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc4_13.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %n.ref: %T = name_ref n, %n
 // CHECK:STDOUT:   return %n.ref
@@ -197,7 +198,8 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @A {
+// CHECK:STDOUT: class @A
+// CHECK:STDOUT:     generic [file.%T.loc4_9.2: type] {
 // CHECK:STDOUT:   %B.decl: %B.type = class_decl @B [template = constants.%B.1] {
 // CHECK:STDOUT:     %T.ref: type = name_ref T, file.%T.loc4_9.2 [symbolic = constants.%T]
 // CHECK:STDOUT:     %N.loc5_11.1: %T = param N
@@ -209,7 +211,8 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT:   .B = %B.decl
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @B {
+// CHECK:STDOUT: class @B
+// CHECK:STDOUT:     generic [file.%T.loc4_9.2: type, @A.%N.loc5_11.2: %T] {
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%B.2 [template = constants.%B.2]
 // CHECK:STDOUT:     %self.loc6_10.1: %B.2 = param self
@@ -225,7 +228,7 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F[%self: %B.2](%a: %T)
-// CHECK:STDOUT:   generic [file.%T.loc4_9.2: type, @A.%N.loc5_11.2: %T] {
+// CHECK:STDOUT:     generic [file.%T.loc4_9.2: type, @A.%N.loc5_11.2: %T] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -267,7 +270,7 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT: fn @F();
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1()
-// CHECK:STDOUT:   generic [file.%T.loc15_15.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc15_15.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -300,7 +303,8 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT:   %.decl: %.type = fn_decl @.1 [template = constants.%.3] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Generic {
+// CHECK:STDOUT: class @Generic
+// CHECK:STDOUT:     generic [file.%T.loc4_15.2: type] {
 // CHECK:STDOUT:   %TooFew.decl: %TooFew.type = fn_decl @TooFew [template = constants.%TooFew] {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -309,7 +313,7 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @TooFew()
-// CHECK:STDOUT:   generic [file.%T.loc4_15.2: type];
+// CHECK:STDOUT:     generic [file.%T.loc4_15.2: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1() {
 // CHECK:STDOUT: !entry:
@@ -350,7 +354,8 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Generic {
+// CHECK:STDOUT: class @Generic
+// CHECK:STDOUT:     generic [file.%T.loc4_15.2: type] {
 // CHECK:STDOUT:   %TooMany.decl: %TooMany.type = fn_decl @TooMany [template = constants.%TooMany] {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -359,10 +364,10 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @TooMany()
-// CHECK:STDOUT:   generic [file.%T.loc4_15.2: type];
+// CHECK:STDOUT:     generic [file.%T.loc4_15.2: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1()
-// CHECK:STDOUT:   generic [file.%T.loc15_12.2: type, file.%U.loc15_22.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc15_12.2: type, file.%U.loc15_22.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -401,7 +406,8 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Generic {
+// CHECK:STDOUT: class @Generic
+// CHECK:STDOUT:     generic [file.%T.loc4_15.2: type] {
 // CHECK:STDOUT:   %WrongType.decl: %WrongType.type = fn_decl @WrongType [template = constants.%WrongType] {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -410,10 +416,10 @@ fn Generic(T:! ()).WrongType() {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @WrongType()
-// CHECK:STDOUT:   generic [file.%T.loc4_15.2: type];
+// CHECK:STDOUT:     generic [file.%T.loc4_15.2: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1()
-// CHECK:STDOUT:   generic [file.%T.loc14_12.2: %.1] {
+// CHECK:STDOUT:     generic [file.%T.loc14_12.2: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 20 - 10
toolchain/check/testdata/class/generic/redeclare.carbon

@@ -113,7 +113,8 @@ class E(U:! type) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Generic {
+// CHECK:STDOUT: class @Generic
+// CHECK:STDOUT:     generic [file.%T.loc4_15.2: type] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%Generic.2
 // CHECK:STDOUT: }
@@ -145,7 +146,8 @@ class E(U:! type) {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @A;
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: class @.1
+// CHECK:STDOUT:     generic [file.%T.loc12_9.2: type] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%.3
 // CHECK:STDOUT: }
@@ -191,9 +193,11 @@ class E(U:! type) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @B;
+// CHECK:STDOUT: class @B
+// CHECK:STDOUT:     generic [file.%N.loc4_9.2: i32];
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: class @.1
+// CHECK:STDOUT:     generic [file.%T.loc12_9.2: type, file.%N.loc12_19.2: %T] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%.3
 // CHECK:STDOUT: }
@@ -239,9 +243,11 @@ class E(U:! type) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @C;
+// CHECK:STDOUT: class @C
+// CHECK:STDOUT:     generic [file.%T.loc4_9.2: type];
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: class @.1
+// CHECK:STDOUT:     generic [file.%T.loc12_9.2: type, file.%U.loc12_19.2: i32] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%.3
 // CHECK:STDOUT: }
@@ -285,9 +291,11 @@ class E(U:! type) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @D;
+// CHECK:STDOUT: class @D
+// CHECK:STDOUT:     generic [file.%T.loc4_9.2: type];
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: class @.1
+// CHECK:STDOUT:     generic [file.%T.loc12_9.2: i32] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%.3
 // CHECK:STDOUT: }
@@ -325,9 +333,11 @@ class E(U:! type) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @E;
+// CHECK:STDOUT: class @E
+// CHECK:STDOUT:     generic [file.%T.loc4_9.2: type];
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: class @.1
+// CHECK:STDOUT:     generic [file.%U.loc11_9.2: type] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%.3
 // CHECK:STDOUT: }

+ 3 - 2
toolchain/check/testdata/class/generic_method.carbon

@@ -52,7 +52,8 @@ fn Class(T:! type).F[self: Self](n: T) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT: class @Class
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT:   %T.ref.loc12: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T]
 // CHECK:STDOUT:   %.loc12: %.2 = field_decl a, element0 [template]
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
@@ -71,7 +72,7 @@ fn Class(T:! type).F[self: Self](n: T) {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F[%self: %Class.2](%n: %T)
-// CHECK:STDOUT:   generic [file.%T.loc11_13.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 1
toolchain/check/testdata/eval/fail_symbolic.carbon

@@ -47,7 +47,7 @@ fn G(N:! i32) {
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(%N: i32)
-// CHECK:STDOUT:   generic [%N: i32] {
+// CHECK:STDOUT:     generic [%N: i32] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %N.ref: i32 = name_ref N, %N [symbolic = constants.%N]

+ 1 - 1
toolchain/check/testdata/eval/symbolic.carbon

@@ -47,7 +47,7 @@ fn F(T:! type) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: type)
-// CHECK:STDOUT:   generic [%T: type] {
+// CHECK:STDOUT:     generic [%T: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %T.ref.loc13_11: type = name_ref T, %T [symbolic = constants.%T]
 // CHECK:STDOUT:   %.loc13_12: type = ptr_type %T [symbolic = constants.%.2]

+ 1 - 1
toolchain/check/testdata/function/builtin/method.carbon

@@ -126,7 +126,7 @@ var arr: [i32; 1.(I.F)(2)];
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1[@I.%self.loc12_8.2: %Self](@I.%other.loc12_20.2: %Self) -> %Self
-// CHECK:STDOUT:   generic [@I.%Self: %.1];
+// CHECK:STDOUT:     generic [@I.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/function/builtin/no_prelude/call_from_operator.carbon

@@ -84,7 +84,7 @@ var arr: [i32; 1 + 2] = (3, 4, 3 + 4);
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Op[@Add.%self.loc7_9.2: %Self](@Add.%other.loc7_21.2: %Self) -> %Self
-// CHECK:STDOUT:   generic [@Add.%Self: %.2];
+// CHECK:STDOUT:     generic [@Add.%Self: %.2];
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- user.carbon
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/function/generic/fail_todo_param_in_type.carbon

@@ -52,5 +52,5 @@ fn F(N:! i32, a: [i32; N]*);
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%N: i32, %a: <error>)
-// CHECK:STDOUT:   generic [%N: i32];
+// CHECK:STDOUT:     generic [%N: i32];
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/function/generic/no_prelude/fail_type_param_mismatch.carbon

@@ -40,7 +40,7 @@ fn F(T:! type, U:! type) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: type, %U: type)
-// CHECK:STDOUT:   generic [%T: type, %U: type] {
+// CHECK:STDOUT:     generic [%T: type, %U: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %T.ref: type = name_ref T, %T [symbolic = constants.%T]
 // CHECK:STDOUT:   %.loc12: type = ptr_type %T [symbolic = constants.%.2]

+ 1 - 1
toolchain/check/testdata/function/generic/no_prelude/type_param.carbon

@@ -34,7 +34,7 @@ fn F(T:! type) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: type)
-// CHECK:STDOUT:   generic [%T: type] {
+// CHECK:STDOUT:     generic [%T: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %T.ref.loc12: type = name_ref T, %T [symbolic = constants.%T]
 // CHECK:STDOUT:   %.loc12: type = ptr_type %T [symbolic = constants.%.2]

+ 1 - 1
toolchain/check/testdata/function/generic/no_prelude/type_param_scope.carbon

@@ -38,7 +38,7 @@ fn F(T:! type, n: T) -> T {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: type, %n: %T) -> %T
-// CHECK:STDOUT:   generic [%T: type] {
+// CHECK:STDOUT:     generic [%T: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %T.ref: type = name_ref T, %T [symbolic = constants.%T]
 // CHECK:STDOUT:   %n.ref: %T = name_ref n, %n

+ 7 - 7
toolchain/check/testdata/function/generic/redeclare.carbon

@@ -134,7 +134,7 @@ fn F(U:! type, T:! type) -> U* {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: type) -> %.1
-// CHECK:STDOUT:   generic [file.%T.loc4_6.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc4_6.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl.loc4 [template = constants.%F]
 // CHECK:STDOUT:   %T.ref: type = name_ref T, %T [symbolic = constants.%T]
@@ -185,10 +185,10 @@ fn F(U:! type, T:! type) -> U* {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: type, %U: type) -> %.1
-// CHECK:STDOUT:   generic [%T: type, %U: type];
+// CHECK:STDOUT:     generic [%T: type, %U: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1(%T: type, %U: type) -> %.3
-// CHECK:STDOUT:   generic [%T: type, %U: type] {
+// CHECK:STDOUT:     generic [%T: type, %U: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %T.ref: type = name_ref T, %T [symbolic = constants.%T]
@@ -239,10 +239,10 @@ fn F(U:! type, T:! type) -> U* {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: type, %U: type) -> %.1
-// CHECK:STDOUT:   generic [%T: type, %U: type];
+// CHECK:STDOUT:     generic [%T: type, %U: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1(%U: type, %T: type) -> %.3
-// CHECK:STDOUT:   generic [%U: type, %T: type] {
+// CHECK:STDOUT:     generic [%U: type, %T: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %T.ref: type = name_ref T, %T [symbolic = constants.%T.2]
@@ -293,10 +293,10 @@ fn F(U:! type, T:! type) -> U* {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: type, %U: type) -> %.1
-// CHECK:STDOUT:   generic [%T: type, %U: type];
+// CHECK:STDOUT:     generic [%T: type, %U: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1(%U: type, %T: type) -> %.3
-// CHECK:STDOUT:   generic [%U: type, %T: type] {
+// CHECK:STDOUT:     generic [%U: type, %T: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %T.ref: type = name_ref T, %T [symbolic = constants.%T.2]

+ 2 - 2
toolchain/check/testdata/impl/compound.carbon

@@ -159,10 +159,10 @@ fn InstanceCallIndirect(p: i32*) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@Simple.%Self: %.1];
+// CHECK:STDOUT:     generic [@Simple.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G.1[@Simple.%self.loc13_8.2: %Self]()
-// CHECK:STDOUT:   generic [@Simple.%Self: %.1];
+// CHECK:STDOUT:     generic [@Simple.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/impl/extend_impl.carbon

@@ -91,7 +91,7 @@ fn G(c: C) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@HasF.%Self: %.1];
+// CHECK:STDOUT:     generic [@HasF.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:

+ 1 - 1
toolchain/check/testdata/impl/fail_call_invalid.carbon

@@ -97,7 +97,7 @@ fn InstanceCall(n: i32) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G.1[@Simple.%self.loc12_8.2: %Self]()
-// CHECK:STDOUT:   generic [@Simple.%Self: %.1];
+// CHECK:STDOUT:     generic [@Simple.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:

+ 4 - 3
toolchain/check/testdata/impl/fail_extend_impl_forall.carbon

@@ -55,7 +55,8 @@ class C {
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @GenericInterface {
+// CHECK:STDOUT: interface @GenericInterface
+// CHECK:STDOUT:     generic [file.%T.loc11_28.2: type] {
 // CHECK:STDOUT:   %Self: %GenericInterface = bind_symbolic_name Self 1 [symbolic = constants.%Self]
 // CHECK:STDOUT:   %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {
 // CHECK:STDOUT:     %T.ref: type = name_ref T, file.%T.loc11_28.2 [symbolic = constants.%T]
@@ -100,10 +101,10 @@ class C {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1(@GenericInterface.%x.loc12_8.2: %T)
-// CHECK:STDOUT:   generic [file.%T.loc11_28.2: type, @GenericInterface.%Self: %GenericInterface];
+// CHECK:STDOUT:     generic [file.%T.loc11_28.2: type, @GenericInterface.%Self: %GenericInterface];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2(@impl.%x.loc20_10.2: %T)
-// CHECK:STDOUT:   generic [@C.%T.loc19_23.2: type] {
+// CHECK:STDOUT:     generic [@C.%T.loc19_23.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 2 - 1
toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon

@@ -50,7 +50,8 @@ interface I {
 // CHECK:STDOUT:
 // CHECK:STDOUT: impl @impl: %C as %.1;
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: class @C
+// CHECK:STDOUT:     generic [@I.%Self: %.1] {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %I.ref: type = name_ref I, file.%I.decl [template = constants.%.1]
 // CHECK:STDOUT:   }

+ 1 - 1
toolchain/check/testdata/impl/fail_impl_as_scope.carbon

@@ -67,7 +67,7 @@ impl as Simple {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@Simple.%Self: %.1];
+// CHECK:STDOUT:     generic [@Simple.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:

+ 3 - 3
toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon

@@ -845,7 +845,7 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@I.%Self: %.1];
+// CHECK:STDOUT:     generic [@I.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @PossiblyF();
 // CHECK:STDOUT:
@@ -858,7 +858,7 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT: fn @F.4() -> bool;
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.5[@J.%self.loc93_20.2: bool](@J.%b.loc93_32.2: bool) -> bool
-// CHECK:STDOUT:   generic [@J.%Self: %.6];
+// CHECK:STDOUT:     generic [@J.%Self: %.6];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.6[@impl.7.%self.loc104_10.2: bool]() -> bool;
 // CHECK:STDOUT:
@@ -877,7 +877,7 @@ class SelfNestedBadReturnType {
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.13(@SelfNested.%x.loc188_8.2: %.13) -> %.15
-// CHECK:STDOUT:   generic [@SelfNested.%Self: %.9];
+// CHECK:STDOUT:     generic [@SelfNested.%Self: %.9];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.14(@impl.14.%x.loc200_10.2: %.20) -> %.21;
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/impl/impl_as.carbon

@@ -81,7 +81,7 @@ class C {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@Simple.%Self: %.1];
+// CHECK:STDOUT:     generic [@Simple.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:

+ 2 - 2
toolchain/check/testdata/impl/impl_forall.carbon

@@ -68,10 +68,10 @@ impl forall [T:! type] T as Simple {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@Simple.%Self: %.1];
+// CHECK:STDOUT:     generic [@Simple.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2()
-// CHECK:STDOUT:   generic [file.%T.loc15_14.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc15_14.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 1
toolchain/check/testdata/impl/lookup/alias.carbon

@@ -97,7 +97,7 @@ fn G(c: C) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@HasF.%Self: %.1];
+// CHECK:STDOUT:     generic [@HasF.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:

+ 1 - 1
toolchain/check/testdata/impl/lookup/fail_alias_impl_not_found.carbon

@@ -84,7 +84,7 @@ fn F(c: C) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@I.%Self: %.1];
+// CHECK:STDOUT:     generic [@I.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2(%c: %C) {
 // CHECK:STDOUT: !entry:

+ 1 - 1
toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon

@@ -103,7 +103,7 @@ impl C as I {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@I.%Self: %.1];
+// CHECK:STDOUT:     generic [@I.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/impl/lookup/import.carbon

@@ -88,7 +88,7 @@ fn G(c: Impl.C) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@HasF.%Self: %.1];
+// CHECK:STDOUT:     generic [@HasF.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:

+ 1 - 1
toolchain/check/testdata/impl/lookup/instance_method.carbon

@@ -122,7 +122,7 @@ fn F(c: C) -> i32 {
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1[@I.%self.loc14_8.2: %Self]() -> i32
-// CHECK:STDOUT:   generic [@I.%Self: %.1];
+// CHECK:STDOUT:     generic [@I.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2[@impl.%self.loc19_10.2: %C]() -> i32;
 // CHECK:STDOUT:

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

@@ -86,7 +86,7 @@ fn G(c: Impl.C) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@HasF.%Self: %.1];
+// CHECK:STDOUT:     generic [@HasF.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:

+ 1 - 1
toolchain/check/testdata/impl/no_prelude/basic.carbon

@@ -74,7 +74,7 @@ impl C as Simple {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@Simple.%Self: %.1];
+// CHECK:STDOUT:     generic [@Simple.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:

+ 1 - 1
toolchain/check/testdata/impl/no_prelude/import_self.carbon

@@ -76,7 +76,7 @@ fn F(x: (), y: ()) -> () {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Op[@Add.%self.loc5_9.2: %Self](@Add.%other.loc5_21.2: %Self) -> %Self
-// CHECK:STDOUT:   generic [@Add.%Self: %.1];
+// CHECK:STDOUT:     generic [@Add.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- b.carbon
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/impl/no_prelude/self_in_class.carbon

@@ -97,7 +97,7 @@ class A {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Make.1() -> %Self
-// CHECK:STDOUT:   generic [@DefaultConstructible.%Self: %.1];
+// CHECK:STDOUT:     generic [@DefaultConstructible.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Make.2() -> @impl.%return.var: %C {
 // CHECK:STDOUT: !entry:

+ 2 - 2
toolchain/check/testdata/impl/no_prelude/self_in_signature.carbon

@@ -250,7 +250,7 @@ impl D as SelfNested {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1[@UseSelf.%self.loc12_8.2: %Self.1](@UseSelf.%x.loc12_20.2: %Self.1) -> %Self.1
-// CHECK:STDOUT:   generic [@UseSelf.%Self: %.1];
+// CHECK:STDOUT:     generic [@UseSelf.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2[@impl.1.%self.loc20_8.2: %C](@impl.1.%x.loc20_17.2: %C) -> @impl.1.%return.var: %C {
 // CHECK:STDOUT: !entry:
@@ -269,7 +269,7 @@ impl D as SelfNested {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.4(@SelfNested.%x.loc28_8.2: %.13)
-// CHECK:STDOUT:   generic [@SelfNested.%Self: %.9];
+// CHECK:STDOUT:     generic [@SelfNested.%Self: %.9];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.5(@impl.3.%x.loc32_8.2: %.18);
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/interface/fail_todo_define_default_fn_inline.carbon

@@ -81,7 +81,7 @@ interface Interface {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F()
-// CHECK:STDOUT:   generic [@Interface.%Self: %.1] {
+// CHECK:STDOUT:     generic [@Interface.%Self: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -89,5 +89,5 @@ interface Interface {
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(@Interface.%a.loc21_16.2: i32, @Interface.%b.loc21_24.2: i32) -> i32 = "int.sadd"
-// CHECK:STDOUT:   generic [@Interface.%Self: %.1];
+// CHECK:STDOUT:     generic [@Interface.%Self: %.1];
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/interface/fail_todo_define_default_fn_out_of_line.carbon

@@ -123,12 +123,12 @@ fn Interface.G(a: i32, b: i32) -> i32 = "int.sadd";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F()
-// CHECK:STDOUT:   generic [@Interface.%Self: %.1];
+// CHECK:STDOUT:     generic [@Interface.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(@Interface.%a.loc22_16.2: i32, @Interface.%b.loc22_24.2: i32) -> i32
-// CHECK:STDOUT:   generic [@Interface.%Self: %.1];
+// CHECK:STDOUT:     generic [@Interface.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1() {
 // CHECK:STDOUT: !entry:

+ 1 - 1
toolchain/check/testdata/interface/no_prelude/as_type_of_type.carbon

@@ -47,7 +47,7 @@ fn F(T:! Empty) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: %.1)
-// CHECK:STDOUT:   generic [%T: %.1] {
+// CHECK:STDOUT:     generic [%T: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %T.ref: %.1 = name_ref T, %T [symbolic = constants.%T]
 // CHECK:STDOUT:   %.loc14_10.1: type = facet_type_access %T.ref [symbolic = constants.%T]

+ 1 - 1
toolchain/check/testdata/interface/no_prelude/basic.carbon

@@ -61,5 +61,5 @@ interface ForwardDeclared {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F()
-// CHECK:STDOUT:   generic [@ForwardDeclared.%Self: %.2];
+// CHECK:STDOUT:     generic [@ForwardDeclared.%Self: %.2];
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/interface/no_prelude/default_fn.carbon

@@ -82,7 +82,7 @@ class C {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@I.%Self: %.1] {
+// CHECK:STDOUT:     generic [@I.%Self: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:   %c.var: ref %C = var c

+ 4 - 3
toolchain/check/testdata/interface/no_prelude/fail_add_member_outside_definition.carbon

@@ -79,7 +79,8 @@ interface Outer {
 // CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @Inner {
+// CHECK:STDOUT: interface @Inner
+// CHECK:STDOUT:     generic [@Outer.%Self: %.3] {
 // CHECK:STDOUT:   %Self: %.4 = bind_symbolic_name Self 1 [symbolic = constants.%Self.3]
 // CHECK:STDOUT:   %.decl: %.type = fn_decl @.1 [template = constants.%.5] {}
 // CHECK:STDOUT:
@@ -95,8 +96,8 @@ interface Outer {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1()
-// CHECK:STDOUT:   generic [@Outer.%Self: %.3, @Inner.%Self: %.4];
+// CHECK:STDOUT:     generic [@Outer.%Self: %.3, @Inner.%Self: %.4];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2()
-// CHECK:STDOUT:   generic [@Outer.%Self: %.3];
+// CHECK:STDOUT:     generic [@Outer.%Self: %.3];
 // CHECK:STDOUT:

+ 8 - 4
toolchain/check/testdata/interface/no_prelude/fail_generic_redeclaration.carbon

@@ -88,7 +88,8 @@ interface DifferentParams(T:! ()) {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @NotGeneric;
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @.1 {
+// CHECK:STDOUT: interface @.1
+// CHECK:STDOUT:     generic [file.%T.loc19_22.2: type] {
 // CHECK:STDOUT:   %Self: %.3 = bind_symbolic_name Self 1 [symbolic = constants.%Self.1]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -96,7 +97,8 @@ interface DifferentParams(T:! ()) {}
 // CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @Generic;
+// CHECK:STDOUT: interface @Generic
+// CHECK:STDOUT:     generic [file.%T.loc21_19.2: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @.2 {
 // CHECK:STDOUT:   %Self: %.4 = bind_symbolic_name Self 0 [symbolic = constants.%Self.2]
@@ -106,9 +108,11 @@ interface DifferentParams(T:! ()) {}
 // CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @DifferentParams;
+// CHECK:STDOUT: interface @DifferentParams
+// CHECK:STDOUT:     generic [file.%T.loc31_27.2: type];
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @.3 {
+// CHECK:STDOUT: interface @.3
+// CHECK:STDOUT:     generic [file.%T.loc38_27.2: %.2] {
 // CHECK:STDOUT:   %Self: %.5 = bind_symbolic_name Self 1 [symbolic = constants.%Self.3]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 2 - 2
toolchain/check/testdata/interface/no_prelude/fail_lookup_undefined.carbon

@@ -108,8 +108,8 @@ interface BeingDefined {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @H() -> <error>
-// CHECK:STDOUT:   generic [@BeingDefined.%Self: %.4];
+// CHECK:STDOUT:     generic [@BeingDefined.%Self: %.4];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.2()
-// CHECK:STDOUT:   generic [@BeingDefined.%Self: %.4];
+// CHECK:STDOUT:     generic [@BeingDefined.%Self: %.4];
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/interface/no_prelude/fail_member_lookup.carbon

@@ -67,7 +67,7 @@ fn F() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@Interface.%Self: %.1];
+// CHECK:STDOUT:     generic [@Interface.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() {
 // CHECK:STDOUT: !entry:

+ 2 - 2
toolchain/check/testdata/interface/no_prelude/fail_redeclare_member.carbon

@@ -53,8 +53,8 @@ interface Interface {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F()
-// CHECK:STDOUT:   generic [@Interface.%Self: %.1];
+// CHECK:STDOUT:     generic [@Interface.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1()
-// CHECK:STDOUT:   generic [@Interface.%Self: %.1];
+// CHECK:STDOUT:     generic [@Interface.%Self: %.1];
 // CHECK:STDOUT:

+ 3 - 3
toolchain/check/testdata/interface/no_prelude/fail_todo_facet_lookup.carbon

@@ -78,10 +78,10 @@ fn CallFacet(T:! Interface, x: T) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F()
-// CHECK:STDOUT:   generic [@Interface.%Self: %.1];
+// CHECK:STDOUT:     generic [@Interface.%Self: %.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @CallStatic(%T: %.1)
-// CHECK:STDOUT:   generic [%T: %.1] {
+// CHECK:STDOUT:     generic [%T: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %T.ref: %.1 = name_ref T, %T [symbolic = constants.%T]
 // CHECK:STDOUT:   %F.ref: %.3 = name_ref F, @Interface.%.loc11 [template = constants.%.4]
@@ -89,7 +89,7 @@ fn CallFacet(T:! Interface, x: T) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @CallFacet(%T: %.1, %x: %T)
-// CHECK:STDOUT:   generic [%T: %.1] {
+// CHECK:STDOUT:     generic [%T: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %x.ref: %T = name_ref x, %x
 // CHECK:STDOUT:   return

+ 4 - 3
toolchain/check/testdata/interface/no_prelude/fail_todo_generic_default_fn.carbon

@@ -73,7 +73,8 @@ fn I(T:! type).F[self: Self]() -> Self { return self; }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @I {
+// CHECK:STDOUT: interface @I
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type] {
 // CHECK:STDOUT:   %Self: %I = bind_symbolic_name Self 1 [symbolic = constants.%Self]
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
 // CHECK:STDOUT:     %Self.ref.loc22_14: %I = name_ref Self, %Self [symbolic = constants.%Self]
@@ -91,10 +92,10 @@ fn I(T:! type).F[self: Self]() -> Self { return self; }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F[@I.%self.loc22_8.2: <error>]() -> <error>
-// CHECK:STDOUT:   generic [file.%T.loc11_13.2: type, @I.%Self: %I];
+// CHECK:STDOUT:     generic [file.%T.loc11_13.2: type, @I.%Self: %I];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1[%self: <error>]() -> <error>
-// CHECK:STDOUT:   generic [file.%T.loc39_6.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc39_6.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %self.ref: <error> = name_ref self, %self
 // CHECK:STDOUT:   return <error>

+ 2 - 2
toolchain/check/testdata/interface/no_prelude/fail_todo_modifiers.carbon

@@ -58,13 +58,13 @@ interface Modifiers {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Final()
-// CHECK:STDOUT:   generic [@Modifiers.%Self: %.1] {
+// CHECK:STDOUT:     generic [@Modifiers.%Self: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Default()
-// CHECK:STDOUT:   generic [@Modifiers.%Self: %.1] {
+// CHECK:STDOUT:     generic [@Modifiers.%Self: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 13 - 9
toolchain/check/testdata/interface/no_prelude/generic.carbon

@@ -141,7 +141,8 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @Simple {
+// CHECK:STDOUT: interface @Simple
+// CHECK:STDOUT:     generic [file.%T.loc4_18.2: type] {
 // CHECK:STDOUT:   %Self: %Simple = bind_symbolic_name Self 1 [symbolic = constants.%Self.1]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -149,7 +150,8 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @WithAssocFn {
+// CHECK:STDOUT: interface @WithAssocFn
+// CHECK:STDOUT:     generic [file.%T.loc8_23.2: type] {
 // CHECK:STDOUT:   %Self: %WithAssocFn = bind_symbolic_name Self 1 [symbolic = constants.%Self.2]
 // CHECK:STDOUT:   %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {
 // CHECK:STDOUT:     %X.ref: type = name_ref X, file.%X.decl [template = constants.%X]
@@ -163,7 +165,8 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT:   witness = (%F.decl)
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @WithImplicitArgs;
+// CHECK:STDOUT: interface @WithImplicitArgs
+// CHECK:STDOUT:     generic [file.%T.loc22_28.2: type, file.%N.loc22_38.2: %T.1];
 // CHECK:STDOUT:
 // CHECK:STDOUT: impl @impl.1: %C as %.5 {
 // CHECK:STDOUT:   %.1: <witness> = interface_witness () [template = constants.%.6]
@@ -210,7 +213,7 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1() -> %X
-// CHECK:STDOUT:   generic [file.%T.loc8_23.2: type, @WithAssocFn.%Self: %WithAssocFn];
+// CHECK:STDOUT:     generic [file.%T.loc8_23.2: type, @WithAssocFn.%Self: %WithAssocFn];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2() -> @impl.2.%return.var: %X {
 // CHECK:STDOUT: !entry:
@@ -221,10 +224,10 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Receive(%T: %.5)
-// CHECK:STDOUT:   generic [%T: %.5];
+// CHECK:STDOUT:     generic [%T: %.5];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Pass(%T: %.5)
-// CHECK:STDOUT:   generic [%T: %.5] {
+// CHECK:STDOUT:     generic [%T: %.5] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Receive.ref: %Receive.type = name_ref Receive, file.%Receive.decl [template = constants.%Receive]
 // CHECK:STDOUT:   %T.ref: %.5 = name_ref T, %T [symbolic = constants.%T.2]
@@ -287,7 +290,8 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @Generic {
+// CHECK:STDOUT: interface @Generic
+// CHECK:STDOUT:     generic [file.%T.loc4_19.2: type] {
 // CHECK:STDOUT:   %Self: %Generic = bind_symbolic_name Self 1 [symbolic = constants.%Self]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -306,10 +310,10 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%T: %.3)
-// CHECK:STDOUT:   generic [%T: %.3];
+// CHECK:STDOUT:     generic [%T: %.3];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(%T: %.4)
-// CHECK:STDOUT:   generic [%T: %.4] {
+// CHECK:STDOUT:     generic [%T: %.4] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %T.ref: %.4 = name_ref T, %T [symbolic = constants.%T.3]

+ 2 - 2
toolchain/check/testdata/interface/no_prelude/generic_binding_after_assoc_const.carbon

@@ -66,8 +66,8 @@ interface I {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(@I.%T.loc12_8.2: type)
-// CHECK:STDOUT:   generic [@I.%Self: %.1, @I.%T.loc12_8.2: type];
+// CHECK:STDOUT:     generic [@I.%Self: %.1, @I.%T.loc12_8.2: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(@I.%T.loc16_8.2: type)
-// CHECK:STDOUT:   generic [@I.%Self: %.1, @I.%T.loc16_8.2: type];
+// CHECK:STDOUT:     generic [@I.%Self: %.1, @I.%T.loc16_8.2: type];
 // CHECK:STDOUT:

+ 3 - 2
toolchain/check/testdata/interface/no_prelude/generic_import.carbon

@@ -51,7 +51,8 @@ impl C as AddWith(C) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @AddWith {
+// CHECK:STDOUT: interface @AddWith
+// CHECK:STDOUT:     generic [file.%T.loc4_19.2: type] {
 // CHECK:STDOUT:   %Self: %AddWith = bind_symbolic_name Self 1 [symbolic = constants.%Self]
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
 // CHECK:STDOUT:   %.loc5: %.2 = assoc_entity element0, %F.decl [template = constants.%.3]
@@ -63,7 +64,7 @@ impl C as AddWith(C) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F()
-// CHECK:STDOUT:   generic [file.%T.loc4_19.2: type, @AddWith.%Self: %AddWith];
+// CHECK:STDOUT:     generic [file.%T.loc4_19.2: type, @AddWith.%Self: %AddWith];
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- b.carbon
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/interface/no_prelude/import.carbon

@@ -127,10 +127,10 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1()
-// CHECK:STDOUT:   generic [@Basic.%Self: %.2];
+// CHECK:STDOUT:     generic [@Basic.%Self: %.2];
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.2()
-// CHECK:STDOUT:   generic [@ForwardDeclared.%Self: %.8];
+// CHECK:STDOUT:     generic [@ForwardDeclared.%Self: %.8];
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- b.carbon
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/interface/no_prelude/self.carbon

@@ -53,5 +53,5 @@ interface UseSelf {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F[@UseSelf.%self.loc12_8.2: %Self]() -> %Self
-// CHECK:STDOUT:   generic [@UseSelf.%Self: %.1];
+// CHECK:STDOUT:     generic [@UseSelf.%Self: %.1];
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/interface/todo_define_not_default.carbon

@@ -107,7 +107,7 @@ interface I {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F()
-// CHECK:STDOUT:   generic [@I.%Self: %.1] {
+// CHECK:STDOUT:     generic [@I.%Self: %.1] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -115,5 +115,5 @@ interface I {
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(@I.%a.loc14_8.2: i32, @I.%b.loc14_16.2: i32) -> i32 = "int.sadd"
-// CHECK:STDOUT:   generic [@I.%Self: %.1];
+// CHECK:STDOUT:     generic [@I.%Self: %.1];
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/namespace/fail_params.carbon

@@ -92,7 +92,7 @@ fn D(T:! type).F() {}
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1()
-// CHECK:STDOUT:   generic [file.%T.loc39_6.2: type] {
+// CHECK:STDOUT:     generic [file.%T.loc39_6.2: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 4 - 2
toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon

@@ -56,9 +56,11 @@ export C2(T:! type);
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @C1;
+// CHECK:STDOUT: class @C1
+// CHECK:STDOUT:     generic [file.%T.loc4_10.2: type];
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @C2;
+// CHECK:STDOUT: class @C2
+// CHECK:STDOUT:     generic [file.%T.loc5_10.2: type];
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_b.carbon
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/return/fail_let_in_type.carbon

@@ -55,7 +55,7 @@ fn FirstPerfectNumber() -> z { return 6; }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @HalfDozen() -> %y
-// CHECK:STDOUT:   generic [<unexpected instref inst+20>: type] {
+// CHECK:STDOUT:     generic [<unexpected instref inst+20>: type] {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %.loc24: i32 = int_literal 6 [template = constants.%.2]
 // CHECK:STDOUT:   return <error>

+ 2 - 1
toolchain/check/testdata/struct/import.carbon

@@ -145,7 +145,8 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: class @C
+// CHECK:STDOUT:     generic [file.%S.loc8_9.2: %.11] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%C.2
 // CHECK:STDOUT: }

+ 2 - 1
toolchain/check/testdata/tuples/import.carbon

@@ -160,7 +160,8 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: class @C
+// CHECK:STDOUT:     generic [file.%X.loc7_9.2: %.9] {
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%C.2
 // CHECK:STDOUT: }

+ 2 - 0
toolchain/sem_ir/class.h

@@ -40,6 +40,8 @@ struct Class : public Printable<Class> {
   NameId name_id;
   // The parent scope.
   NameScopeId parent_scope_id;
+  // If this is a generic function, information about the generic.
+  GenericId generic_id;
   // A block containing a single reference instruction per implicit parameter.
   InstBlockId implicit_param_refs_id;
   // A block containing a single reference instruction per parameter.

+ 26 - 5
toolchain/sem_ir/formatter.cpp

@@ -120,6 +120,14 @@ class Formatter {
     Indent(-2);
   }
 
+  // Wraps the current line, prior to some text that we expect to be quite long
+  // and more readable on a separate line. This is indented two levels more than
+  // the ambient text.
+  auto WrapLine() -> void {
+    out_ << '\n';
+    Indent(4);
+  }
+
   auto FormatConstants() -> void {
     if (!sem_ir_.constants().size()) {
       return;
@@ -139,6 +147,10 @@ class Formatter {
     out_ << "\nclass ";
     FormatClassName(id);
 
+    if (class_info.generic_id.is_valid()) {
+      FormatGeneric(class_info.generic_id);
+    }
+
     llvm::SaveAndRestore class_scope(scope_, inst_namer_.GetScopeFor(id));
 
     if (class_info.scope_id.is_valid()) {
@@ -159,6 +171,10 @@ class Formatter {
     out_ << "\ninterface ";
     FormatInterfaceName(id);
 
+    if (interface_info.generic_id.is_valid()) {
+      FormatGeneric(interface_info.generic_id);
+    }
+
     llvm::SaveAndRestore interface_scope(scope_, inst_namer_.GetScopeFor(id));
 
     if (interface_info.scope_id.is_valid()) {
@@ -263,7 +279,6 @@ class Formatter {
     }
 
     if (fn.generic_id.is_valid()) {
-      out_ << "\n  ";
       FormatGeneric(fn.generic_id);
     }
 
@@ -287,6 +302,7 @@ class Formatter {
   }
 
   auto FormatGeneric(GenericId generic_id) -> void {
+    WrapLine();
     out_ << "generic [";
     FormatParamList(sem_ir_.generics().Get(generic_id).bindings_id);
     out_ << "]";
@@ -621,8 +637,8 @@ class Formatter {
   }
 
   auto FormatInstructionRHS(ClassType inst) -> void {
-    if (inst.args_id.is_valid()) {
-      FormatArgs(inst.class_id, inst.args_id);
+    if (inst.instance_id.is_valid()) {
+      FormatArgs(inst.class_id, inst.instance_id);
     } else {
       FormatArgs(inst.class_id);
     }
@@ -639,8 +655,8 @@ class Formatter {
   }
 
   auto FormatInstructionRHS(InterfaceType inst) -> void {
-    if (inst.args_id.is_valid()) {
-      FormatArgs(inst.interface_id, inst.args_id);
+    if (inst.instance_id.is_valid()) {
+      FormatArgs(inst.interface_id, inst.instance_id);
     } else {
       FormatArgs(inst.interface_id);
     }
@@ -776,6 +792,11 @@ class Formatter {
     out_ << ')';
   }
 
+  auto FormatArg(GenericInstanceId id) -> void {
+    const auto& instance = sem_ir_.generic_instances().Get(id);
+    FormatArg(instance.args_id);
+  }
+
   auto FormatArg(RealId id) -> void {
     // TODO: Format with a `.` when the exponent is near zero.
     const auto& real = sem_ir_.reals().Get(id);

+ 10 - 0
toolchain/sem_ir/generic.h

@@ -54,6 +54,16 @@ class GenericInstanceStore : public Yaml::Printable<GenericInstanceStore> {
   // and must be a canonical instruction block ID.
   auto GetOrAdd(GenericId generic_id, InstBlockId args_id) -> GenericInstanceId;
 
+  // Gets the specified generic instance.
+  auto Get(GenericInstanceId instance_id) const -> const GenericInstance& {
+    return generic_instances_.Get(instance_id);
+  }
+
+  // Gets the specified generic instance.
+  auto Get(GenericInstanceId instance_id) -> GenericInstance& {
+    return generic_instances_.Get(instance_id);
+  }
+
   // These are to support printable structures, and are not guaranteed.
   auto OutputYaml() const -> Yaml::OutputMapping {
     return generic_instances_.OutputYaml();

+ 3 - 3
toolchain/sem_ir/id_kind.h

@@ -121,9 +121,9 @@ using IdKind = TypeEnum<
     IntId, RealId, FloatId, StringLiteralValueId,
     // From sem_ir/id.h.
     InstId, ConstantId, BindNameId, CompileTimeBindIndex, FunctionId, ClassId,
-    InterfaceId, ImplId, GenericId, ImportIRId, ImportIRInstId, LocId,
-    BoolValue, IntKind, NameId, NameScopeId, InstBlockId, TypeId, TypeBlockId,
-    ElementIndex, FloatKind>;
+    InterfaceId, ImplId, GenericId, GenericInstanceId, ImportIRId,
+    ImportIRInstId, LocId, BoolValue, IntKind, NameId, NameScopeId, InstBlockId,
+    TypeId, TypeBlockId, ElementIndex, FloatKind>;
 
 }  // namespace Carbon::SemIR
 

+ 2 - 0
toolchain/sem_ir/interface.h

@@ -37,6 +37,8 @@ struct Interface : public Printable<Interface> {
   NameId name_id;
   // The parent scope.
   NameScopeId parent_scope_id;
+  // If this is a generic function, information about the generic.
+  GenericId generic_id;
   // A block containing a single reference instruction per implicit parameter.
   InstBlockId implicit_param_refs_id;
   // A block containing a single reference instruction per parameter.

+ 2 - 2
toolchain/sem_ir/typed_insts.h

@@ -418,7 +418,7 @@ struct ClassType {
 
   TypeId type_id;
   ClassId class_id;
-  InstBlockId args_id = InstBlockId::Invalid;
+  GenericInstanceId instance_id;
 };
 
 struct ConstType {
@@ -614,7 +614,7 @@ struct InterfaceType {
 
   TypeId type_id;
   InterfaceId interface_id;
-  InstBlockId args_id = InstBlockId::Invalid;
+  GenericInstanceId instance_id;
 };
 
 // A witness that a type implements an interface.