Просмотр исходного кода

Accept generic parameter lists in class declarations. (#3933)

Check that they match across redeclarations.

Fix some importing bugs for symbolic bindings which were uncovered when
testing this.
Richard Smith 2 лет назад
Родитель
Сommit
d5c0c9cfe3
57 измененных файлов с 1005 добавлено и 286 удалено
  1. 7 2
      toolchain/check/class.cpp
  2. 5 129
      toolchain/check/function.cpp
  3. 8 6
      toolchain/check/handle_class.cpp
  4. 2 0
      toolchain/check/handle_function.cpp
  5. 26 10
      toolchain/check/import_ref.cpp
  6. 157 0
      toolchain/check/merge.cpp
  7. 27 1
      toolchain/check/merge.h
  8. 0 2
      toolchain/check/testdata/alias/no_prelude/import.carbon
  9. 0 1
      toolchain/check/testdata/alias/no_prelude/import_order.carbon
  10. 0 4
      toolchain/check/testdata/class/cross_package_import.carbon
  11. 8 16
      toolchain/check/testdata/class/extern.carbon
  12. 2 5
      toolchain/check/testdata/class/fail_import_misuses.carbon
  13. 13 17
      toolchain/check/testdata/class/fail_todo_generic_method.carbon
  14. 1 8
      toolchain/check/testdata/class/generic.carbon
  15. 96 0
      toolchain/check/testdata/class/generic/basic.carbon
  16. 71 0
      toolchain/check/testdata/class/generic/fail_todo_member_out_of_line.carbon
  17. 100 0
      toolchain/check/testdata/class/generic/fail_todo_use.carbon
  18. 121 0
      toolchain/check/testdata/class/generic/import.carbon
  19. 55 0
      toolchain/check/testdata/class/generic/member_inline.carbon
  20. 280 0
      toolchain/check/testdata/class/generic/redeclare.carbon
  21. 0 5
      toolchain/check/testdata/class/import.carbon
  22. 0 2
      toolchain/check/testdata/class/import_base.carbon
  23. 2 4
      toolchain/check/testdata/class/import_forward_decl.carbon
  24. 0 6
      toolchain/check/testdata/class/import_indirect.carbon
  25. 0 1
      toolchain/check/testdata/class/import_member_cycle.carbon
  26. 0 1
      toolchain/check/testdata/class/import_struct_cyle.carbon
  27. 0 1
      toolchain/check/testdata/function/builtin/call_from_operator.carbon
  28. 0 2
      toolchain/check/testdata/function/declaration/no_prelude/fail_import_incomplete_return.carbon
  29. 1 1
      toolchain/check/testdata/function/declaration/no_prelude/fail_redecl.carbon
  30. 3 3
      toolchain/check/testdata/function/definition/no_prelude/fail_decl_param_mismatch.carbon
  31. 2 2
      toolchain/check/testdata/function/generic/redeclare.carbon
  32. 8 8
      toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon
  33. 0 2
      toolchain/check/testdata/impl/lookup/import.carbon
  34. 0 2
      toolchain/check/testdata/impl/lookup/no_prelude/import.carbon
  35. 0 1
      toolchain/check/testdata/impl/no_prelude/import_self.carbon
  36. 0 3
      toolchain/check/testdata/interface/no_prelude/import.carbon
  37. 0 2
      toolchain/check/testdata/operators/overloaded/add.carbon
  38. 0 2
      toolchain/check/testdata/operators/overloaded/bit_and.carbon
  39. 0 1
      toolchain/check/testdata/operators/overloaded/bit_complement.carbon
  40. 0 2
      toolchain/check/testdata/operators/overloaded/bit_or.carbon
  41. 0 2
      toolchain/check/testdata/operators/overloaded/bit_xor.carbon
  42. 0 1
      toolchain/check/testdata/operators/overloaded/dec.carbon
  43. 0 2
      toolchain/check/testdata/operators/overloaded/div.carbon
  44. 0 3
      toolchain/check/testdata/operators/overloaded/eq.carbon
  45. 0 2
      toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon
  46. 0 4
      toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon
  47. 0 2
      toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon
  48. 0 1
      toolchain/check/testdata/operators/overloaded/inc.carbon
  49. 0 2
      toolchain/check/testdata/operators/overloaded/left_shift.carbon
  50. 0 2
      toolchain/check/testdata/operators/overloaded/mod.carbon
  51. 0 2
      toolchain/check/testdata/operators/overloaded/mul.carbon
  52. 0 1
      toolchain/check/testdata/operators/overloaded/negate.carbon
  53. 0 2
      toolchain/check/testdata/operators/overloaded/ordered.carbon
  54. 0 2
      toolchain/check/testdata/operators/overloaded/right_shift.carbon
  55. 0 2
      toolchain/check/testdata/operators/overloaded/sub.carbon
  56. 6 4
      toolchain/diagnostics/diagnostic_kind.def
  57. 4 0
      toolchain/sem_ir/class.h

+ 7 - 2
toolchain/check/class.cpp

@@ -17,8 +17,11 @@ auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
   SemIRLoc prev_loc =
       prev_class.is_defined() ? prev_class.definition_id : prev_class.decl_id;
 
-  // TODO: Check that the generic parameter list agrees with the prior
-  // declaration.
+  // Check the generic parameters match, if they were specified.
+  if (!CheckRedeclParamsMatch(context, DeclParams(new_class),
+                              DeclParams(prev_class), {})) {
+    return false;
+  }
 
   CheckIsAllowedRedecl(context, Lex::TokenKind::Class, prev_class.name_id,
                        {.loc = new_loc,
@@ -43,6 +46,8 @@ auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
   }
 
   if (new_is_definition) {
+    prev_class.implicit_param_refs_id = new_class.implicit_param_refs_id;
+    prev_class.param_refs_id = new_class.param_refs_id;
     prev_class.definition_id = new_class.definition_id;
     prev_class.scope_id = new_class.scope_id;
     prev_class.body_block_id = new_class.body_block_id;

+ 5 - 129
toolchain/check/function.cpp

@@ -10,141 +10,17 @@
 
 namespace Carbon::Check {
 
-// Returns true if there was an error in declaring the function, which will have
-// previously been diagnosed.
-static auto FunctionDeclHasError(Context& context, const SemIR::Function& fn)
-    -> bool {
-  if (fn.return_type_id == SemIR::TypeId::Error) {
-    return true;
-  }
-  for (auto param_refs_id : {fn.implicit_param_refs_id, fn.param_refs_id}) {
-    if (param_refs_id != SemIR::InstBlockId::Empty) {
-      for (auto param_id : context.inst_blocks().Get(param_refs_id)) {
-        if (context.insts().Get(param_id).type_id() == SemIR::TypeId::Error) {
-          return true;
-        }
-      }
-    }
-  }
-  return false;
-}
-
-// Returns false if a param differs for a redeclaration. The caller is expected
-// to provide a diagnostic.
-static auto CheckRedeclParam(Context& context,
-                             llvm::StringLiteral param_diag_label,
-                             int32_t param_index,
-                             SemIR::InstId new_param_ref_id,
-                             SemIR::InstId prev_param_ref_id,
-                             Substitutions substitutions) -> bool {
-  // TODO: Consider differentiating between type and name mistakes. For now,
-  // taking the simpler approach because I also think we may want to refactor
-  // params.
-  auto diagnose = [&]() {
-    CARBON_DIAGNOSTIC(FunctionRedeclParamDiffers, Error,
-                      "Function redeclaration differs at {0}parameter {1}.",
-                      llvm::StringLiteral, int32_t);
-    CARBON_DIAGNOSTIC(FunctionRedeclParamPrevious, Note,
-                      "Previous declaration's corresponding {0}parameter here.",
-                      llvm::StringLiteral);
-    context.emitter()
-        .Build(new_param_ref_id, FunctionRedeclParamDiffers, param_diag_label,
-               param_index + 1)
-        .Note(prev_param_ref_id, FunctionRedeclParamPrevious, param_diag_label)
-        .Emit();
-  };
-
-  auto new_param_ref = context.insts().Get(new_param_ref_id);
-  auto prev_param_ref = context.insts().Get(prev_param_ref_id);
-  if (new_param_ref.kind() != prev_param_ref.kind() ||
-      new_param_ref.type_id() !=
-          SubstType(context, prev_param_ref.type_id(), substitutions)) {
-    diagnose();
-    return false;
-  }
-
-  if (new_param_ref.Is<SemIR::AddrPattern>()) {
-    new_param_ref =
-        context.insts().Get(new_param_ref.As<SemIR::AddrPattern>().inner_id);
-    prev_param_ref =
-        context.insts().Get(prev_param_ref.As<SemIR::AddrPattern>().inner_id);
-    if (new_param_ref.kind() != prev_param_ref.kind()) {
-      diagnose();
-      return false;
-    }
-  }
-
-  if (new_param_ref.Is<SemIR::AnyBindName>()) {
-    new_param_ref =
-        context.insts().Get(new_param_ref.As<SemIR::AnyBindName>().value_id);
-    prev_param_ref =
-        context.insts().Get(prev_param_ref.As<SemIR::AnyBindName>().value_id);
-  }
-
-  auto new_param = new_param_ref.As<SemIR::Param>();
-  auto prev_param = prev_param_ref.As<SemIR::Param>();
-  if (new_param.name_id != prev_param.name_id) {
-    diagnose();
-    return false;
-  }
-
-  return true;
-}
-
-// Returns false if the param refs differ for a redeclaration.
-static auto CheckRedeclParams(Context& context, SemIR::InstId new_decl_id,
-                              SemIR::InstBlockId new_param_refs_id,
-                              SemIR::InstId prev_decl_id,
-                              SemIR::InstBlockId prev_param_refs_id,
-                              llvm::StringLiteral param_diag_label,
-                              Substitutions substitutions) -> bool {
-  // This will often occur for empty params.
-  if (new_param_refs_id == prev_param_refs_id) {
-    return true;
-  }
-  const auto new_param_ref_ids = context.inst_blocks().Get(new_param_refs_id);
-  const auto prev_param_ref_ids = context.inst_blocks().Get(prev_param_refs_id);
-  if (new_param_ref_ids.size() != prev_param_ref_ids.size()) {
-    CARBON_DIAGNOSTIC(
-        FunctionRedeclParamCountDiffers, Error,
-        "Function redeclaration differs because of {0}parameter count of {1}.",
-        llvm::StringLiteral, int32_t);
-    CARBON_DIAGNOSTIC(FunctionRedeclParamCountPrevious, Note,
-                      "Previously declared with {0}parameter count of {1}.",
-                      llvm::StringLiteral, int32_t);
-    context.emitter()
-        .Build(new_decl_id, FunctionRedeclParamCountDiffers, param_diag_label,
-               new_param_ref_ids.size())
-        .Note(prev_decl_id, FunctionRedeclParamCountPrevious, param_diag_label,
-              prev_param_ref_ids.size())
-        .Emit();
-    return false;
-  }
-  for (auto [index, new_param_ref_id, prev_param_ref_id] :
-       llvm::enumerate(new_param_ref_ids, prev_param_ref_ids)) {
-    if (!CheckRedeclParam(context, param_diag_label, index, new_param_ref_id,
-                          prev_param_ref_id, substitutions)) {
-      return false;
-    }
-  }
-  return true;
-}
-
 // Returns false if the provided function declarations differ.
 static auto CheckRedecl(Context& context, const SemIR::Function& new_function,
                         const SemIR::Function& prev_function,
                         Substitutions substitutions) -> bool {
-  if (FunctionDeclHasError(context, new_function) ||
-      FunctionDeclHasError(context, prev_function)) {
+  if (!CheckRedeclParamsMatch(context, DeclParams(new_function),
+                              DeclParams(prev_function), substitutions)) {
     return false;
   }
-  if (!CheckRedeclParams(
-          context, new_function.decl_id, new_function.implicit_param_refs_id,
-          prev_function.decl_id, prev_function.implicit_param_refs_id,
-          "implicit ", substitutions) ||
-      !CheckRedeclParams(context, new_function.decl_id,
-                         new_function.param_refs_id, prev_function.decl_id,
-                         prev_function.param_refs_id, "", substitutions)) {
+
+  if (new_function.return_type_id == SemIR::TypeId::Error ||
+      prev_function.return_type_id == SemIR::TypeId::Error) {
     return false;
   }
   auto prev_return_type_id =

+ 8 - 6
toolchain/check/handle_class.cpp

@@ -86,12 +86,12 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
 static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
                            bool is_definition)
     -> std::tuple<SemIR::ClassId, SemIR::InstId> {
-  if (context.node_stack().PopIf<Parse::NodeKind::TuplePattern>()) {
-    context.TODO(node_id, "generic class");
-  }
-  if (context.node_stack().PopIf<Parse::NodeKind::ImplicitParamList>()) {
-    context.TODO(node_id, "generic class");
-  }
+  auto param_refs_id =
+      context.node_stack().PopIf<Parse::NodeKind::TuplePattern>().value_or(
+          SemIR::InstBlockId::Invalid);
+  auto implicit_param_refs_id =
+      context.node_stack().PopIf<Parse::NodeKind::ImplicitParamList>().value_or(
+          SemIR::InstBlockId::Invalid);
 
   auto name_context = context.decl_name_stack().FinishName();
   context.node_stack()
@@ -132,6 +132,8 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
   SemIR::Class class_info = {
       .name_id = name_context.name_id_for_new_inst(),
       .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(),
+      .implicit_param_refs_id = implicit_param_refs_id,
+      .param_refs_id = param_refs_id,
       // `.self_type_id` depends on the ClassType, so is set below.
       .self_type_id = SemIR::TypeId::Invalid,
       .decl_id = class_decl_id,

+ 2 - 0
toolchain/check/handle_function.cpp

@@ -134,6 +134,8 @@ static auto BuildFunctionDecl(Context& context,
 
   SemIR::InstBlockId param_refs_id =
       context.node_stack().Pop<Parse::NodeKind::TuplePattern>();
+  // TODO: Use Invalid rather than Empty if there was no implicit parameter
+  // list.
   SemIR::InstBlockId implicit_param_refs_id =
       context.node_stack().PopIf<Parse::NodeKind::ImplicitParamList>().value_or(
           SemIR::InstBlockId::Empty);

+ 26 - 10
toolchain/check/import_ref.cpp

@@ -317,11 +317,13 @@ class ImportRefResolver {
   // to work_stack_.
   auto GetLocalParamConstantIds(SemIR::InstBlockId param_refs_id)
       -> llvm::SmallVector<SemIR::ConstantId> {
-    if (param_refs_id == SemIR::InstBlockId::Empty) {
-      return {};
+    llvm::SmallVector<SemIR::ConstantId> const_ids;
+    if (!param_refs_id.is_valid() ||
+        param_refs_id == SemIR::InstBlockId::Empty) {
+      return const_ids;
     }
+
     const auto& param_refs = import_ir_.inst_blocks().Get(param_refs_id);
-    llvm::SmallVector<SemIR::ConstantId> const_ids;
     const_ids.reserve(param_refs.size());
     for (auto inst_id : param_refs) {
       const_ids.push_back(
@@ -345,8 +347,9 @@ class ImportRefResolver {
       SemIR::InstBlockId param_refs_id,
       const llvm::SmallVector<SemIR::ConstantId>& const_ids)
       -> SemIR::InstBlockId {
-    if (param_refs_id == SemIR::InstBlockId::Empty) {
-      return SemIR::InstBlockId::Empty;
+    if (!param_refs_id.is_valid() ||
+        param_refs_id == SemIR::InstBlockId::Empty) {
+      return param_refs_id;
     }
     const auto& param_refs = import_ir_.inst_blocks().Get(param_refs_id);
     llvm::SmallVector<SemIR::InstId> new_param_refs;
@@ -394,13 +397,16 @@ class ImportRefResolver {
           case SemIR::BindSymbolicName::Kind: {
             // The symbolic name will be created on first reference, so might
             // already exist. Update the value in it to refer to the parameter.
+            auto new_bind_inst_id = GetLocalConstantId(bind_id).inst_id();
             auto new_bind_inst =
                 context_.insts().GetAs<SemIR::BindSymbolicName>(
-                    GetLocalConstantId(bind_id).inst_id());
+                    new_bind_inst_id);
             new_bind_inst.value_id = new_param_id;
             // This is not before constant use, but doesn't change the
             // constant value of the instruction.
-            context_.ReplaceInstBeforeConstantUse(bind_id, new_bind_inst);
+            context_.ReplaceInstBeforeConstantUse(new_bind_inst_id,
+                                                  new_bind_inst);
+            new_param_id = new_bind_inst_id;
             break;
           }
           default: {
@@ -682,14 +688,16 @@ class ImportRefResolver {
     auto class_decl =
         SemIR::ClassDecl{SemIR::TypeId::TypeType, SemIR::ClassId::Invalid,
                          SemIR::InstBlockId::Empty};
-    auto class_decl_id = context_.AddPlaceholderInst(
+    auto class_decl_id = context_.AddPlaceholderInstInNoBlock(
         {AddImportIRInst(import_class.decl_id), class_decl});
     // 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({
         .name_id = GetLocalNameId(import_class.name_id),
-        // Set in the second pass once we've imported it.
+        // Set in the second pass once we've imported them.
         .enclosing_scope_id = SemIR::NameScopeId::Invalid,
+        .implicit_param_refs_id = SemIR::InstBlockId::Invalid,
+        .param_refs_id = SemIR::InstBlockId::Invalid,
         // `.self_type_id` depends on the ClassType, so is set below.
         .self_type_id = SemIR::TypeId::Invalid,
         .decl_id = class_decl_id,
@@ -758,6 +766,10 @@ class ImportRefResolver {
 
     auto enclosing_scope_id =
         GetLocalNameScopeId(import_class.enclosing_scope_id);
+    llvm::SmallVector<SemIR::ConstantId> implicit_param_const_ids =
+        GetLocalParamConstantIds(import_class.implicit_param_refs_id);
+    llvm::SmallVector<SemIR::ConstantId> param_const_ids =
+        GetLocalParamConstantIds(import_class.param_refs_id);
     auto object_repr_const_id =
         import_class.object_repr_id.is_valid()
             ? GetLocalConstantId(import_class.object_repr_id)
@@ -775,6 +787,10 @@ class ImportRefResolver {
             .GetAs<SemIR::ClassType>(class_const_id.inst_id())
             .class_id);
     new_class.enclosing_scope_id = enclosing_scope_id;
+    new_class.implicit_param_refs_id = GetLocalParamRefsId(
+        import_class.implicit_param_refs_id, implicit_param_const_ids);
+    new_class.param_refs_id =
+        GetLocalParamRefsId(import_class.param_refs_id, param_const_ids);
 
     if (import_class.is_defined()) {
       AddClassDefinition(import_class, new_class, object_repr_const_id,
@@ -929,7 +945,7 @@ class ImportRefResolver {
     auto interface_decl = SemIR::InterfaceDecl{SemIR::TypeId::TypeType,
                                                SemIR::InterfaceId::Invalid,
                                                SemIR::InstBlockId::Empty};
-    auto interface_decl_id = context_.AddPlaceholderInst(
+    auto interface_decl_id = context_.AddPlaceholderInstInNoBlock(
         {AddImportIRInst(import_interface.decl_id), interface_decl});
 
     // Start with an incomplete interface.

+ 157 - 0
toolchain/check/merge.cpp

@@ -148,4 +148,161 @@ auto ReplacePrevInstForMerge(Context& context, SemIR::NameScopeId scope_id,
   }
 }
 
+// Returns true if there was an error in declaring the entity, which will have
+// previously been diagnosed.
+static auto EntityHasParamError(Context& context, const DeclParams& info)
+    -> bool {
+  for (auto param_refs_id : {info.implicit_param_refs_id, info.param_refs_id}) {
+    if (param_refs_id.is_valid() &&
+        param_refs_id != SemIR::InstBlockId::Empty) {
+      for (auto param_id : context.inst_blocks().Get(param_refs_id)) {
+        if (context.insts().Get(param_id).type_id() == SemIR::TypeId::Error) {
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
+
+// Returns false if a param differs for a redeclaration. The caller is expected
+// to provide a diagnostic.
+static auto CheckRedeclParam(Context& context,
+                             llvm::StringLiteral param_diag_label,
+                             int32_t param_index,
+                             SemIR::InstId new_param_ref_id,
+                             SemIR::InstId prev_param_ref_id,
+                             Substitutions substitutions) -> bool {
+  // TODO: Consider differentiating between type and name mistakes. For now,
+  // taking the simpler approach because I also think we may want to refactor
+  // params.
+  auto diagnose = [&]() {
+    CARBON_DIAGNOSTIC(RedeclParamDiffers, Error,
+                      "Redeclaration differs at {0}parameter {1}.",
+                      llvm::StringLiteral, int32_t);
+    CARBON_DIAGNOSTIC(RedeclParamPrevious, Note,
+                      "Previous declaration's corresponding {0}parameter here.",
+                      llvm::StringLiteral);
+    context.emitter()
+        .Build(new_param_ref_id, RedeclParamDiffers, param_diag_label,
+               param_index + 1)
+        .Note(prev_param_ref_id, RedeclParamPrevious, param_diag_label)
+        .Emit();
+  };
+
+  auto new_param_ref = context.insts().Get(new_param_ref_id);
+  auto prev_param_ref = context.insts().Get(prev_param_ref_id);
+  if (new_param_ref.kind() != prev_param_ref.kind() ||
+      new_param_ref.type_id() !=
+          SubstType(context, prev_param_ref.type_id(), substitutions)) {
+    diagnose();
+    return false;
+  }
+
+  if (new_param_ref.Is<SemIR::AddrPattern>()) {
+    new_param_ref =
+        context.insts().Get(new_param_ref.As<SemIR::AddrPattern>().inner_id);
+    prev_param_ref =
+        context.insts().Get(prev_param_ref.As<SemIR::AddrPattern>().inner_id);
+    if (new_param_ref.kind() != prev_param_ref.kind()) {
+      diagnose();
+      return false;
+    }
+  }
+
+  if (new_param_ref.Is<SemIR::AnyBindName>()) {
+    new_param_ref =
+        context.insts().Get(new_param_ref.As<SemIR::AnyBindName>().value_id);
+    prev_param_ref =
+        context.insts().Get(prev_param_ref.As<SemIR::AnyBindName>().value_id);
+  }
+
+  auto new_param = new_param_ref.As<SemIR::Param>();
+  auto prev_param = prev_param_ref.As<SemIR::Param>();
+  if (new_param.name_id != prev_param.name_id) {
+    diagnose();
+    return false;
+  }
+
+  return true;
+}
+
+// Returns false if the param refs differ for a redeclaration.
+static auto CheckRedeclParams(Context& context, SemIR::InstId new_decl_id,
+                              SemIR::InstBlockId new_param_refs_id,
+                              SemIR::InstId prev_decl_id,
+                              SemIR::InstBlockId prev_param_refs_id,
+                              llvm::StringLiteral param_diag_label,
+                              Substitutions substitutions) -> bool {
+  // This will often occur for empty params.
+  if (new_param_refs_id == prev_param_refs_id) {
+    return true;
+  }
+
+  // If exactly one of the parameter lists was present, they differ.
+  if (new_param_refs_id.is_valid() != prev_param_refs_id.is_valid()) {
+    CARBON_DIAGNOSTIC(RedeclParamListDiffers, Error,
+                      "Redeclaration differs because of {1}{0}parameter list.",
+                      llvm::StringLiteral, llvm::StringLiteral);
+    CARBON_DIAGNOSTIC(RedeclParamListPrevious, Note,
+                      "Previously declared with{1} {0}parameter list.",
+                      llvm::StringLiteral, llvm::StringLiteral);
+    context.emitter()
+        .Build(
+            new_decl_id, RedeclParamListDiffers, param_diag_label,
+            new_param_refs_id.is_valid() ? llvm::StringLiteral("") : "missing ")
+        .Note(prev_decl_id, RedeclParamListPrevious, param_diag_label,
+              prev_param_refs_id.is_valid() ? llvm::StringLiteral("") : "out")
+        .Emit();
+    return false;
+  }
+
+  CARBON_CHECK(new_param_refs_id.is_valid() && prev_param_refs_id.is_valid());
+  const auto new_param_ref_ids = context.inst_blocks().Get(new_param_refs_id);
+  const auto prev_param_ref_ids = context.inst_blocks().Get(prev_param_refs_id);
+  if (new_param_ref_ids.size() != prev_param_ref_ids.size()) {
+    CARBON_DIAGNOSTIC(
+        RedeclParamCountDiffers, Error,
+        "Redeclaration differs because of {0}parameter count of {1}.",
+        llvm::StringLiteral, int32_t);
+    CARBON_DIAGNOSTIC(RedeclParamCountPrevious, Note,
+                      "Previously declared with {0}parameter count of {1}.",
+                      llvm::StringLiteral, int32_t);
+    context.emitter()
+        .Build(new_decl_id, RedeclParamCountDiffers, param_diag_label,
+               new_param_ref_ids.size())
+        .Note(prev_decl_id, RedeclParamCountPrevious, param_diag_label,
+              prev_param_ref_ids.size())
+        .Emit();
+    return false;
+  }
+  for (auto [index, new_param_ref_id, prev_param_ref_id] :
+       llvm::enumerate(new_param_ref_ids, prev_param_ref_ids)) {
+    if (!CheckRedeclParam(context, param_diag_label, index, new_param_ref_id,
+                          prev_param_ref_id, substitutions)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity,
+                            const DeclParams& prev_entity,
+                            Substitutions substitutions) -> bool {
+  if (EntityHasParamError(context, new_entity) ||
+      EntityHasParamError(context, prev_entity)) {
+    return false;
+  }
+  if (!CheckRedeclParams(context, new_entity.decl_id,
+                         new_entity.implicit_param_refs_id, prev_entity.decl_id,
+                         prev_entity.implicit_param_refs_id, "implicit ",
+                         substitutions) ||
+      !CheckRedeclParams(context, new_entity.decl_id, new_entity.param_refs_id,
+                         prev_entity.decl_id, prev_entity.param_refs_id, "",
+                         substitutions)) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace Carbon::Check

+ 27 - 1
toolchain/check/merge.h

@@ -6,8 +6,8 @@
 #define CARBON_TOOLCHAIN_CHECK_MERGE_H_
 
 #include "toolchain/check/context.h"
+#include "toolchain/check/subst.h"
 #include "toolchain/sem_ir/ids.h"
-#include "toolchain/sem_ir/import_ir.h"
 
 namespace Carbon::Check {
 
@@ -56,6 +56,32 @@ auto ReplacePrevInstForMerge(Context& context, SemIR::NameScopeId scope_id,
                              SemIR::NameId name_id, SemIR::InstId new_inst_id)
     -> void;
 
+// Information about the parameters of a declaration, which is common across
+// different kinds of entity such as classes and functions.
+struct DeclParams {
+  template <typename Entity>
+  explicit DeclParams(const Entity& entity)
+      : decl_id(entity.decl_id),
+        implicit_param_refs_id(entity.implicit_param_refs_id),
+        param_refs_id(entity.param_refs_id) {}
+
+  // The declaration of the entity.
+  SemIR::InstId decl_id;
+  // The implicit parameters of the entity. Can be Invalid if there is no
+  // implicit parameter list.
+  SemIR::InstBlockId implicit_param_refs_id;
+  // The explicit parameters of the entity. Can be Invalid if there is no
+  // explicit parameter list.
+  SemIR::InstBlockId param_refs_id;
+};
+
+// Checks that the parameters in a redeclaration of an entity match the
+// parameters in the prior declaration. If not, produces a diagnostic and
+// returns false.
+auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity,
+                            const DeclParams& prev_entity,
+                            Substitutions substitutions) -> bool;
+
 }  // namespace Carbon::Check
 
 #endif  // CARBON_TOOLCHAIN_CHECK_MERGE_H_

+ 0 - 2
toolchain/check/testdata/alias/no_prelude/import.carbon

@@ -111,7 +111,6 @@ var c: () = a_alias_alias;
 // CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+1, unloaded
 // CHECK:STDOUT:   %import_ref.2: type = import_ref ir1, inst+5, loaded [template = constants.%C]
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+10, unloaded
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+2, unloaded
 // CHECK:STDOUT:   %c_alias.ref.loc6: type = name_ref c_alias, %import_ref.2 [template = constants.%C]
 // CHECK:STDOUT:   %c_alias_alias: type = bind_alias c_alias_alias, %import_ref.2 [template = constants.%C]
@@ -142,7 +141,6 @@ var c: () = a_alias_alias;
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+9, loaded [template = constants.%C]
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+14, unloaded
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+7, unloaded
 // CHECK:STDOUT:   %c_alias_alias.ref: type = name_ref c_alias_alias, %import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:   %.loc6: type = ptr_type C [template = constants.%.2]

+ 0 - 1
toolchain/check/testdata/alias/no_prelude/import_order.carbon

@@ -93,7 +93,6 @@ var a_val: a = {.v = b_val.v};
 // CHECK:STDOUT:   %import_ref.3: type = import_ref ir1, inst+14, loaded [template = constants.%C]
 // CHECK:STDOUT:   %import_ref.4: type = import_ref ir1, inst+16, loaded [template = constants.%C]
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir1, inst+18, loaded [template = constants.%C]
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.6: <unbound element of class C> = import_ref ir1, inst+7, loaded [template = imports.%.1]
 // CHECK:STDOUT:   %import_ref.7 = import_ref ir1, inst+2, unloaded
 // CHECK:STDOUT:   %d.ref: type = name_ref d, %import_ref.5 [template = constants.%C]

+ 0 - 4
toolchain/check/testdata/class/cross_package_import.carbon

@@ -185,7 +185,6 @@ var c: Other.C = {};
 // CHECK:STDOUT:   %Other: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, %Other [template = %Other]
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C]
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir2, inst+3, unloaded
 // CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:   %c.var: ref C = var c
@@ -223,7 +222,6 @@ var c: Other.C = {};
 // CHECK:STDOUT:   %Other: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, %Other [template = %Other]
 // CHECK:STDOUT:   %import_ref: type = import_ref ir2, inst+2, loaded [template = constants.%C]
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref [template = constants.%C]
 // CHECK:STDOUT:   %c.var: ref <error> = var c
 // CHECK:STDOUT:   %c: ref <error> = bind_name c, %c.var
@@ -258,7 +256,6 @@ var c: Other.C = {};
 // CHECK:STDOUT:   %Other: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, %Other [template = %Other]
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir2, inst+2, loaded [template = constants.%C]
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir2, inst+3, unloaded
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir3, inst+2, unloaded
 // CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C]
@@ -300,7 +297,6 @@ var c: Other.C = {};
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Other.ref: <namespace> = name_ref Other, %Other [template = %Other]
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+2, loaded [template = constants.%C]
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unloaded
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir2, inst+2, unloaded
 // CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C]

+ 8 - 16
toolchain/check/testdata/class/extern.carbon

@@ -555,13 +555,11 @@ extern class C;
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl.loc6
+// CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref: type = import_ref ir1, inst+2, loaded [template = constants.%C]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl.loc6: type = class_decl @C [template = constants.%C] {
-// CHECK:STDOUT:     %C.decl.1: type = class_decl @C [template = constants.%C] {}
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C;
@@ -575,13 +573,11 @@ extern class C;
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl.loc6
+// CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref: type = import_ref ir1, inst+2, loaded [template = constants.%C]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl.loc6: type = class_decl @C [template = constants.%C] {
-// CHECK:STDOUT:     %C.decl.1: type = class_decl @C [template = constants.%C] {}
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C;
@@ -596,14 +592,12 @@ extern class C;
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl.loc6
+// CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+2, loaded [template = constants.%C]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unloaded
-// CHECK:STDOUT:   %C.decl.loc6: type = class_decl @C [template = constants.%C] {
-// CHECK:STDOUT:     %C.decl.1: type = class_decl @C [template = constants.%C] {}
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
@@ -621,14 +615,12 @@ extern class C;
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .C = %C.decl.loc6
+// CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+2, loaded [template = constants.%C]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unloaded
-// CHECK:STDOUT:   %C.decl.loc6: type = class_decl @C [template = constants.%C] {
-// CHECK:STDOUT:     %C.decl.1: type = class_decl @C [template = constants.%C] {}
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {

+ 2 - 5
toolchain/check/testdata/class/fail_import_misuses.carbon

@@ -81,17 +81,14 @@ var a: Incomplete;
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .Incomplete = %import_ref.2
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .Empty = %Empty.decl.loc16
+// CHECK:STDOUT:     .Empty = %Empty.decl
 // CHECK:STDOUT:     .a = %a
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+2, loaded [template = constants.%Empty]
 // CHECK:STDOUT:   %import_ref.2: type = import_ref ir1, inst+5, loaded [template = constants.%Incomplete]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+3, unloaded
-// CHECK:STDOUT:   %Empty.decl.loc16: type = class_decl @Empty [template = constants.%Empty] {
-// CHECK:STDOUT:     %Empty.decl.1: type = class_decl @Empty [template = constants.%Empty] {}
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Incomplete.decl: type = class_decl @Incomplete [template = constants.%Incomplete] {}
+// CHECK:STDOUT:   %Empty.decl: type = class_decl @Empty [template = constants.%Empty] {}
 // CHECK:STDOUT:   %Incomplete.ref: type = name_ref Incomplete, %import_ref.2 [template = constants.%Incomplete]
 // CHECK:STDOUT:   %a.var: ref <error> = var a
 // CHECK:STDOUT:   %a: ref <error> = bind_name a, %a.var

+ 13 - 17
toolchain/check/testdata/class/fail_todo_generic_method.carbon

@@ -4,10 +4,6 @@
 //
 // AUTOUPDATE
 
-// CHECK:STDERR: fail_todo_generic_method.carbon:[[@LINE+4]]:1: ERROR: Semantics TODO: `generic class`.
-// CHECK:STDERR: class Class(T:! type) {
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR:
 class Class(T:! type) {
   var a: T;
   fn F[self: Self](n: T);
@@ -46,34 +42,34 @@ fn Class(T:! type).F[self: Self](n: T) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Class.decl: type = class_decl @Class [template = constants.%Class] {
-// CHECK:STDOUT:     %T.loc11_13.1: type = param T
-// CHECK:STDOUT:     %T.loc11_13.2: type = bind_symbolic_name T 0, %T.loc11_13.1 [symbolic = constants.%T]
+// CHECK:STDOUT:     %T.loc7_13.1: type = param T
+// CHECK:STDOUT:     %T.loc7_13.2: type = bind_symbolic_name T 0, %T.loc7_13.1 [symbolic = constants.%T]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.decl: <invalid> = fn_decl @.1 [template = constants.%struct.2] {
-// CHECK:STDOUT:     %T.loc26_10.1: type = param T
-// CHECK:STDOUT:     @.1.%T: type = bind_symbolic_name T 0, %T.loc26_10.1 [symbolic = constants.%T]
+// CHECK:STDOUT:     %T.loc22_10.1: type = param T
+// CHECK:STDOUT:     @.1.%T: type = bind_symbolic_name T 0, %T.loc22_10.1 [symbolic = constants.%T]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Class {
-// CHECK:STDOUT:   %T.ref.loc12: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T]
-// CHECK:STDOUT:   %.loc12: <unbound element of class Class> = field_decl a, element0 [template]
+// CHECK:STDOUT:   %T.ref.loc8: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:   %.loc8: <unbound element of class Class> = field_decl a, element0 [template]
 // CHECK:STDOUT:   %F.decl: F = fn_decl @F [template = constants.%struct.1] {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%Class [template = constants.%Class]
-// CHECK:STDOUT:     %self.loc13_8.1: Class = param self
-// CHECK:STDOUT:     %self.loc13_8.2: Class = bind_name self, %self.loc13_8.1
-// CHECK:STDOUT:     %T.ref.loc13: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T]
-// CHECK:STDOUT:     %n.loc13_20.1: T = param n
-// CHECK:STDOUT:     %n.loc13_20.2: T = bind_name n, %n.loc13_20.1
+// CHECK:STDOUT:     %self.loc9_8.1: Class = param self
+// CHECK:STDOUT:     %self.loc9_8.2: Class = bind_name self, %self.loc9_8.1
+// CHECK:STDOUT:     %T.ref.loc9: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %n.loc9_20.1: T = param n
+// CHECK:STDOUT:     %n.loc9_20.2: T = bind_name n, %n.loc9_20.1
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%Class
-// CHECK:STDOUT:   .a = %.loc12
+// CHECK:STDOUT:   .a = %.loc8
 // CHECK:STDOUT:   .F = %F.decl
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F[@Class.%self.loc13_8.2: Class](@Class.%n.loc13_20.2: T);
+// CHECK:STDOUT: fn @F[@Class.%self.loc9_8.2: Class](@Class.%n.loc9_20.2: T);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @.1(%T: type);
 // CHECK:STDOUT:

+ 1 - 8
toolchain/check/testdata/class/fail_todo_generic.carbon → toolchain/check/testdata/class/generic.carbon

@@ -4,16 +4,9 @@
 //
 // AUTOUPDATE
 
-// CHECK:STDERR: fail_todo_generic.carbon:[[@LINE+7]]:1: ERROR: Semantics TODO: `generic class`.
-// CHECK:STDERR: class C[]();
-// CHECK:STDERR: ^~~~~~~~~~~~
-// CHECK:STDERR:
-// CHECK:STDERR: fail_todo_generic.carbon:[[@LINE+3]]:1: ERROR: Semantics TODO: `generic class`.
-// CHECK:STDERR: class C[]();
-// CHECK:STDERR: ^~~~~~~~~~~~
 class C[]();
 
-// CHECK:STDOUT: --- fail_todo_generic.carbon
+// CHECK:STDOUT: --- generic.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C: type = class_type @C [template]

+ 96 - 0
toolchain/check/testdata/class/generic/basic.carbon

@@ -0,0 +1,96 @@
+// 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
+
+class Class(T:! type) {
+  fn GetAddr[addr self: Self*]() -> T* {
+    return &self->k;
+  }
+
+  // TODO: Should this work? T is not necessarily copyable.
+  fn GetValue[self: Self]() -> T {
+    return self.k;
+  }
+
+  var k: T;
+}
+
+
+// CHECK:STDOUT: --- basic.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Class: type = class_type @Class [template]
+// CHECK:STDOUT:   %.1: type = ptr_type Class [template]
+// CHECK:STDOUT:   %.2: type = ptr_type T [symbolic]
+// CHECK:STDOUT:   %GetAddr: type = fn_type @GetAddr [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct.1: GetAddr = struct_value () [template]
+// CHECK:STDOUT:   %GetValue: type = fn_type @GetValue [template]
+// CHECK:STDOUT:   %struct.2: GetValue = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = unbound_element_type Class, T [symbolic]
+// CHECK:STDOUT:   %.5: type = struct_type {.k: T} [symbolic]
+// CHECK:STDOUT:   %.6: type = ptr_type {.k: T} [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Class = %Class.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Class.decl: type = class_decl @Class [template = constants.%Class] {
+// CHECK:STDOUT:     %T.loc7_13.1: type = param T
+// CHECK:STDOUT:     %T.loc7_13.2: type = bind_symbolic_name T 0, %T.loc7_13.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %GetAddr.decl: GetAddr = fn_decl @GetAddr [template = constants.%struct.1] {
+// CHECK:STDOUT:     %Self.ref.loc8: type = name_ref Self, constants.%Class [template = constants.%Class]
+// CHECK:STDOUT:     %.loc8_29: type = ptr_type Class [template = constants.%.1]
+// CHECK:STDOUT:     %self.loc8_19.1: Class* = param self
+// CHECK:STDOUT:     %self.loc8_19.3: Class* = bind_name self, %self.loc8_19.1
+// CHECK:STDOUT:     %.loc8_14: Class* = addr_pattern %self.loc8_19.3
+// CHECK:STDOUT:     %T.ref.loc8: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %.loc8_38: type = ptr_type T [symbolic = constants.%.2]
+// CHECK:STDOUT:     %return.var.loc8: ref T* = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %GetValue.decl: GetValue = fn_decl @GetValue [template = constants.%struct.2] {
+// CHECK:STDOUT:     %Self.ref.loc13: type = name_ref Self, constants.%Class [template = constants.%Class]
+// CHECK:STDOUT:     %self.loc13_15.1: Class = param self
+// CHECK:STDOUT:     %self.loc13_15.2: Class = bind_name self, %self.loc13_15.1
+// CHECK:STDOUT:     %T.ref.loc13: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %return.var.loc13: ref T = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %T.ref.loc17: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:   %.loc17: <unbound element of class Class> = field_decl k, element0 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Class
+// CHECK:STDOUT:   .GetAddr = %GetAddr.decl
+// CHECK:STDOUT:   .GetValue = %GetValue.decl
+// CHECK:STDOUT:   .k = %.loc17
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @GetAddr[addr @Class.%self.loc8_19.3: Class*]() -> T* {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %self.ref: Class* = name_ref self, @Class.%self.loc8_19.3
+// CHECK:STDOUT:   %.loc9_17.1: ref Class = deref %self.ref
+// CHECK:STDOUT:   %k.ref: <unbound element of class Class> = name_ref k, @Class.%.loc17 [template = @Class.%.loc17]
+// CHECK:STDOUT:   %.loc9_17.2: ref T = class_element_access %.loc9_17.1, element0
+// CHECK:STDOUT:   %.loc9_12: T* = addr_of %.loc9_17.2
+// CHECK:STDOUT:   return %.loc9_12
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @GetValue[@Class.%self.loc13_15.2: Class]() -> T {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %self.ref: Class = name_ref self, @Class.%self.loc13_15.2
+// CHECK:STDOUT:   %k.ref: <unbound element of class Class> = name_ref k, @Class.%.loc17 [template = @Class.%.loc17]
+// CHECK:STDOUT:   %.loc14_16.1: ref T = class_element_access %self.ref, element0
+// CHECK:STDOUT:   %.loc14_16.2: T = bind_value %.loc14_16.1
+// CHECK:STDOUT:   return %.loc14_16.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 71 - 0
toolchain/check/testdata/class/generic/fail_todo_member_out_of_line.carbon

@@ -0,0 +1,71 @@
+// 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
+
+class Class(T:! type) {
+  fn F(n: T) -> T;
+}
+
+// CHECK:STDERR: fail_todo_member_out_of_line.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fn Class(T:! type).F(n: T) -> T {
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_todo_member_out_of_line.carbon:[[@LINE-7]]:1: Name is previously declared here.
+// CHECK:STDERR: class Class(T:! type) {
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_todo_member_out_of_line.carbon:[[@LINE+3]]:19: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition.
+// CHECK:STDERR: fn Class(T:! type).F(n: T) -> T {
+// CHECK:STDERR:                   ^
+fn Class(T:! type).F(n: T) -> T {
+  return n;
+}
+
+// CHECK:STDOUT: --- fail_todo_member_out_of_line.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Class: type = class_type @Class [template]
+// CHECK:STDOUT:   %F: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct.1: F = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %.3: type = fn_type @.1 [template]
+// CHECK:STDOUT:   %struct.2: <invalid> = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Class = %Class.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Class.decl: type = class_decl @Class [template = constants.%Class] {
+// CHECK:STDOUT:     %T.loc7_13.1: type = param T
+// CHECK:STDOUT:     %T.loc7_13.2: type = bind_symbolic_name T 0, %T.loc7_13.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.decl: <invalid> = fn_decl @.1 [template = constants.%struct.2] {
+// CHECK:STDOUT:     %T.loc21_10.1: type = param T
+// CHECK:STDOUT:     @.1.%T: type = bind_symbolic_name T 0, %T.loc21_10.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %F.decl: F = fn_decl @F [template = constants.%struct.1] {
+// CHECK:STDOUT:     %T.ref.loc8_11: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %n.loc8_8.1: T = param n
+// CHECK:STDOUT:     %n.loc8_8.2: T = bind_name n, %n.loc8_8.1
+// CHECK:STDOUT:     %T.ref.loc8_17: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %return.var: ref T = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Class
+// CHECK:STDOUT:   .F = %F.decl
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(@Class.%n.loc8_8.2: T) -> T;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @.1(%T: type);
+// CHECK:STDOUT:

+ 100 - 0
toolchain/check/testdata/class/generic/fail_todo_use.carbon

@@ -0,0 +1,100 @@
+// 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
+
+class Class(T:! type) {
+  fn Get[addr self: Self*]() -> T* {
+    return &self->k;
+  }
+
+  var k: T;
+}
+
+// TODO: The following should work.
+fn Run() -> i32 {
+  // CHECK:STDERR: fail_todo_use.carbon:[[@LINE+3]]:10: ERROR: Value of type `type` is not callable.
+  // CHECK:STDERR:   var v: Class(i32) = {.k = 0};
+  // CHECK:STDERR:          ^~~~~~
+  var v: Class(i32) = {.k = 0};
+  return v.Get();
+}
+
+// CHECK:STDOUT: --- fail_todo_use.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Class: type = class_type @Class [template]
+// CHECK:STDOUT:   %.1: type = ptr_type Class [template]
+// CHECK:STDOUT:   %.2: type = ptr_type T [symbolic]
+// CHECK:STDOUT:   %Get: type = fn_type @Get [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct.1: Get = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = unbound_element_type Class, T [symbolic]
+// CHECK:STDOUT:   %.5: type = struct_type {.k: T} [symbolic]
+// CHECK:STDOUT:   %.6: type = ptr_type {.k: T} [symbolic]
+// CHECK:STDOUT:   %Run: type = fn_type @Run [template]
+// CHECK:STDOUT:   %struct.2: Run = struct_value () [template]
+// CHECK:STDOUT:   %.7: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.8: type = struct_type {.k: i32} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Class = %Class.decl
+// CHECK:STDOUT:     .Run = %Run.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Class.decl: type = class_decl @Class [template = constants.%Class] {
+// CHECK:STDOUT:     %T.loc7_13.1: type = param T
+// CHECK:STDOUT:     %T.loc7_13.2: type = bind_symbolic_name T 0, %T.loc7_13.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Run.decl: Run = fn_decl @Run [template = constants.%struct.2] {
+// CHECK:STDOUT:     @Run.%return: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %Get.decl: Get = fn_decl @Get [template = constants.%struct.1] {
+// CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%Class [template = constants.%Class]
+// CHECK:STDOUT:     %.loc8_25: type = ptr_type Class [template = constants.%.1]
+// CHECK:STDOUT:     %self.loc8_15.1: Class* = param self
+// CHECK:STDOUT:     %self.loc8_15.3: Class* = bind_name self, %self.loc8_15.1
+// CHECK:STDOUT:     %.loc8_10: Class* = addr_pattern %self.loc8_15.3
+// CHECK:STDOUT:     %T.ref.loc8: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %.loc8_34: type = ptr_type T [symbolic = constants.%.2]
+// CHECK:STDOUT:     %return.var: ref T* = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %T.ref.loc12: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:   %.loc12: <unbound element of class Class> = field_decl k, element0 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Class
+// CHECK:STDOUT:   .Get = %Get.decl
+// CHECK:STDOUT:   .k = %.loc12
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Get[addr @Class.%self.loc8_15.3: Class*]() -> T* {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %self.ref: Class* = name_ref self, @Class.%self.loc8_15.3
+// CHECK:STDOUT:   %.loc9_17.1: ref Class = deref %self.ref
+// CHECK:STDOUT:   %k.ref: <unbound element of class Class> = name_ref k, @Class.%.loc12 [template = @Class.%.loc12]
+// CHECK:STDOUT:   %.loc9_17.2: ref T = class_element_access %.loc9_17.1, element0
+// CHECK:STDOUT:   %.loc9_12: T* = addr_of %.loc9_17.2
+// CHECK:STDOUT:   return %.loc9_12
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Class.ref: type = name_ref Class, file.%Class.decl [template = constants.%Class]
+// CHECK:STDOUT:   %v.var: ref <error> = var v
+// CHECK:STDOUT:   %v: ref <error> = bind_name v, %v.var
+// CHECK:STDOUT:   %.loc20_29: i32 = int_literal 0 [template = constants.%.7]
+// CHECK:STDOUT:   %.loc20_30: {.k: i32} = struct_literal (%.loc20_29)
+// CHECK:STDOUT:   assign %v.var, <error>
+// CHECK:STDOUT:   %v.ref: ref <error> = name_ref v, %v
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 121 - 0
toolchain/check/testdata/class/generic/import.carbon

@@ -0,0 +1,121 @@
+// 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
+
+// --- foo.carbon
+
+library "foo" api;
+
+class Class(T:! type);
+
+// --- foo.impl.carbon
+
+library "foo" impl;
+
+class Class(T:! type) {
+  var x: T;
+}
+
+// --- fail_bad_foo.impl.carbon
+
+library "foo" impl;
+
+// CHECK:STDERR: fail_bad_foo.impl.carbon:[[@LINE+5]]:13: ERROR: Redeclaration differs at parameter 1.
+// CHECK:STDERR: class Class(U:! type) {
+// CHECK:STDERR:             ^
+// CHECK:STDERR: fail_bad_foo.impl.carbon: Previous declaration's corresponding parameter here.
+// CHECK:STDERR:
+class Class(U:! type) {
+  // CHECK:STDERR: fail_bad_foo.impl.carbon:[[@LINE+3]]:10: ERROR: Name `T` not found.
+  // CHECK:STDERR:   var x: T;
+  // CHECK:STDERR:          ^
+  var x: T;
+}
+
+// CHECK:STDOUT: --- foo.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Class: type = class_type @Class [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Class = %Class.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Class.decl: type = class_decl @Class [template = constants.%Class] {
+// CHECK:STDOUT:     %T.loc4_13.1: type = param T
+// CHECK:STDOUT:     %T.loc4_13.2: type = bind_symbolic_name T 0, %T.loc4_13.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- foo.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+9> [symbolic]
+// CHECK:STDOUT:   %Class: type = class_type @Class [template]
+// CHECK:STDOUT:   %.1: type = unbound_element_type Class, T [symbolic]
+// CHECK:STDOUT:   %.2: type = struct_type {.x: T} [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Class = %Class.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: type = import_ref ir0, inst+5, loaded [template = constants.%Class]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Class.decl: type = class_decl @Class [template = constants.%Class] {
+// CHECK:STDOUT:     %T.loc4_13.1: type = param T
+// CHECK:STDOUT:     %T.loc4_13.2: type = bind_symbolic_name T 0, %T.loc4_13.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %T.ref: type = name_ref T, file.%T.loc4_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:   %.loc5: <unbound element of class Class> = field_decl x, element0 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Class
+// CHECK:STDOUT:   .x = %.loc5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_bad_foo.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %U: type = bind_symbolic_name U 0 [symbolic]
+// CHECK:STDOUT:   %Class: type = class_type @Class [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+10> [symbolic]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Class = %import_ref
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: type = import_ref ir0, inst+5, loaded [template = constants.%Class]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {
+// CHECK:STDOUT:     %U.loc9_13.1: type = param U
+// CHECK:STDOUT:     %U.loc9_13.2: type = bind_symbolic_name U 0, %U.loc9_13.1 [symbolic = constants.%U]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT:   %T.ref: <error> = name_ref T, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc13: <error> = field_decl x, element0 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%.1
+// CHECK:STDOUT:   .x = %.loc13
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 55 - 0
toolchain/check/testdata/class/generic/member_inline.carbon

@@ -0,0 +1,55 @@
+// 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
+
+class Class(T:! type) {
+  fn F(n: T) -> T {
+    return n;
+  }
+}
+
+// CHECK:STDOUT: --- member_inline.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Class: type = class_type @Class [template]
+// CHECK:STDOUT:   %F: type = fn_type @F [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct: F = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Class = %Class.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Class.decl: type = class_decl @Class [template = constants.%Class] {
+// CHECK:STDOUT:     %T.loc7_13.1: type = param T
+// CHECK:STDOUT:     %T.loc7_13.2: type = bind_symbolic_name T 0, %T.loc7_13.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %F.decl: F = fn_decl @F [template = constants.%struct] {
+// CHECK:STDOUT:     %T.ref.loc8_11: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %n.loc8_8.1: T = param n
+// CHECK:STDOUT:     %n.loc8_8.2: T = bind_name n, %n.loc8_8.1
+// CHECK:STDOUT:     %T.ref.loc8_17: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %return.var: ref T = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Class
+// CHECK:STDOUT:   .F = %F.decl
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(@Class.%n.loc8_8.2: T) -> T {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %n.ref: T = name_ref n, @Class.%n.loc8_8.2
+// CHECK:STDOUT:   return %n.ref
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 280 - 0
toolchain/check/testdata/class/generic/redeclare.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
+
+// --- valid.carbon
+
+library "valid" api;
+
+class Generic(T:! type);
+
+class Generic(T:! type) {
+}
+
+// --- fail_mismatch_param_list.carbon
+
+library "mismatch_param_list" api;
+
+class A;
+// CHECK:STDERR: fail_mismatch_param_list.carbon:[[@LINE+7]]:1: ERROR: Redeclaration differs because of parameter list.
+// CHECK:STDERR: class A(T:! type) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_mismatch_param_list.carbon:[[@LINE-4]]:1: Previously declared without parameter list.
+// CHECK:STDERR: class A;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR:
+class A(T:! type) {}
+
+// --- fail_mismatch_implicit_param_list.carbon
+
+library "implicit_mismatch_param_list" api;
+
+class B(N:! i32);
+// CHECK:STDERR: fail_mismatch_implicit_param_list.carbon:[[@LINE+7]]:1: ERROR: Redeclaration differs because of implicit parameter list.
+// CHECK:STDERR: class B[T:! type](N:! T) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_mismatch_implicit_param_list.carbon:[[@LINE-4]]:1: Previously declared without implicit parameter list.
+// CHECK:STDERR: class B(N:! i32);
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+class B[T:! type](N:! T) {}
+
+// --- fail_mismatch_param_count.carbon
+
+library "mismatch_param_count" api;
+
+class C(T:! type);
+// CHECK:STDERR: fail_mismatch_param_count.carbon:[[@LINE+7]]:1: ERROR: Redeclaration differs because of parameter count of 2.
+// CHECK:STDERR: class C(T:! type, U:! i32) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_mismatch_param_count.carbon:[[@LINE-4]]:1: Previously declared with parameter count of 1.
+// CHECK:STDERR: class C(T:! type);
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+class C(T:! type, U:! i32) {}
+
+// --- fail_mismatch_param_type.carbon
+
+library "mismatch_param_type" api;
+
+class D(T:! type);
+// CHECK:STDERR: fail_mismatch_param_type.carbon:[[@LINE+7]]:9: ERROR: Redeclaration differs at parameter 1.
+// CHECK:STDERR: class D(T:! i32) {}
+// CHECK:STDERR:         ^
+// CHECK:STDERR: fail_mismatch_param_type.carbon:[[@LINE-4]]:9: Previous declaration's corresponding parameter here.
+// CHECK:STDERR: class D(T:! type);
+// CHECK:STDERR:         ^
+// CHECK:STDERR:
+class D(T:! i32) {}
+
+// --- fail_mismatch_param_name.carbon
+
+library "mismatch_param_name" api;
+
+class E(T:! type);
+// CHECK:STDERR: fail_mismatch_param_name.carbon:[[@LINE+6]]:9: ERROR: Redeclaration differs at parameter 1.
+// CHECK:STDERR: class E(U:! type) {}
+// CHECK:STDERR:         ^
+// CHECK:STDERR: fail_mismatch_param_name.carbon:[[@LINE-4]]:9: Previous declaration's corresponding parameter here.
+// CHECK:STDERR: class E(T:! type);
+// CHECK:STDERR:         ^
+class E(U:! type) {}
+
+// CHECK:STDOUT: --- valid.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Generic: type = class_type @Generic [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Generic = %Generic.decl.loc4
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Generic.decl.loc4: type = class_decl @Generic [template = constants.%Generic] {
+// CHECK:STDOUT:     %T.loc4_15.1: type = param T
+// CHECK:STDOUT:     %T.loc4_15.2: type = bind_symbolic_name T 0, %T.loc4_15.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Generic.decl.loc6: type = class_decl @Generic [template = constants.%Generic] {
+// CHECK:STDOUT:     %T.loc6_15.1: type = param T
+// CHECK:STDOUT:     %T.loc6_15.2: type = bind_symbolic_name T 0, %T.loc6_15.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Generic {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Generic
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_mismatch_param_list.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %A: type = class_type @A [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %A.decl: type = class_decl @A [template = constants.%A] {}
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {
+// CHECK:STDOUT:     %T.loc12_9.1: type = param T
+// CHECK:STDOUT:     %T.loc12_9.2: type = bind_symbolic_name T 0, %T.loc12_9.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @A;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_mismatch_implicit_param_list.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %N.1: i32 = bind_symbolic_name N 0 [symbolic]
+// CHECK:STDOUT:   %B: type = class_type @B [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %N.2: T = bind_symbolic_name N 1 [symbolic]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .B = %B.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %B.decl: type = class_decl @B [template = constants.%B] {
+// CHECK:STDOUT:     %N.loc4_9.1: i32 = param N
+// CHECK:STDOUT:     %N.loc4_9.2: i32 = bind_symbolic_name N 0, %N.loc4_9.1 [symbolic = constants.%N.1]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {
+// CHECK:STDOUT:     %T.loc12_9.1: type = param T
+// CHECK:STDOUT:     %T.loc12_9.2: type = bind_symbolic_name T 0, %T.loc12_9.1 [symbolic = constants.%T]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc12_9.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %N.loc12_19.1: T = param N
+// CHECK:STDOUT:     %N.loc12_19.2: T = bind_symbolic_name N 1, %N.loc12_19.1 [symbolic = constants.%N.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @B;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_mismatch_param_count.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %U: i32 = bind_symbolic_name U 1 [symbolic]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {
+// CHECK:STDOUT:     %T.loc4_9.1: type = param T
+// CHECK:STDOUT:     %T.loc4_9.2: type = bind_symbolic_name T 0, %T.loc4_9.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {
+// CHECK:STDOUT:     %T.loc12_9.1: type = param T
+// CHECK:STDOUT:     %T.loc12_9.2: type = bind_symbolic_name T 0, %T.loc12_9.1 [symbolic = constants.%T]
+// CHECK:STDOUT:     %U.loc12_19.1: i32 = param U
+// CHECK:STDOUT:     %U.loc12_19.2: i32 = bind_symbolic_name U 1, %U.loc12_19.1 [symbolic = constants.%U]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_mismatch_param_type.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T.1: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %T.2: i32 = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .D = %D.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {
+// CHECK:STDOUT:     %T.loc4_9.1: type = param T
+// CHECK:STDOUT:     %T.loc4_9.2: type = bind_symbolic_name T 0, %T.loc4_9.1 [symbolic = constants.%T.1]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {
+// CHECK:STDOUT:     %T.loc12_9.1: i32 = param T
+// CHECK:STDOUT:     %T.loc12_9.2: i32 = bind_symbolic_name T 0, %T.loc12_9.1 [symbolic = constants.%T.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_mismatch_param_name.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %E: type = class_type @E [template]
+// CHECK:STDOUT:   %U: type = bind_symbolic_name U 0 [symbolic]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .E = %E.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %E.decl: type = class_decl @E [template = constants.%E] {
+// CHECK:STDOUT:     %T.loc4_9.1: type = param T
+// CHECK:STDOUT:     %T.loc4_9.2: type = bind_symbolic_name T 0, %T.loc4_9.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {
+// CHECK:STDOUT:     %U.loc11_9.1: type = param U
+// CHECK:STDOUT:     %U.loc11_9.2: type = bind_symbolic_name U 0, %U.loc11_9.1 [symbolic = constants.%U]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @E;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 0 - 5
toolchain/check/testdata/class/import.carbon

@@ -202,7 +202,6 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Run() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %Empty.decl: type = class_decl @Empty [template = constants.%Empty] {}
 // CHECK:STDOUT:   %Empty.ref: type = name_ref Empty, file.%import_ref.1 [template = constants.%Empty]
 // CHECK:STDOUT:   %a.var: ref Empty = var a
 // CHECK:STDOUT:   %a: ref Empty = bind_name a, %a.var
@@ -210,7 +209,6 @@ fn Run() {
 // CHECK:STDOUT:   %.loc7_19.2: init Empty = class_init (), %a.var [template = constants.%struct.2]
 // CHECK:STDOUT:   %.loc7_20: init Empty = converted %.loc7_19.1, %.loc7_19.2 [template = constants.%struct.2]
 // CHECK:STDOUT:   assign %a.var, %.loc7_20
-// CHECK:STDOUT:   %Field.decl: type = class_decl @Field [template = constants.%Field] {}
 // CHECK:STDOUT:   %Field.ref: type = name_ref Field, file.%import_ref.2 [template = constants.%Field]
 // CHECK:STDOUT:   %b.var: ref Field = var b
 // CHECK:STDOUT:   %b: ref Field = bind_name b, %b.var
@@ -226,7 +224,6 @@ fn Run() {
 // CHECK:STDOUT:   %.loc10_4: ref i32 = class_element_access %b.ref, element0
 // CHECK:STDOUT:   %.loc10_9: i32 = int_literal 2 [template = constants.%.8]
 // CHECK:STDOUT:   assign %.loc10_4, %.loc10_9
-// CHECK:STDOUT:   %ForwardDeclared.decl.1: type = class_decl @ForwardDeclared.1 [template = constants.%ForwardDeclared.1] {}
 // CHECK:STDOUT:   %ForwardDeclared.ref.loc12: type = name_ref ForwardDeclared, file.%import_ref.3 [template = constants.%ForwardDeclared.1]
 // CHECK:STDOUT:   %c.var: ref ForwardDeclared = var c
 // CHECK:STDOUT:   %c: ref ForwardDeclared = bind_name c, %c.var
@@ -235,7 +232,6 @@ fn Run() {
 // CHECK:STDOUT:   %.loc12_30: init ForwardDeclared = converted %.loc12_29.1, %.loc12_29.2 [template = constants.%struct.4]
 // CHECK:STDOUT:   assign %c.var, %.loc12_30
 // CHECK:STDOUT:   %c.ref.loc13: ref ForwardDeclared = name_ref c, %c
-// CHECK:STDOUT:   %ForwardDeclared.decl.2: type = class_decl @ForwardDeclared.2 [template = constants.%ForwardDeclared.2] {}
 // CHECK:STDOUT:   %F.ref: F = name_ref F, file.%import_ref.10 [template = constants.%struct.5]
 // CHECK:STDOUT:   %.loc13_4: <bound method> = bound_method %c.ref.loc13, %F.ref
 // CHECK:STDOUT:   %.loc13_3: ForwardDeclared = bind_value %c.ref.loc13
@@ -252,7 +248,6 @@ fn Run() {
 // CHECK:STDOUT:   %c.ref.loc16: ref ForwardDeclared = name_ref c, %c
 // CHECK:STDOUT:   %.loc16_29: ForwardDeclared* = addr_of %c.ref.loc16
 // CHECK:STDOUT:   assign %d.var, %.loc16_29
-// CHECK:STDOUT:   %Incomplete.decl: type = class_decl @Incomplete [template = constants.%Incomplete] {}
 // CHECK:STDOUT:   %Incomplete.ref: type = name_ref Incomplete, file.%import_ref.4 [template = constants.%Incomplete]
 // CHECK:STDOUT:   %.loc18: type = ptr_type Incomplete [template = constants.%.10]
 // CHECK:STDOUT:   %e.var: ref Incomplete* = var e

+ 0 - 2
toolchain/check/testdata/class/import_base.carbon

@@ -159,8 +159,6 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Run() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %Child.decl: type = class_decl @Child [template = constants.%Child] {}
-// CHECK:STDOUT:   %Base.decl: type = class_decl @Base [template = constants.%Base] {}
 // CHECK:STDOUT:   %Child.ref: type = name_ref Child, file.%import_ref.2 [template = constants.%Child]
 // CHECK:STDOUT:   %a.var: ref Child = var a
 // CHECK:STDOUT:   %a: ref Child = bind_name a, %a.var

+ 2 - 4
toolchain/check/testdata/class/import_forward_decl.carbon

@@ -44,13 +44,11 @@ class ForwardDecl {
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .Core = %Core
-// CHECK:STDOUT:     .ForwardDecl = %ForwardDecl.decl.loc4
+// CHECK:STDOUT:     .ForwardDecl = %ForwardDecl.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref: type = import_ref ir0, inst+2, loaded [template = constants.%ForwardDecl]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %ForwardDecl.decl.loc4: type = class_decl @ForwardDecl [template = constants.%ForwardDecl] {
-// CHECK:STDOUT:     %ForwardDecl.decl.1: type = class_decl @ForwardDecl [template = constants.%ForwardDecl] {}
-// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %ForwardDecl.decl: type = class_decl @ForwardDecl [template = constants.%ForwardDecl] {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @ForwardDecl {

+ 0 - 6
toolchain/check/testdata/class/import_indirect.carbon

@@ -138,7 +138,6 @@ var ptr: E* = &val;
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+2, loaded [template = constants.%C]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unloaded
 // CHECK:STDOUT:   %C.ref.loc6: type = name_ref C, %import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:   %D: type = bind_alias D, %import_ref.1 [template = constants.%C]
@@ -189,7 +188,6 @@ var ptr: E* = &val;
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: type = import_ref ir1, inst+2, loaded [template = constants.%C]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+3, unloaded
 // CHECK:STDOUT:   %C.ref.loc6: type = name_ref C, %import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:   %E: type = bind_alias E, %import_ref.1 [template = constants.%C]
@@ -245,7 +243,6 @@ var ptr: E* = &val;
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir2, inst+13, unloaded
 // CHECK:STDOUT:   %import_ref.4 = import_ref ir2, inst+23, unloaded
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+3, unloaded
 // CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:   %val.var: ref C = var val
@@ -299,7 +296,6 @@ var ptr: E* = &val;
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+23, unloaded
 // CHECK:STDOUT:   %import_ref.4: type = import_ref ir2, inst+2, loaded [template = constants.%C]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.5 = import_ref ir2, inst+3, unloaded
 // CHECK:STDOUT:   %C.ref: type = name_ref C, %import_ref.4 [template = constants.%C]
 // CHECK:STDOUT:   %val.var: ref C = var val
@@ -357,7 +353,6 @@ var ptr: E* = &val;
 // CHECK:STDOUT:   %import_ref.5 = import_ref ir2, inst+13, unloaded
 // CHECK:STDOUT:   %import_ref.6 = import_ref ir2, inst+23, unloaded
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.7 = import_ref ir1, inst+6, unloaded
 // CHECK:STDOUT:   %D.ref: type = name_ref D, %import_ref.1 [template = constants.%C]
 // CHECK:STDOUT:   %val.var: ref C = var val
@@ -415,7 +410,6 @@ var ptr: E* = &val;
 // CHECK:STDOUT:   %import_ref.5 = import_ref ir2, inst+13, unloaded
 // CHECK:STDOUT:   %import_ref.6 = import_ref ir2, inst+23, unloaded
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.7 = import_ref ir2, inst+6, unloaded
 // CHECK:STDOUT:   %D.ref: type = name_ref D, %import_ref.4 [template = constants.%C]
 // CHECK:STDOUT:   %val.var: ref C = var val

+ 0 - 1
toolchain/check/testdata/class/import_member_cycle.carbon

@@ -82,7 +82,6 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Run() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %Cycle.decl: type = class_decl @Cycle [template = constants.%Cycle] {}
 // CHECK:STDOUT:   %Cycle.ref: type = name_ref Cycle, file.%import_ref.1 [template = constants.%Cycle]
 // CHECK:STDOUT:   %.loc7: type = ptr_type Cycle [template = constants.%.2]
 // CHECK:STDOUT:   %a.var: ref Cycle* = var a

+ 0 - 1
toolchain/check/testdata/class/import_struct_cyle.carbon

@@ -101,7 +101,6 @@ fn Run() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Run() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %Cycle.decl: type = class_decl @Cycle [template = constants.%Cycle] {}
 // CHECK:STDOUT:   %a.ref.loc7_3: ref {.b: Cycle*} = name_ref a, file.%import_ref.2
 // CHECK:STDOUT:   %.loc7_4: ref Cycle* = struct_access %a.ref.loc7_3, element0
 // CHECK:STDOUT:   %a.ref.loc7_11: ref {.b: Cycle*} = name_ref a, file.%import_ref.2

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

@@ -110,7 +110,6 @@ var arr: [i32; 1 + 2] = (3, 4, 3 + 4);
 // CHECK:STDOUT:   %import_ref.4: Op = import_ref ir2, inst+19, loaded [template = constants.%struct.2]
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Add.decl: type = interface_decl @Add [template = constants.%.1] {}
 // CHECK:STDOUT:     %Add.ref: type = name_ref Add, %import_ref.1 [template = constants.%.1]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.loc10_16: i32 = int_literal 1 [template = constants.%.4]

+ 0 - 2
toolchain/check/testdata/function/declaration/no_prelude/fail_import_incomplete_return.carbon

@@ -197,12 +197,10 @@ fn CallFAndGIncomplete() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @CallFAndGIncomplete() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %ReturnCUnused.ref: ReturnCUnused = name_ref ReturnCUnused, file.%import_ref.3 [template = constants.%struct.2]
 // CHECK:STDOUT:   %ReturnCUnused.call: init <error> = call %ReturnCUnused.ref()
 // CHECK:STDOUT:   %ReturnCUsed.ref: ReturnCUsed = name_ref ReturnCUsed, file.%import_ref.4 [template = constants.%struct.3]
 // CHECK:STDOUT:   %ReturnCUsed.call: init <error> = call %ReturnCUsed.ref()
-// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {}
 // CHECK:STDOUT:   %ReturnDUnused.ref: ReturnDUnused = name_ref ReturnDUnused, file.%import_ref.5 [template = constants.%struct.4]
 // CHECK:STDOUT:   %.loc24_16.1: ref D = temporary_storage
 // CHECK:STDOUT:   %ReturnDUnused.call: init D = call %ReturnDUnused.ref() to %.loc24_16.1

+ 1 - 1
toolchain/check/testdata/function/declaration/no_prelude/fail_redecl.carbon

@@ -25,7 +25,7 @@ fn B(x: ());
 fn B(x: ());
 
 fn C();
-// CHECK:STDERR: fail_redecl.carbon:[[@LINE+7]]:1: ERROR: Function redeclaration differs because of parameter count of 1.
+// CHECK:STDERR: fail_redecl.carbon:[[@LINE+7]]:1: ERROR: Redeclaration differs because of parameter count of 1.
 // CHECK:STDERR: fn C(x: ());
 // CHECK:STDERR: ^~~~~~~~~~~~
 // CHECK:STDERR: fail_redecl.carbon:[[@LINE-4]]:1: Previously declared with parameter count of 0.

+ 3 - 3
toolchain/check/testdata/function/definition/no_prelude/fail_decl_param_mismatch.carbon

@@ -5,7 +5,7 @@
 // AUTOUPDATE
 
 fn F();
-// CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: ERROR: Function redeclaration differs because of parameter count of 1.
+// CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: ERROR: Redeclaration differs because of parameter count of 1.
 // CHECK:STDERR: fn F(x: ()) {}
 // CHECK:STDERR: ^~~~~~~~~~~~~
 // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE-4]]:1: Previously declared with parameter count of 0.
@@ -15,7 +15,7 @@ fn F();
 fn F(x: ()) {}
 
 fn G(x: ());
-// CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: ERROR: Function redeclaration differs because of parameter count of 0.
+// CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: ERROR: Redeclaration differs because of parameter count of 0.
 // CHECK:STDERR: fn G() {}
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE-4]]:1: Previously declared with parameter count of 1.
@@ -25,7 +25,7 @@ fn G(x: ());
 fn G() {}
 
 fn H(x: ());
-// CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:6: ERROR: Function redeclaration differs at parameter 1.
+// CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:6: ERROR: Redeclaration differs at parameter 1.
 // CHECK:STDERR: fn H(x: bool) {}
 // CHECK:STDERR:      ^
 // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE-4]]:6: Previous declaration's corresponding parameter here.

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

@@ -48,7 +48,7 @@ library "fail_reorder" api;
 
 fn F(T:! type, U:! type) -> T*;
 
-// CHECK:STDERR: fail_reorder.carbon:[[@LINE+7]]:6: ERROR: Function redeclaration differs at parameter 1.
+// CHECK:STDERR: fail_reorder.carbon:[[@LINE+7]]:6: ERROR: Redeclaration differs at parameter 1.
 // CHECK:STDERR: fn F(U:! type, T:! type) -> T* {
 // CHECK:STDERR:      ^
 // CHECK:STDERR: fail_reorder.carbon:[[@LINE-5]]:6: Previous declaration's corresponding parameter here.
@@ -76,7 +76,7 @@ library "fail_rename" api;
 
 fn F(T:! type, U:! type) -> T*;
 
-// CHECK:STDERR: fail_rename.carbon:[[@LINE+7]]:6: ERROR: Function redeclaration differs at parameter 1.
+// CHECK:STDERR: fail_rename.carbon:[[@LINE+7]]:6: ERROR: Redeclaration differs at parameter 1.
 // CHECK:STDERR: fn F(U:! type, T:! type) -> U* {
 // CHECK:STDERR:      ^
 // CHECK:STDERR: fail_rename.carbon:[[@LINE-5]]:6: Previous declaration's corresponding parameter here.

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

@@ -48,7 +48,7 @@ class FAlias {
 
 class FExtraParam {
   impl as I {
-    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: ERROR: Function redeclaration differs because of parameter count of 1.
+    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: ERROR: Redeclaration differs because of parameter count of 1.
     // CHECK:STDERR:     fn F(b: bool);
     // CHECK:STDERR:     ^~~~~~~~~~~~~~
     // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-47]]:15: Previously declared with parameter count of 0.
@@ -61,7 +61,7 @@ class FExtraParam {
 
 class FExtraImplicitParam {
   impl as I {
-    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: ERROR: Function redeclaration differs because of implicit parameter count of 1.
+    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: ERROR: Redeclaration differs because of implicit parameter count of 1.
     // CHECK:STDERR:     fn F[self: Self]();
     // CHECK:STDERR:     ^~~~~~~~~~~~~~~~~~~
     // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-60]]:15: Previously declared with implicit parameter count of 0.
@@ -90,7 +90,7 @@ interface J { fn F[self: bool](b: bool) -> bool; }
 
 class FMissingParam {
   impl as J {
-    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: ERROR: Function redeclaration differs because of parameter count of 0.
+    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: ERROR: Redeclaration differs because of parameter count of 0.
     // CHECK:STDERR:     fn F[self: bool]() -> bool;
     // CHECK:STDERR:     ^~~~~~~~~~~~~~~~~~~~~~~~~~~
     // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-7]]:15: Previously declared with parameter count of 1.
@@ -103,7 +103,7 @@ class FMissingParam {
 
 class FMissingImplicitParam {
   impl as J {
-    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: ERROR: Function redeclaration differs because of implicit parameter count of 0.
+    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: ERROR: Redeclaration differs because of implicit parameter count of 0.
     // CHECK:STDERR:     fn F(b: bool) -> bool;
     // CHECK:STDERR:     ^~~~~~~~~~~~~~~~~~~~~~
     // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-20]]:15: Previously declared with implicit parameter count of 1.
@@ -129,7 +129,7 @@ class FMissingReturnType {
 
 class FDifferentParamType {
   impl as J {
-    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:22: ERROR: Function redeclaration differs at parameter 1.
+    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:22: ERROR: Redeclaration differs at parameter 1.
     // CHECK:STDERR:     fn F[self: bool](b: Self) -> bool;
     // CHECK:STDERR:                      ^
     // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-46]]:32: Previous declaration's corresponding parameter here.
@@ -142,7 +142,7 @@ class FDifferentParamType {
 
 class FDifferentImplicitParamType {
   impl as J {
-    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: ERROR: Function redeclaration differs at implicit parameter 1.
+    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: ERROR: Redeclaration differs at implicit parameter 1.
     // CHECK:STDERR:     fn F[self: Self](b: bool) -> bool;
     // CHECK:STDERR:          ^~~~
     // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-59]]:20: Previous declaration's corresponding implicit parameter here.
@@ -169,7 +169,7 @@ class FDifferentReturnType {
 // TODO: This should probably be permitted.
 class FDifferentParamName {
   impl as J {
-    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:22: ERROR: Function redeclaration differs at parameter 1.
+    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:22: ERROR: Redeclaration differs at parameter 1.
     // CHECK:STDERR:     fn F[self: bool](not_b: bool) -> bool;
     // CHECK:STDERR:                      ^~~~~
     // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-86]]:32: Previous declaration's corresponding parameter here.
@@ -186,7 +186,7 @@ interface SelfNested {
 
 class SelfNestedBadParam {
   impl as SelfNested {
-    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: ERROR: Function redeclaration differs at parameter 1.
+    // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: ERROR: Redeclaration differs at parameter 1.
     // CHECK:STDERR:     fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> [SelfNestedBadParam; 4];
     // CHECK:STDERR:          ^
     // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-8]]:8: Previous declaration's corresponding parameter here.

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

@@ -118,9 +118,7 @@ fn G(c: Impl.C) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Impl: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+14, unloaded
-// CHECK:STDOUT:   %HasF.decl: type = interface_decl @HasF [template = constants.%.2] {}
 // CHECK:STDOUT:   %import_ref.2: <associated F in HasF> = import_ref ir1, inst+11, loaded [template = constants.%.6]
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+4, unloaded
 // CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+6, unloaded

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

@@ -114,9 +114,7 @@ fn G(c: Impl.C) {
 // CHECK:STDOUT:     .G = %G.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Impl: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
 // CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+13, unloaded
-// CHECK:STDOUT:   %HasF.decl: type = interface_decl @HasF [template = constants.%.2] {}
 // CHECK:STDOUT:   %import_ref.2: <associated F in HasF> = import_ref ir1, inst+10, loaded [template = constants.%.6]
 // CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+3, unloaded
 // CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+5, unloaded

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

@@ -103,7 +103,6 @@ fn F(x: (), y: ()) -> () {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %.loc6_7.1: () = tuple_literal ()
 // CHECK:STDOUT:     %.loc6_7.2: type = converted %.loc6_7.1, constants.%.1 [template = constants.%.1]
-// CHECK:STDOUT:     %Add.decl: type = interface_decl @Add [template = constants.%.2] {}
 // CHECK:STDOUT:     %Add.ref: type = name_ref Add, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %F.decl: F = fn_decl @F [template = constants.%struct.3] {

+ 0 - 3
toolchain/check/testdata/interface/no_prelude/import.carbon

@@ -180,7 +180,6 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:   %import_ref.4: ref {.f: ForwardDeclared} = import_ref ir1, inst+42, loaded
 // CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+3, unloaded
 // CHECK:STDOUT:   %UseEmpty.decl: UseEmpty = fn_decl @UseEmpty [template = constants.%struct.1] {
-// CHECK:STDOUT:     %Empty.decl: type = interface_decl @Empty [template = constants.%.1] {}
 // CHECK:STDOUT:     %Empty.ref: type = name_ref Empty, %import_ref.1 [template = constants.%.1]
 // CHECK:STDOUT:     %e.loc6_13.1: Empty = param e
 // CHECK:STDOUT:     @UseEmpty.%e: Empty = bind_name e, %e.loc6_13.1
@@ -191,7 +190,6 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:   %import_ref.9 = import_ref ir1, inst+9, unloaded
 // CHECK:STDOUT:   %import_ref.10 = import_ref ir1, inst+13, unloaded
 // CHECK:STDOUT:   %UseBasic.decl: UseBasic = fn_decl @UseBasic [template = constants.%struct.2] {
-// CHECK:STDOUT:     %Basic.decl: type = interface_decl @Basic [template = constants.%.3] {}
 // CHECK:STDOUT:     %Basic.ref.loc7: type = name_ref Basic, %import_ref.2 [template = constants.%.3]
 // CHECK:STDOUT:     %e.loc7_13.1: Basic = param e
 // CHECK:STDOUT:     @UseBasic.%e: Basic = bind_name e, %e.loc7_13.1
@@ -202,7 +200,6 @@ var f: ForwardDeclared* = &f_ref.f;
 // CHECK:STDOUT:   %import_ref.14 = import_ref ir1, inst+24, unloaded
 // CHECK:STDOUT:   %import_ref.15 = import_ref ir1, inst+28, unloaded
 // CHECK:STDOUT:   %UseForwardDeclared.decl: UseForwardDeclared = fn_decl @UseForwardDeclared [template = constants.%struct.3] {
-// CHECK:STDOUT:     %ForwardDeclared.decl: type = interface_decl @ForwardDeclared [template = constants.%.4] {}
 // CHECK:STDOUT:     %ForwardDeclared.ref.loc8: type = name_ref ForwardDeclared, %import_ref.3 [template = constants.%.4]
 // CHECK:STDOUT:     %f.loc8_23.1: ForwardDeclared = param f
 // CHECK:STDOUT:     @UseForwardDeclared.%f: ForwardDeclared = bind_name f, %f.loc8_23.1

+ 0 - 2
toolchain/check/testdata/operators/overloaded/add.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Add.decl: type = interface_decl @Add [template = constants.%.2] {}
 // CHECK:STDOUT:     %Add.ref: type = name_ref Add, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %AddAssign.decl: type = interface_decl @AddAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %AddAssign.ref: type = name_ref AddAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 2
toolchain/check/testdata/operators/overloaded/bit_and.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %BitAnd.decl: type = interface_decl @BitAnd [template = constants.%.2] {}
 // CHECK:STDOUT:     %BitAnd.ref: type = name_ref BitAnd, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %BitAndAssign.decl: type = interface_decl @BitAndAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %BitAndAssign.ref: type = name_ref BitAndAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 1
toolchain/check/testdata/operators/overloaded/bit_complement.carbon

@@ -110,7 +110,6 @@ fn TestOp(a: C) -> C {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %BitComplement.decl: type = interface_decl @BitComplement [template = constants.%.2] {}
 // CHECK:STDOUT:     %BitComplement.ref: type = name_ref BitComplement, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.4] {

+ 0 - 2
toolchain/check/testdata/operators/overloaded/bit_or.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %BitOr.decl: type = interface_decl @BitOr [template = constants.%.2] {}
 // CHECK:STDOUT:     %BitOr.ref: type = name_ref BitOr, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %BitOrAssign.decl: type = interface_decl @BitOrAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %BitOrAssign.ref: type = name_ref BitOrAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 2
toolchain/check/testdata/operators/overloaded/bit_xor.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %BitXor.decl: type = interface_decl @BitXor [template = constants.%.2] {}
 // CHECK:STDOUT:     %BitXor.ref: type = name_ref BitXor, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %BitXorAssign.decl: type = interface_decl @BitXorAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %BitXorAssign.ref: type = name_ref BitXorAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 1
toolchain/check/testdata/operators/overloaded/dec.carbon

@@ -110,7 +110,6 @@ fn TestOp() {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %C.ref: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Dec.decl: type = interface_decl @Dec [template = constants.%.2] {}
 // CHECK:STDOUT:     %Dec.ref: type = name_ref Dec, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.3] {}

+ 0 - 2
toolchain/check/testdata/operators/overloaded/div.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Div.decl: type = interface_decl @Div [template = constants.%.2] {}
 // CHECK:STDOUT:     %Div.ref: type = name_ref Div, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %DivAssign.decl: type = interface_decl @DivAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %DivAssign.ref: type = name_ref DivAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 3
toolchain/check/testdata/operators/overloaded/eq.carbon

@@ -203,7 +203,6 @@ fn TestLhsBad(a: D, b: C) -> bool {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Eq.decl: type = interface_decl @Eq [template = constants.%.2] {}
 // CHECK:STDOUT:     %Eq.ref: type = name_ref Eq, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestEqual.decl: TestEqual = fn_decl @TestEqual [template = constants.%struct.5] {
@@ -380,7 +379,6 @@ fn TestLhsBad(a: D, b: C) -> bool {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %a.ref: D = name_ref a, %a
 // CHECK:STDOUT:   %b.ref: D = name_ref b, %b
-// CHECK:STDOUT:   %Eq.decl: type = interface_decl @Eq [template = constants.%.4] {}
 // CHECK:STDOUT:   return <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -444,7 +442,6 @@ fn TestLhsBad(a: D, b: C) -> bool {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %C.ref.loc9: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Eq.decl: type = interface_decl @Eq [template = constants.%.2] {}
 // CHECK:STDOUT:     %Eq.ref: type = name_ref Eq, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestRhsBad.decl: TestRhsBad = fn_decl @TestRhsBad [template = constants.%struct.5] {

+ 0 - 2
toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon

@@ -177,7 +177,6 @@ fn TestAddAssignNonRef(a: C, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Inc.decl: type = interface_decl @Inc [template = constants.%.2] {}
 // CHECK:STDOUT:     %Inc.ref: type = name_ref Inc, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+20, loaded [template = constants.%.7]
@@ -187,7 +186,6 @@ fn TestAddAssignNonRef(a: C, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc11: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc11: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %AddAssign.decl: type = interface_decl @AddAssign [template = constants.%.7] {}
 // CHECK:STDOUT:     %AddAssign.ref: type = name_ref AddAssign, %import_ref.5 [template = constants.%.7]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestIncNonRef.decl: TestIncNonRef = fn_decl @TestIncNonRef [template = constants.%struct.5] {

+ 0 - 4
toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon

@@ -329,7 +329,6 @@ fn TestRef(b: C) {
 // CHECK:STDOUT: fn @TestUnary(%a: C) -> %return: C {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %a.ref: C = name_ref a, %a
-// CHECK:STDOUT:   %Negate.decl: type = interface_decl @Negate [template = constants.%.4] {}
 // CHECK:STDOUT:   return <error> to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -339,7 +338,6 @@ fn TestRef(b: C) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %a.ref: C = name_ref a, %a
 // CHECK:STDOUT:   %b.ref: C = name_ref b, %b
-// CHECK:STDOUT:   %Add.decl: type = interface_decl @Add [template = constants.%.7] {}
 // CHECK:STDOUT:   return <error> to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -356,9 +354,7 @@ fn TestRef(b: C) {
 // CHECK:STDOUT:   assign %a.var, %.loc25_16
 // CHECK:STDOUT:   %a.ref.loc30: ref C = name_ref a, %a
 // CHECK:STDOUT:   %b.ref: C = name_ref b, %b
-// CHECK:STDOUT:   %AddAssign.decl: type = interface_decl @AddAssign [template = constants.%.10] {}
 // CHECK:STDOUT:   %a.ref.loc34: ref C = name_ref a, %a
-// CHECK:STDOUT:   %Inc.decl: type = interface_decl @Inc [template = constants.%.14] {}
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 2
toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon

@@ -188,7 +188,6 @@ fn TestAssign(b: D) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc9: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc9: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Add.decl: type = interface_decl @Add [template = constants.%.2] {}
 // CHECK:STDOUT:     %Add.ref: type = name_ref Add, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.5]
@@ -198,7 +197,6 @@ fn TestAssign(b: D) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc12: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc12: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %AddAssign.decl: type = interface_decl @AddAssign [template = constants.%.5] {}
 // CHECK:STDOUT:     %AddAssign.ref: type = name_ref AddAssign, %import_ref.5 [template = constants.%.5]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Test.decl: Test = fn_decl @Test [template = constants.%struct.5] {

+ 0 - 1
toolchain/check/testdata/operators/overloaded/inc.carbon

@@ -110,7 +110,6 @@ fn TestOp() {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %C.ref: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Inc.decl: type = interface_decl @Inc [template = constants.%.2] {}
 // CHECK:STDOUT:     %Inc.ref: type = name_ref Inc, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.3] {}

+ 0 - 2
toolchain/check/testdata/operators/overloaded/left_shift.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %LeftShift.decl: type = interface_decl @LeftShift [template = constants.%.2] {}
 // CHECK:STDOUT:     %LeftShift.ref: type = name_ref LeftShift, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %LeftShiftAssign.decl: type = interface_decl @LeftShiftAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %LeftShiftAssign.ref: type = name_ref LeftShiftAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 2
toolchain/check/testdata/operators/overloaded/mod.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Mod.decl: type = interface_decl @Mod [template = constants.%.2] {}
 // CHECK:STDOUT:     %Mod.ref: type = name_ref Mod, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %ModAssign.decl: type = interface_decl @ModAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %ModAssign.ref: type = name_ref ModAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 2
toolchain/check/testdata/operators/overloaded/mul.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Mul.decl: type = interface_decl @Mul [template = constants.%.2] {}
 // CHECK:STDOUT:     %Mul.ref: type = name_ref Mul, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %MulAssign.decl: type = interface_decl @MulAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %MulAssign.ref: type = name_ref MulAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 1
toolchain/check/testdata/operators/overloaded/negate.carbon

@@ -110,7 +110,6 @@ fn TestOp(a: C) -> C {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Negate.decl: type = interface_decl @Negate [template = constants.%.2] {}
 // CHECK:STDOUT:     %Negate.ref: type = name_ref Negate, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.4] {

+ 0 - 2
toolchain/check/testdata/operators/overloaded/ordered.carbon

@@ -263,7 +263,6 @@ fn TestGreaterEqual(a: D, b: D) -> bool {
 // CHECK:STDOUT:   impl_decl @impl {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Ordered.decl: type = interface_decl @Ordered [template = constants.%.2] {}
 // CHECK:STDOUT:     %Ordered.ref: type = name_ref Ordered, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestLess.decl: TestLess = fn_decl @TestLess [template = constants.%struct.9] {
@@ -558,7 +557,6 @@ fn TestGreaterEqual(a: D, b: D) -> bool {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %a.ref: D = name_ref a, %a
 // CHECK:STDOUT:   %b.ref: D = name_ref b, %b
-// CHECK:STDOUT:   %Ordered.decl: type = interface_decl @Ordered [template = constants.%.4] {}
 // CHECK:STDOUT:   return <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 2
toolchain/check/testdata/operators/overloaded/right_shift.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %RightShift.decl: type = interface_decl @RightShift [template = constants.%.2] {}
 // CHECK:STDOUT:     %RightShift.ref: type = name_ref RightShift, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %RightShiftAssign.decl: type = interface_decl @RightShiftAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %RightShiftAssign.ref: type = name_ref RightShiftAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 0 - 2
toolchain/check/testdata/operators/overloaded/sub.carbon

@@ -174,7 +174,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.1 {
 // CHECK:STDOUT:     %C.ref.loc8: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc8: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %Sub.decl: type = interface_decl @Sub [template = constants.%.2] {}
 // CHECK:STDOUT:     %Sub.ref: type = name_ref Sub, %import_ref.1 [template = constants.%.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.5: type = import_ref ir2, inst+26, loaded [template = constants.%.6]
@@ -184,7 +183,6 @@ fn TestAssign(a: C*, b: C) {
 // CHECK:STDOUT:   impl_decl @impl.2 {
 // CHECK:STDOUT:     %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C]
 // CHECK:STDOUT:     %Core.ref.loc13: <namespace> = name_ref Core, %Core [template = %Core]
-// CHECK:STDOUT:     %SubAssign.decl: type = interface_decl @SubAssign [template = constants.%.6] {}
 // CHECK:STDOUT:     %SubAssign.ref: type = name_ref SubAssign, %import_ref.5 [template = constants.%.6]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %TestOp.decl: TestOp = fn_decl @TestOp [template = constants.%struct.6] {

+ 6 - 4
toolchain/diagnostics/diagnostic_kind.def

@@ -156,6 +156,12 @@ CARBON_DIAGNOSTIC_KIND(RedeclNonExtern)
 CARBON_DIAGNOSTIC_KIND(RedeclPrevDef)
 CARBON_DIAGNOSTIC_KIND(RedeclRedef)
 CARBON_DIAGNOSTIC_KIND(RedeclExternMismatch)
+CARBON_DIAGNOSTIC_KIND(RedeclParamListDiffers)
+CARBON_DIAGNOSTIC_KIND(RedeclParamListPrevious)
+CARBON_DIAGNOSTIC_KIND(RedeclParamCountDiffers)
+CARBON_DIAGNOSTIC_KIND(RedeclParamCountPrevious)
+CARBON_DIAGNOSTIC_KIND(RedeclParamDiffers)
+CARBON_DIAGNOSTIC_KIND(RedeclParamPrevious)
 
 // Function call checking.
 CARBON_DIAGNOSTIC_KIND(AddrSelfIsNonRef)
@@ -168,10 +174,6 @@ CARBON_DIAGNOSTIC_KIND(InCallToFunctionSelf)
 CARBON_DIAGNOSTIC_KIND(MissingObjectInMethodCall)
 
 // Function declaration checking.
-CARBON_DIAGNOSTIC_KIND(FunctionRedeclParamCountDiffers)
-CARBON_DIAGNOSTIC_KIND(FunctionRedeclParamCountPrevious)
-CARBON_DIAGNOSTIC_KIND(FunctionRedeclParamDiffers)
-CARBON_DIAGNOSTIC_KIND(FunctionRedeclParamPrevious)
 CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypeDiffers)
 CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypeDiffersNoReturn)
 CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypePrevious)

+ 4 - 0
toolchain/sem_ir/class.h

@@ -36,6 +36,10 @@ struct Class : public Printable<Class> {
   NameId name_id;
   // The enclosing scope.
   NameScopeId enclosing_scope_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.
+  InstBlockId param_refs_id;
   // The class type, which is the type of `Self` in the class definition.
   TypeId self_type_id;
   // The first declaration of the class. This is a ClassDecl.