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

More support for generic interfaces, aiming for parity with support for generic classes. (#4034)

This is mostly mechanically duplicating work done for generic classes to
also support generic interfaces.

Also fix both generic interfaces and generic classes to support
importing class and interface types with arguments from another file.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
Richard Smith 1 жил өмнө
parent
commit
f772c1f5f3

+ 1 - 0
toolchain/check/BUILD

@@ -135,6 +135,7 @@ cc_library(
     deps = [
         ":context",
         "//common:check",
+        "//toolchain/base:kind_switch",
         "//toolchain/sem_ir:file",
         "//toolchain/sem_ir:ids",
         "//toolchain/sem_ir:inst",

+ 43 - 11
toolchain/check/call.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/check/call.h"
 
+#include "toolchain/base/kind_switch.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/function.h"
@@ -31,24 +32,55 @@ static auto PerformCallToGenericClass(Context& context, Parse::NodeId node_id,
                                             .args_id = converted_args_id});
 }
 
+// Performs a call where the callee is the name of a generic interface, such as
+// `AddWith(i32)`.
+// TODO: Refactor with PerformCallToGenericClass.
+static auto PerformCallToGenericInterface(Context& context,
+                                          Parse::NodeId node_id,
+                                          SemIR::InterfaceId interface_id,
+                                          llvm::ArrayRef<SemIR::InstId> arg_ids)
+    -> SemIR::InstId {
+  auto& interface_info = context.interfaces().Get(interface_id);
+
+  // Convert the arguments to match the parameters.
+  auto converted_args_id = ConvertCallArgs(
+      context, node_id, /*self_id=*/SemIR::InstId::Invalid, arg_ids,
+      /*return_storage_id=*/SemIR::InstId::Invalid, interface_info.decl_id,
+      interface_info.implicit_param_refs_id, interface_info.param_refs_id);
+  return context.AddInst<SemIR::InterfaceType>(
+      node_id, {.type_id = SemIR::TypeId::TypeType,
+                .interface_id = interface_id,
+                .args_id = converted_args_id});
+}
+
 auto PerformCall(Context& context, Parse::NodeId node_id,
                  SemIR::InstId callee_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
     -> SemIR::InstId {
   // Identify the function we're calling.
   auto callee_function = GetCalleeFunction(context.sem_ir(), callee_id);
   if (!callee_function.function_id.is_valid()) {
-    if (auto generic_class = context.types().TryGetAs<SemIR::GenericClassType>(
-            context.insts().Get(callee_id).type_id())) {
-      return PerformCallToGenericClass(context, node_id,
-                                       generic_class->class_id, arg_ids);
-    }
-    if (!callee_function.is_error) {
-      CARBON_DIAGNOSTIC(CallToNonCallable, Error,
-                        "Value of type `{0}` is not callable.", SemIR::TypeId);
-      context.emitter().Emit(node_id, CallToNonCallable,
-                             context.insts().Get(callee_id).type_id());
+    auto type_inst =
+        context.types().GetAsInst(context.insts().Get(callee_id).type_id());
+    CARBON_KIND_SWITCH(type_inst) {
+      case CARBON_KIND(SemIR::GenericClassType generic_class): {
+        return PerformCallToGenericClass(context, node_id,
+                                         generic_class.class_id, arg_ids);
+      }
+      case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): {
+        return PerformCallToGenericInterface(
+            context, node_id, generic_interface.interface_id, arg_ids);
+      }
+      default: {
+        if (!callee_function.is_error) {
+          CARBON_DIAGNOSTIC(CallToNonCallable, Error,
+                            "Value of type `{0}` is not callable.",
+                            SemIR::TypeId);
+          context.emitter().Emit(node_id, CallToNonCallable,
+                                 context.insts().Get(callee_id).type_id());
+        }
+        return SemIR::InstId::BuiltinError;
+      }
     }
-    return SemIR::InstId::BuiltinError;
   }
   auto& callable = context.functions().Get(callee_function.function_id);
 

+ 6 - 0
toolchain/check/context.cpp

@@ -982,6 +982,7 @@ class TypeCompleter {
       case SemIR::AssociatedEntityType::Kind:
       case SemIR::FunctionType::Kind:
       case SemIR::GenericClassType::Kind:
+      case SemIR::GenericInterfaceType::Kind:
       case SemIR::InterfaceType::Kind:
       case SemIR::UnboundElementType::Kind: {
         // These types have no runtime operations, so we use an empty value
@@ -1110,6 +1111,11 @@ auto Context::GetGenericClassType(SemIR::ClassId class_id) -> SemIR::TypeId {
   return GetCompleteTypeImpl<SemIR::GenericClassType>(*this, class_id);
 }
 
+auto Context::GetGenericInterfaceType(SemIR::InterfaceId interface_id)
+    -> SemIR::TypeId {
+  return GetCompleteTypeImpl<SemIR::GenericInterfaceType>(*this, interface_id);
+}
+
 auto Context::GetPointerType(SemIR::TypeId pointee_type_id) -> SemIR::TypeId {
   return GetTypeImpl<SemIR::PointerType>(*this, pointee_type_id);
 }

+ 6 - 0
toolchain/check/context.h

@@ -264,6 +264,12 @@ class Context {
   // type will be complete.
   auto GetGenericClassType(SemIR::ClassId class_id) -> SemIR::TypeId;
 
+  // Gets a generic interface type, which is the type of a name of a generic
+  // interface, such as the type of `AddWith` given
+  // `interface AddWith(T:! type)`. The returned type will be complete.
+  auto GetGenericInterfaceType(SemIR::InterfaceId interface_id)
+      -> SemIR::TypeId;
+
   // Returns a pointer type whose pointee type is `pointee_type_id`.
   auto GetPointerType(SemIR::TypeId pointee_type_id) -> SemIR::TypeId;
 

+ 23 - 7
toolchain/check/eval.cpp

@@ -128,6 +128,9 @@ static auto GetConstantValue(Context& context, SemIR::TypeId type_id,
 // corresponding block of those values.
 static auto GetConstantValue(Context& context, SemIR::InstBlockId inst_block_id,
                              Phase* phase) -> SemIR::InstBlockId {
+  if (!inst_block_id.is_valid()) {
+    return SemIR::InstBlockId::Invalid;
+  }
   auto insts = context.inst_blocks().Get(inst_block_id);
   llvm::SmallVector<SemIR::InstId> const_insts;
   for (auto inst_id : insts) {
@@ -154,6 +157,9 @@ static auto GetConstantValue(Context& context, SemIR::InstBlockId inst_block_id,
 // extract its phase.
 static auto GetConstantValue(Context& context, SemIR::TypeBlockId type_block_id,
                              Phase* phase) -> SemIR::TypeBlockId {
+  if (!type_block_id.is_valid()) {
+    return SemIR::TypeBlockId::Invalid;
+  }
   auto types = context.type_blocks().Get(type_block_id);
   for (auto type_id : types) {
     GetConstantValue(context, type_id, phase);
@@ -169,7 +175,7 @@ static auto ReplaceFieldWithConstantValue(Context& context, InstT* inst,
                                           FieldIdT InstT::*field, Phase* phase)
     -> bool {
   auto unwrapped = GetConstantValue(context, inst->*field, phase);
-  if (!unwrapped.is_valid()) {
+  if (!unwrapped.is_valid() && (inst->*field).is_valid()) {
     return false;
   }
   inst->*field = unwrapped;
@@ -915,11 +921,11 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
                                         &SemIR::BoundMethod::object_id,
                                         &SemIR::BoundMethod::function_id);
     case SemIR::ClassType::Kind:
-      // TODO: Look at generic arguments once they're modeled.
-      return MakeConstantResult(context, inst, Phase::Template);
+      return RebuildIfFieldsAreConstant(context, inst,
+                                        &SemIR::ClassType::args_id);
     case SemIR::InterfaceType::Kind:
-      // TODO: Look at generic arguments once they're modeled.
-      return MakeConstantResult(context, inst, Phase::Template);
+      return RebuildIfFieldsAreConstant(context, inst,
+                                        &SemIR::InterfaceType::args_id);
     case SemIR::InterfaceWitness::Kind:
       return RebuildIfFieldsAreConstant(context, inst,
                                         &SemIR::InterfaceWitness::elements_id);
@@ -980,6 +986,7 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
     case SemIR::Builtin::Kind:
     case SemIR::FunctionType::Kind:
     case SemIR::GenericClassType::Kind:
+    case SemIR::GenericInterfaceType::Kind:
       // Builtins are always template constants.
       return MakeConstantResult(context, inst, Phase::Template);
 
@@ -992,7 +999,7 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
     }
 
     case CARBON_KIND(SemIR::ClassDecl class_decl): {
-      // If the class has generic arguments, we don't produce a class type, but
+      // If the class has generic parameters, we don't produce a class type, but
       // a callable whose return value is a class type.
       if (context.classes().Get(class_decl.class_id).is_generic()) {
         return MakeConstantResult(
@@ -1009,7 +1016,16 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
           Phase::Template);
     }
     case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
-      // TODO: Once interfaces have generic arguments, handle them.
+      // If the interface has generic parameters, we don't produce an interface
+      // type, but a callable whose return value is an interface type.
+      if (context.interfaces().Get(interface_decl.interface_id).is_generic()) {
+        return MakeConstantResult(
+            context,
+            SemIR::StructValue{.type_id = interface_decl.type_id,
+                               .elements_id = SemIR::InstBlockId::Empty},
+            Phase::Template);
+      }
+      // A non-generic interface declaration evaluates to the interface type.
       return MakeConstantResult(
           context,
           SemIR::InterfaceType{.type_id = SemIR::TypeId::TypeType,

+ 3 - 1
toolchain/check/handle_class.cpp

@@ -127,7 +127,8 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
 
   auto prev_class_id = SemIR::ClassId::Invalid;
   auto prev_import_ir_id = SemIR::ImportIRId::Invalid;
-  CARBON_KIND_SWITCH(context.insts().Get(prev_id)) {
+  auto prev = context.insts().Get(prev_id);
+  CARBON_KIND_SWITCH(prev) {
     case CARBON_KIND(SemIR::ClassDecl class_decl): {
       prev_class_id = class_decl.class_id;
       break;
@@ -174,6 +175,7 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
                        prev_import_ir_id)) {
     // When merging, use the existing entity rather than adding a new one.
     class_decl.class_id = prev_class_id;
+    class_decl.type_id = prev.type_id();
     // TODO: Validate that the redeclaration doesn't set an access modifier.
   }
 }

+ 12 - 6
toolchain/check/handle_interface.cpp

@@ -65,6 +65,7 @@ static auto BuildInterfaceDecl(Context& context,
                   existing_interface_decl->interface_id)))) {
         // This is a redeclaration of an existing interface.
         interface_decl.interface_id = existing_interface_decl->interface_id;
+        interface_decl.type_id = existing_interface_decl->type_id;
         // TODO: If the new declaration is a definition, keep its parameter
         // and implicit parameter lists rather than the ones from the
         // previous declaration.
@@ -81,12 +82,17 @@ static auto BuildInterfaceDecl(Context& context,
     // there was an error in the qualifier, we will have lost track of the
     // interface name here. We should keep track of it even if the name is
     // invalid.
-    interface_decl.interface_id = context.interfaces().Add(
-        {.name_id = name_context.name_id_for_new_inst(),
-         .parent_scope_id = name_context.parent_scope_id_for_new_inst(),
-         .implicit_param_refs_id = name.implicit_params_id,
-         .param_refs_id = name.params_id,
-         .decl_id = interface_decl_id});
+    SemIR::Interface interface_info = {
+        .name_id = name_context.name_id_for_new_inst(),
+        .parent_scope_id = name_context.parent_scope_id_for_new_inst(),
+        .implicit_param_refs_id = name.implicit_params_id,
+        .param_refs_id = name.params_id,
+        .decl_id = interface_decl_id};
+    interface_decl.interface_id = context.interfaces().Add(interface_info);
+    if (interface_info.is_generic()) {
+      interface_decl.type_id =
+          context.GetGenericInterfaceType(interface_decl.interface_id);
+    }
   }
 
   // TODO: For a generic interface declaration, set the `type_id` to a suitable

+ 137 - 16
toolchain/check/import_ref.cpp

@@ -371,6 +371,38 @@ class ImportRefResolver {
     return GetLocalConstantId(import_ir_.types().GetInstId(type_id));
   }
 
+  // Gets the local constant values corresponding to an imported inst block.
+  auto GetLocalInstBlockContents(SemIR::InstBlockId import_block_id)
+      -> llvm::SmallVector<SemIR::InstId> {
+    llvm::SmallVector<SemIR::InstId> inst_ids;
+    if (!import_block_id.is_valid() ||
+        import_block_id == SemIR::InstBlockId::Empty) {
+      return inst_ids;
+    }
+
+    // Import all the values in the block.
+    auto import_block = import_ir_.inst_blocks().Get(import_block_id);
+    inst_ids.reserve(import_block.size());
+    for (auto import_inst_id : import_block) {
+      auto const_id = GetLocalConstantId(import_inst_id);
+      inst_ids.push_back(const_id.inst_id());
+    }
+
+    return inst_ids;
+  }
+
+  // Gets a local canonical instruction block ID corresponding to an imported
+  // inst block whose contents were already imported, for example by
+  // GetLocalInstBlockContents.
+  auto GetLocalCanonicalInstBlockId(SemIR::InstBlockId import_block_id,
+                                    llvm::ArrayRef<SemIR::InstId> contents)
+      -> SemIR::InstBlockId {
+    if (!import_block_id.is_valid()) {
+      return SemIR::InstBlockId::Invalid;
+    }
+    return context_.inst_blocks().AddCanonical(contents);
+  }
+
   // Returns the ConstantId for each parameter's type. Adds unresolved constants
   // to work_stack_.
   auto GetLocalParamConstantIds(SemIR::InstBlockId param_refs_id)
@@ -523,13 +555,30 @@ class ImportRefResolver {
       case CARBON_KIND(SemIR::InterfaceType inst): {
         return context_.interfaces().Get(inst.interface_id).scope_id;
       }
-      default:
+      case SemIR::StructValue::Kind: {
+        auto type_inst = context_.types().GetAsInst(name_scope_inst.type_id());
+        CARBON_KIND_SWITCH(type_inst) {
+          case CARBON_KIND(SemIR::GenericClassType inst): {
+            return context_.classes().Get(inst.class_id).scope_id;
+          }
+          case CARBON_KIND(SemIR::GenericInterfaceType inst): {
+            return context_.interfaces().Get(inst.interface_id).scope_id;
+          }
+          default: {
+            break;
+          }
+        }
+        break;
+      }
+      default: {
         if (const_id == SemIR::ConstantId::Error) {
           return SemIR::NameScopeId::Invalid;
         }
-        CARBON_FATAL() << "Unexpected instruction kind for name scope: "
-                       << name_scope_inst;
+        break;
+      }
     }
+    CARBON_FATAL() << "Unexpected instruction kind for name scope: "
+                   << name_scope_inst;
   }
 
   // Adds ImportRefUnloaded entries for members of the imported scope, for name
@@ -628,6 +677,9 @@ class ImportRefResolver {
       case CARBON_KIND(SemIR::GenericClassType inst): {
         return TryResolveTypedInst(inst);
       }
+      case CARBON_KIND(SemIR::GenericInterfaceType inst): {
+        return TryResolveTypedInst(inst);
+      }
       case CARBON_KIND(SemIR::ImportRefLoaded inst): {
         return TryResolveTypedInst(inst, inst_id);
       }
@@ -900,9 +952,26 @@ class ImportRefResolver {
     CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
     auto class_const_id =
         GetLocalConstantId(import_ir_.classes().Get(inst.class_id).decl_id);
+    auto args = GetLocalInstBlockContents(inst.args_id);
     if (HasNewWork(initial_work)) {
       return ResolveResult::Retry();
     }
+
+    // Find the corresponding class type. For a non-generic class, this is the
+    // type of the class declaration. For a generic class, build a class type
+    // referencing this specialization of the generic class.
+    auto class_const_inst = context_.insts().Get(class_const_id.inst_id());
+    if (!class_const_inst.Is<SemIR::ClassType>()) {
+      auto generic_class_type = context_.types().GetAs<SemIR::GenericClassType>(
+          class_const_inst.type_id());
+      auto args_id = GetLocalCanonicalInstBlockId(inst.args_id, args);
+      class_const_id =
+          TryEvalInst(context_, SemIR::InstId::Invalid,
+                      SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
+                                       .class_id = generic_class_type.class_id,
+                                       .args_id = args_id});
+    }
+
     return {.const_id = class_const_id};
   }
 
@@ -1038,6 +1107,21 @@ class ImportRefResolver {
     return {.const_id = context_.types().GetConstantId(class_val.type_id())};
   }
 
+  auto TryResolveTypedInst(SemIR::GenericInterfaceType inst) -> ResolveResult {
+    auto initial_work = work_stack_.size();
+    CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+    auto interface_const_id = GetLocalConstantId(
+        import_ir_.interfaces().Get(inst.interface_id).decl_id);
+    if (HasNewWork(initial_work)) {
+      return ResolveResult::Retry();
+    }
+    auto interface_val = context_.insts().Get(interface_const_id.inst_id());
+    CARBON_CHECK(context_.types().Is<SemIR::GenericInterfaceType>(
+        interface_val.type_id()));
+    return {.const_id =
+                context_.types().GetConstantId(interface_val.type_id())};
+  }
+
   auto TryResolveTypedInst(SemIR::ImportRefLoaded /*inst*/,
                            SemIR::InstId inst_id) -> ResolveResult {
     auto initial_work = work_stack_.size();
@@ -1063,7 +1147,7 @@ class ImportRefResolver {
   // Make a declaration of an interface. This is done as a separate step from
   // importing the interface definition in order to resolve cycles.
   auto MakeInterfaceDecl(const SemIR::Interface& import_interface)
-      -> SemIR::ConstantId {
+      -> std::pair<SemIR::InterfaceId, SemIR::ConstantId> {
     SemIR::InterfaceDecl interface_decl = {
         .type_id = SemIR::TypeId::TypeType,
         .interface_id = SemIR::InterfaceId::Invalid,
@@ -1073,7 +1157,7 @@ class ImportRefResolver {
             AddImportIRInst(import_interface.decl_id), interface_decl));
 
     // Start with an incomplete interface.
-    SemIR::Interface new_interface = {
+    interface_decl.interface_id = context_.interfaces().Add({
         .name_id = GetLocalNameId(import_interface.name_id),
         // These are set in the second pass once we've imported them. Import
         // enough of the parameter lists that we know whether this interface is
@@ -1089,14 +1173,17 @@ class ImportRefResolver {
                              ? SemIR::InstBlockId::Empty
                              : SemIR::InstBlockId::Invalid,
         .decl_id = interface_decl_id,
-    };
+    });
+
+    if (import_interface.is_generic()) {
+      interface_decl.type_id =
+          context_.GetGenericInterfaceType(interface_decl.interface_id);
+    }
 
     // Write the interface ID into the InterfaceDecl.
-    interface_decl.interface_id = context_.interfaces().Add(new_interface);
     context_.ReplaceInstBeforeConstantUse(interface_decl_id, interface_decl);
-
-    // Set the constant value for the imported interface.
-    return context_.constant_values().Get(interface_decl_id);
+    return {interface_decl.interface_id,
+            context_.constant_values().Get(interface_decl_id)};
   }
 
   // Imports the definition for an interface that has been imported as a forward
@@ -1129,9 +1216,25 @@ class ImportRefResolver {
     const auto& import_interface =
         import_ir_.interfaces().Get(inst.interface_id);
 
-    // On the first pass, create a forward declaration of the interface.
+    SemIR::InterfaceId interface_id = SemIR::InterfaceId::Invalid;
     if (!interface_const_id.is_valid()) {
-      interface_const_id = MakeInterfaceDecl(import_interface);
+      // On the first pass, create a forward declaration of the interface.
+      std::tie(interface_id, interface_const_id) =
+          MakeInterfaceDecl(import_interface);
+    } else {
+      // On the second pass, compute the interface ID from the constant value of
+      // the declaration.
+      auto interface_const_inst =
+          context_.insts().Get(interface_const_id.inst_id());
+      if (auto interface_type =
+              interface_const_inst.TryAs<SemIR::InterfaceType>()) {
+        interface_id = interface_type->interface_id;
+      } else {
+        auto generic_interface_type =
+            context_.types().GetAs<SemIR::GenericInterfaceType>(
+                interface_const_inst.type_id());
+        interface_id = generic_interface_type.interface_id;
+      }
     }
 
     auto initial_work = work_stack_.size();
@@ -1148,10 +1251,7 @@ class ImportRefResolver {
       return ResolveResult::Retry(interface_const_id);
     }
 
-    auto& new_interface = context_.interfaces().Get(
-        context_.insts()
-            .GetAs<SemIR::InterfaceType>(interface_const_id.inst_id())
-            .interface_id);
+    auto& new_interface = context_.interfaces().Get(interface_id);
     new_interface.parent_scope_id = parent_scope_id;
     new_interface.implicit_param_refs_id = GetLocalParamRefsId(
         import_interface.implicit_param_refs_id, implicit_param_const_ids);
@@ -1169,9 +1269,30 @@ class ImportRefResolver {
     CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
     auto interface_const_id = GetLocalConstantId(
         import_ir_.interfaces().Get(inst.interface_id).decl_id);
+    auto args = GetLocalInstBlockContents(inst.args_id);
     if (HasNewWork(initial_work)) {
       return ResolveResult::Retry();
     }
+
+    // Find the corresponding interface type. For a non-generic interface, this
+    // is the type of the interface declaration. For a generic interface, build
+    // a interface type referencing this specialization of the generic
+    // interface.
+    auto interface_const_inst =
+        context_.insts().Get(interface_const_id.inst_id());
+    if (!interface_const_inst.Is<SemIR::InterfaceType>()) {
+      auto generic_interface_type =
+          context_.types().GetAs<SemIR::GenericInterfaceType>(
+              interface_const_inst.type_id());
+      auto args_id = GetLocalCanonicalInstBlockId(inst.args_id, args);
+      interface_const_id =
+          TryEvalInst(context_, SemIR::InstId::Invalid,
+                      SemIR::InterfaceType{
+                          .type_id = SemIR::TypeId::TypeType,
+                          .interface_id = generic_interface_type.interface_id,
+                          .args_id = args_id});
+    }
+
     return {.const_id = interface_const_id};
   }
 

+ 2 - 2
toolchain/check/testdata/class/generic/call.carbon

@@ -77,10 +77,10 @@ var a: Class(5, i32*);
 // CHECK:STDOUT:   %.2: type = struct_type {} [template]
 // CHECK:STDOUT:   %.3: type = ptr_type i32 [template]
 // CHECK:STDOUT:   %.4: i32 = int_literal 5 [template]
-// CHECK:STDOUT:   %Class.3: type = class_type @Class, (file.%.loc6_17.3, file.%.loc6_20) [template]
+// CHECK:STDOUT:   %Class.3: type = class_type @Class, (%.3, %.4) [template]
 // CHECK:STDOUT:   %.5: type = ptr_type %.2 [template]
 // CHECK:STDOUT:   %.6: i32 = int_literal 0 [template]
-// CHECK:STDOUT:   %Class.4: type = class_type @Class, (file.%.loc9_13, file.%.loc9_18) [template]
+// CHECK:STDOUT:   %Class.4: type = class_type @Class, (%.1, %.6) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {

+ 1 - 1
toolchain/check/testdata/class/generic/fail_todo_use.carbon

@@ -55,7 +55,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
 // CHECK:STDOUT:   %Run.type: type = fn_type @Run [template]
 // CHECK:STDOUT:   %Run: %Run.type = struct_value () [template]
-// CHECK:STDOUT:   %Class.3: type = class_type @Class, (@Run.%.loc25_15.2) [template]
+// CHECK:STDOUT:   %Class.3: type = class_type @Class, (i32) [template]
 // CHECK:STDOUT:   %.7: i32 = int_literal 0 [template]
 // CHECK:STDOUT:   %.8: type = struct_type {.k: i32} [template]
 // CHECK:STDOUT:   %.9: type = ptr_type %Class.3 [template]

+ 395 - 11
toolchain/check/testdata/class/generic/import.carbon

@@ -14,6 +14,13 @@ library "foo";
 
 class Class(T:! type);
 
+class CompleteClass(T:! type) {
+  var n: i32;
+  fn F() -> i32 { return 0; }
+}
+
+fn F() -> CompleteClass(i32);
+
 // --- foo.impl.carbon
 
 impl library "foo";
@@ -22,6 +29,49 @@ class Class(T:! type) {
   var x: T;
 }
 
+fn F() -> CompleteClass(i32) {
+  return {.n = 1};
+}
+
+// --- use_foo.carbon
+
+library "use_foo";
+import library "foo";
+
+fn Use() -> i32 {
+  var v: CompleteClass(i32) = F();
+  return v.F();
+}
+
+// --- fail_todo_use_foo.carbon
+
+library "fail_todo_use_foo";
+import library "foo";
+
+fn Use() -> i32 {
+  var v: CompleteClass(i32) = F();
+  // TODO: This should be accepted.
+  // CHECK:STDERR: fail_todo_use_foo.carbon:[[@LINE+4]]:10: ERROR: Cannot implicitly convert from `CompleteClass` to `CompleteClass`.
+  // CHECK:STDERR:   return v.n;
+  // CHECK:STDERR:          ^~~
+  // CHECK:STDERR:
+  return v.n;
+}
+
+// --- fail_generic_arg_mismatch.carbon
+
+library "fail_generic_arg_mismatch";
+import library "foo";
+
+fn Use() {
+  // TODO: Include the generic arguments in the formatted type name.
+  // CHECK:STDERR: fail_generic_arg_mismatch.carbon:[[@LINE+4]]:3: ERROR: Cannot implicitly convert from `CompleteClass` to `CompleteClass`.
+  // CHECK:STDERR:   var v: CompleteClass(i32*) = F();
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  var v: CompleteClass(i32*) = F();
+}
+
 // --- fail_bad_foo.impl.carbon
 
 impl library "foo";
@@ -46,45 +96,131 @@ class Class(U:! type) {
 // CHECK:STDOUT:   %.1: type = tuple_type () [template]
 // CHECK:STDOUT:   %Class.1: %Class.type = struct_value () [template]
 // CHECK:STDOUT:   %Class.2: type = class_type @Class [template]
+// CHECK:STDOUT:   %CompleteClass.type: type = generic_class_type @CompleteClass [template]
+// CHECK:STDOUT:   %CompleteClass.1: %CompleteClass.type = struct_value () [template]
+// CHECK:STDOUT:   %CompleteClass.2: type = class_type @CompleteClass [template]
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = unbound_element_type %CompleteClass.2, i32 [template]
+// CHECK:STDOUT:   %F.type.1: type = fn_type @F.1 [template]
+// CHECK:STDOUT:   %F.1: %F.type.1 = struct_value () [template]
+// CHECK:STDOUT:   %.3: type = struct_type {.n: i32} [template]
+// CHECK:STDOUT:   %.4: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %CompleteClass.3: type = class_type @CompleteClass, (i32) [template]
+// CHECK:STDOUT:   %F.type.2: type = fn_type @F.2 [template]
+// CHECK:STDOUT:   %F.2: %F.type.2 = 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:     .CompleteClass = %CompleteClass.decl
+// CHECK:STDOUT:     .F = %F.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %Class.decl: %Class.type = class_decl @Class [template = constants.%Class.1] {
 // 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:   %CompleteClass.decl: %CompleteClass.type = class_decl @CompleteClass [template = constants.%CompleteClass.1] {
+// CHECK:STDOUT:     %T.loc6_21.1: type = param T
+// CHECK:STDOUT:     %T.loc6_21.2: type = bind_symbolic_name T 0, %T.loc6_21.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.2: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.3: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %F.decl: %F.type.2 = fn_decl @F.2 [template = constants.%F.2] {
+// CHECK:STDOUT:     %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, %CompleteClass.decl [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc11_24.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc11_24.2: type = converted %int.make_type_32, %.loc11_24.1 [template = i32]
+// CHECK:STDOUT:     %CompleteClass: type = class_type @CompleteClass, (%.loc11_24.2) [template = constants.%CompleteClass.3]
+// CHECK:STDOUT:     @F.2.%return: ref %CompleteClass.3 = var <return slot>
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Class;
 // CHECK:STDOUT:
+// CHECK:STDOUT: class @CompleteClass {
+// CHECK:STDOUT:   %int.make_type_32.loc7: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc7_10.1: type = value_of_initializer %int.make_type_32.loc7 [template = i32]
+// CHECK:STDOUT:   %.loc7_10.2: type = converted %int.make_type_32.loc7, %.loc7_10.1 [template = i32]
+// CHECK:STDOUT:   %.loc7_8: %.2 = field_decl n, element0 [template]
+// CHECK:STDOUT:   %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {
+// CHECK:STDOUT:     %int.make_type_32.loc8: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc8_13.1: type = value_of_initializer %int.make_type_32.loc8 [template = i32]
+// CHECK:STDOUT:     %.loc8_13.2: type = converted %int.make_type_32.loc8, %.loc8_13.1 [template = i32]
+// CHECK:STDOUT:     %return.var: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%CompleteClass.2
+// CHECK:STDOUT:   .n = %.loc7_8
+// CHECK:STDOUT:   .F = %F.decl
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8: i32 = int_literal 0 [template = constants.%.4]
+// CHECK:STDOUT:   return %.loc8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() -> %CompleteClass.3;
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- foo.impl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+11> [symbolic]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+30> [symbolic]
 // CHECK:STDOUT:   %Class.type: type = generic_class_type @Class [template]
 // CHECK:STDOUT:   %.1: type = tuple_type () [template]
-// CHECK:STDOUT:   %Class: %Class.type = struct_value () [template]
-// CHECK:STDOUT:   %struct: type = struct_value () [template]
-// CHECK:STDOUT:   %.2: type = unbound_element_type %Class, %T [symbolic]
+// CHECK:STDOUT:   %Class.1: %Class.type = struct_value () [template]
+// CHECK:STDOUT:   %Class.2: type = class_type @Class [template]
+// CHECK:STDOUT:   %.2: type = unbound_element_type %Class.2, %T [symbolic]
 // CHECK:STDOUT:   %.3: type = struct_type {.x: %T} [symbolic]
+// CHECK:STDOUT:   %CompleteClass.type: type = generic_class_type @CompleteClass [template]
+// CHECK:STDOUT:   %CompleteClass.1: %CompleteClass.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = struct_type {.n: i32} [template]
+// CHECK:STDOUT:   %CompleteClass.2: type = class_type @CompleteClass [template]
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %CompleteClass.3: type = class_type @CompleteClass, (i32) [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.5: type = ptr_type %.4 [template]
+// CHECK:STDOUT:   %.6: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %struct: %CompleteClass.3 = struct_value (%.6) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .CompleteClass = %import_ref.2
 // CHECK:STDOUT:     .Core = %Core
 // CHECK:STDOUT:     .Class = %Class.decl
+// CHECK:STDOUT:     .F = %F.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: %Class.type = import_ref ir0, inst+5, loaded [template = constants.%Class]
+// CHECK:STDOUT:   %import_ref.1: %Class.type = import_ref ir0, inst+5, loaded [template = constants.%Class.1]
+// CHECK:STDOUT:   %import_ref.2: %CompleteClass.type = import_ref ir0, inst+12, loaded [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:   %import_ref.3: %F.type = import_ref ir0, inst+48, loaded [template = constants.%F]
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %Class.decl: type = class_decl @Class [template = constants.%struct] {
+// CHECK:STDOUT:   %Class.decl: %Class.type = class_decl @Class [template = constants.%Class.1] {
 // 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:   %import_ref.4 = import_ref ir0, inst+15, unloaded
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir0, inst+25, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref ir0, inst+33, unloaded
+// CHECK:STDOUT:   %import_ref.7: %Int32.type = import_ref ir3, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
+// CHECK:STDOUT:     %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, %import_ref.2 [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc8_24.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc8_24.2: type = converted %int.make_type_32, %.loc8_24.1 [template = i32]
+// CHECK:STDOUT:     %CompleteClass: type = class_type @CompleteClass, (%.loc8_24.2) [template = constants.%CompleteClass.3]
+// CHECK:STDOUT:     @F.%return: ref %CompleteClass.3 = var <return slot>
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Class {
@@ -92,18 +228,262 @@ class Class(U:! type) {
 // CHECK:STDOUT:   %.loc5: %.2 = field_decl x, element0 [template]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = constants.%Class
+// CHECK:STDOUT:   .Self = constants.%Class.2
 // CHECK:STDOUT:   .x = %.loc5
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: class @CompleteClass {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.4
+// CHECK:STDOUT:   .n = file.%import_ref.5
+// CHECK:STDOUT:   .F = file.%import_ref.6
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %return: %CompleteClass.3 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc9_16: i32 = int_literal 1 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc9_17.1: %.4 = struct_literal (%.loc9_16)
+// CHECK:STDOUT:   %.loc9_17.2: ref i32 = class_element_access %return, element0
+// CHECK:STDOUT:   %.loc9_17.3: init i32 = initialize_from %.loc9_16 to %.loc9_17.2 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc9_17.4: init %CompleteClass.3 = class_init (%.loc9_17.3), %return [template = constants.%struct]
+// CHECK:STDOUT:   %.loc9_18: init %CompleteClass.3 = converted %.loc9_17.1, %.loc9_17.4 [template = constants.%struct]
+// CHECK:STDOUT:   return %.loc9_18 to %return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- use_foo.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %Use.type: type = fn_type @Use [template]
+// CHECK:STDOUT:   %Use: %Use.type = struct_value () [template]
+// CHECK:STDOUT:   %CompleteClass.type: type = generic_class_type @CompleteClass [template]
+// CHECK:STDOUT:   %CompleteClass.1: %CompleteClass.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {.n: i32} [template]
+// CHECK:STDOUT:   %CompleteClass.2: type = class_type @CompleteClass [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+27> [symbolic]
+// CHECK:STDOUT:   %CompleteClass.3: type = class_type @CompleteClass, (i32) [template]
+// CHECK:STDOUT:   %.3: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %F.type.1: type = fn_type @F.1 [template]
+// CHECK:STDOUT:   %F.1: %F.type.1 = struct_value () [template]
+// CHECK:STDOUT:   %F.type.2: type = fn_type @F.2 [template]
+// CHECK:STDOUT:   %F.2: %F.type.2 = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Class = %import_ref.1
+// CHECK:STDOUT:     .CompleteClass = %import_ref.2
+// CHECK:STDOUT:     .F = %import_ref.3
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Use = %Use.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+5, unloaded
+// CHECK:STDOUT:   %import_ref.2: %CompleteClass.type = import_ref ir1, inst+12, loaded [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:   %import_ref.3: %F.type.1 = import_ref ir1, inst+48, loaded [template = constants.%F.1]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %import_ref.4: %Int32.type = import_ref ir4, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %Use.decl: %Use.type = fn_decl @Use [template = constants.%Use] {
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc5_13.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc5_13.2: type = converted %int.make_type_32, %.loc5_13.1 [template = i32]
+// CHECK:STDOUT:     @Use.%return: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+15, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref ir1, inst+25, unloaded
+// CHECK:STDOUT:   %import_ref.7: %F.type.2 = import_ref ir1, inst+33, loaded [template = constants.%F.2]
+// CHECK:STDOUT:   %import_ref.8: %Int32.type = import_ref ir4, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @CompleteClass {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.5
+// CHECK:STDOUT:   .n = file.%import_ref.6
+// CHECK:STDOUT:   .F = file.%import_ref.7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Use() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, file.%import_ref.2 [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc6_23.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:   %.loc6_23.2: type = converted %int.make_type_32, %.loc6_23.1 [template = i32]
+// CHECK:STDOUT:   %CompleteClass: type = class_type @CompleteClass, (%.loc6_23.2) [template = constants.%CompleteClass.3]
+// CHECK:STDOUT:   %v.var: ref %CompleteClass.3 = var v
+// CHECK:STDOUT:   %v: ref %CompleteClass.3 = bind_name v, %v.var
+// CHECK:STDOUT:   %F.ref.loc6: %F.type.1 = name_ref F, file.%import_ref.3 [template = constants.%F.1]
+// CHECK:STDOUT:   %.loc6_7: ref %CompleteClass.3 = splice_block %v.var {}
+// CHECK:STDOUT:   %F.call.loc6: init %CompleteClass.3 = call %F.ref.loc6() to %.loc6_7
+// CHECK:STDOUT:   assign %v.var, %F.call.loc6
+// CHECK:STDOUT:   %v.ref: ref %CompleteClass.3 = name_ref v, %v
+// CHECK:STDOUT:   %F.ref.loc7: %F.type.2 = name_ref F, file.%import_ref.7 [template = constants.%F.2]
+// CHECK:STDOUT:   %F.call.loc7: init i32 = call %F.ref.loc7()
+// CHECK:STDOUT:   %.loc7_15.1: i32 = value_of_initializer %F.call.loc7
+// CHECK:STDOUT:   %.loc7_15.2: i32 = converted %F.call.loc7, %.loc7_15.1
+// CHECK:STDOUT:   return %.loc7_15.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1() -> %CompleteClass.3;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() -> i32;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_use_foo.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %Use.type: type = fn_type @Use [template]
+// CHECK:STDOUT:   %Use: %Use.type = struct_value () [template]
+// CHECK:STDOUT:   %CompleteClass.type: type = generic_class_type @CompleteClass [template]
+// CHECK:STDOUT:   %CompleteClass.1: %CompleteClass.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {.n: i32} [template]
+// CHECK:STDOUT:   %CompleteClass.2: type = class_type @CompleteClass [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+27> [symbolic]
+// CHECK:STDOUT:   %CompleteClass.3: type = class_type @CompleteClass, (i32) [template]
+// CHECK:STDOUT:   %.3: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = unbound_element_type %CompleteClass.2, i32 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Class = %import_ref.1
+// CHECK:STDOUT:     .CompleteClass = %import_ref.2
+// CHECK:STDOUT:     .F = %import_ref.3
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Use = %Use.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+5, unloaded
+// CHECK:STDOUT:   %import_ref.2: %CompleteClass.type = import_ref ir1, inst+12, loaded [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:   %import_ref.3: %F.type = import_ref ir1, inst+48, loaded [template = constants.%F]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %import_ref.4: %Int32.type = import_ref ir4, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %Use.decl: %Use.type = fn_decl @Use [template = constants.%Use] {
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc5_13.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc5_13.2: type = converted %int.make_type_32, %.loc5_13.1 [template = i32]
+// CHECK:STDOUT:     @Use.%return: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+15, unloaded
+// CHECK:STDOUT:   %import_ref.6: %.4 = import_ref ir1, inst+25, loaded [template = imports.%.1]
+// CHECK:STDOUT:   %import_ref.7 = import_ref ir1, inst+33, unloaded
+// CHECK:STDOUT:   %import_ref.8: %Int32.type = import_ref ir4, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @CompleteClass {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.5
+// CHECK:STDOUT:   .n = file.%import_ref.6
+// CHECK:STDOUT:   .F = file.%import_ref.7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Use() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, file.%import_ref.2 [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc6_23.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:   %.loc6_23.2: type = converted %int.make_type_32, %.loc6_23.1 [template = i32]
+// CHECK:STDOUT:   %CompleteClass: type = class_type @CompleteClass, (%.loc6_23.2) [template = constants.%CompleteClass.3]
+// CHECK:STDOUT:   %v.var: ref %CompleteClass.3 = var v
+// CHECK:STDOUT:   %v: ref %CompleteClass.3 = bind_name v, %v.var
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%import_ref.3 [template = constants.%F]
+// CHECK:STDOUT:   %.loc6_7: ref %CompleteClass.3 = splice_block %v.var {}
+// CHECK:STDOUT:   %F.call: init %CompleteClass.3 = call %F.ref() to %.loc6_7
+// CHECK:STDOUT:   assign %v.var, %F.call
+// CHECK:STDOUT:   %v.ref: ref %CompleteClass.3 = name_ref v, %v
+// CHECK:STDOUT:   %n.ref: %.4 = name_ref n, file.%import_ref.6 [template = imports.%.1]
+// CHECK:STDOUT:   %.loc12: i32 = class_element_access <error>, element0 [template = <error>]
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %CompleteClass.3;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_generic_arg_mismatch.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Use.type: type = fn_type @Use [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Use: %Use.type = struct_value () [template]
+// CHECK:STDOUT:   %CompleteClass.type: type = generic_class_type @CompleteClass [template]
+// CHECK:STDOUT:   %CompleteClass.1: %CompleteClass.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = struct_type {.n: i32} [template]
+// CHECK:STDOUT:   %CompleteClass.2: type = class_type @CompleteClass [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+18> [symbolic]
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %.3: type = ptr_type i32 [template]
+// CHECK:STDOUT:   %CompleteClass.3: type = class_type @CompleteClass, (%.3) [template]
+// CHECK:STDOUT:   %.4: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %CompleteClass.4: type = class_type @CompleteClass, (i32) [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Class = %import_ref.1
+// CHECK:STDOUT:     .CompleteClass = %import_ref.2
+// CHECK:STDOUT:     .F = %import_ref.3
+// CHECK:STDOUT:     .Core = %Core
+// CHECK:STDOUT:     .Use = %Use.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir1, inst+5, unloaded
+// CHECK:STDOUT:   %import_ref.2: %CompleteClass.type = import_ref ir1, inst+12, loaded [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:   %import_ref.3: %F.type = import_ref ir1, inst+48, loaded [template = constants.%F]
+// CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
+// CHECK:STDOUT:   %Use.decl: %Use.type = fn_decl @Use [template = constants.%Use] {}
+// CHECK:STDOUT:   %import_ref.4 = import_ref ir1, inst+15, unloaded
+// CHECK:STDOUT:   %import_ref.5 = import_ref ir1, inst+25, unloaded
+// CHECK:STDOUT:   %import_ref.6 = import_ref ir1, inst+33, unloaded
+// CHECK:STDOUT:   %import_ref.7: %Int32.type = import_ref ir4, inst+3, loaded [template = constants.%Int32]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @CompleteClass {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.4
+// CHECK:STDOUT:   .n = file.%import_ref.5
+// CHECK:STDOUT:   .F = file.%import_ref.6
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Use() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, file.%import_ref.2 [template = constants.%CompleteClass.1]
+// CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:   %.loc11_27.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:   %.loc11_27.2: type = converted %int.make_type_32, %.loc11_27.1 [template = i32]
+// CHECK:STDOUT:   %.loc11_27.3: type = ptr_type i32 [template = constants.%.3]
+// CHECK:STDOUT:   %CompleteClass: type = class_type @CompleteClass, (%.loc11_27.3) [template = constants.%CompleteClass.3]
+// CHECK:STDOUT:   %v.var: ref %CompleteClass.3 = var v
+// CHECK:STDOUT:   %v: ref %CompleteClass.3 = bind_name v, %v.var
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%import_ref.3 [template = constants.%F]
+// CHECK:STDOUT:   %.loc11_33: ref %CompleteClass.4 = temporary_storage
+// CHECK:STDOUT:   %F.call: init %CompleteClass.4 = call %F.ref() to %.loc11_33
+// CHECK:STDOUT:   assign %v.var, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %CompleteClass.4;
+// 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: type = generic_class_type @Class [template]
 // CHECK:STDOUT:   %.1: type = tuple_type () [template]
-// CHECK:STDOUT:   %Class: %Class.type = struct_value () [template]
-// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+12> [symbolic]
+// CHECK:STDOUT:   %Class.1: %Class.type = struct_value () [template]
+// CHECK:STDOUT:   %Class.2: type = class_type @Class [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+15> [symbolic]
 // CHECK:STDOUT:   %.type: type = generic_class_type @.1 [template]
 // CHECK:STDOUT:   %.2: %.type = struct_value () [template]
 // CHECK:STDOUT:   %.3: type = class_type @.1 [template]
@@ -111,10 +491,14 @@ class Class(U:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .Class = %import_ref
+// CHECK:STDOUT:     .Class = %import_ref.1
+// CHECK:STDOUT:     .CompleteClass = %import_ref.2
+// CHECK:STDOUT:     .F = %import_ref.3
 // CHECK:STDOUT:     .Core = %Core
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref: %Class.type = import_ref ir0, inst+5, loaded [template = constants.%Class]
+// CHECK:STDOUT:   %import_ref.1: %Class.type = import_ref ir0, inst+5, loaded [template = constants.%Class.1]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir0, inst+12, unloaded
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir0, inst+48, unloaded
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
 // CHECK:STDOUT:   %.decl: %.type = class_decl @.1 [template = constants.%.2] {
 // CHECK:STDOUT:     %U.loc9_13.1: type = param U

+ 1 - 2
toolchain/check/testdata/class/generic/redeclare.carbon

@@ -94,7 +94,6 @@ class E(U:! type) {}
 // CHECK:STDOUT:   %.1: type = tuple_type () [template]
 // CHECK:STDOUT:   %Generic.1: %Generic.type = struct_value () [template]
 // CHECK:STDOUT:   %Generic.2: type = class_type @Generic [template]
-// CHECK:STDOUT:   %struct: type = struct_value () [template]
 // CHECK:STDOUT:   %.2: type = struct_type {} [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -108,7 +107,7 @@ class E(U:! type) {}
 // 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.%struct] {
+// CHECK:STDOUT:   %Generic.decl.loc6: %Generic.type = class_decl @Generic [template = constants.%Generic.1] {
 // 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:   }

+ 25 - 28
toolchain/check/testdata/impl/fail_extend_impl_forall.carbon

@@ -13,17 +13,9 @@ interface GenericInterface(T:! type) {
 }
 
 class C {
-  // CHECK:STDERR: fail_extend_impl_forall.carbon:[[@LINE+11]]:3: ERROR: Cannot `extend` a parameterized `impl`.
+  // CHECK:STDERR: fail_extend_impl_forall.carbon:[[@LINE+3]]:3: ERROR: Cannot `extend` a parameterized `impl`.
   // CHECK:STDERR:   extend impl forall [T:! type] as GenericInterface(T) {
   // CHECK:STDERR:   ^~~~~~
-  // CHECK:STDERR:
-  // CHECK:STDERR: fail_extend_impl_forall.carbon:[[@LINE+7]]:3: ERROR: Semantics TODO: `impl as non-interface`.
-  // CHECK:STDERR:   extend impl forall [T:! type] as GenericInterface(T) {
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR:
-  // CHECK:STDERR: fail_extend_impl_forall.carbon:[[@LINE+3]]:36: ERROR: Value of type `type` is not callable.
-  // CHECK:STDERR:   extend impl forall [T:! type] as GenericInterface(T) {
-  // CHECK:STDERR:                                    ^~~~~~~~~~~~~~~~~
   extend impl forall [T:! type] as GenericInterface(T) {
     fn F(x: T) {}
   }
@@ -33,17 +25,20 @@ class C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
-// CHECK:STDOUT:   %.1: type = interface_type @GenericInterface [template]
-// CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %GenericInterface.type: type = generic_interface_type @GenericInterface [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %GenericInterface: %GenericInterface.type = struct_value () [template]
+// CHECK:STDOUT:   %Self: %GenericInterface = bind_symbolic_name Self 1 [symbolic]
 // CHECK:STDOUT:   %F.type.1: type = fn_type @F.1 [template]
-// CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %F.1: %F.type.1 = struct_value () [template]
-// CHECK:STDOUT:   %.3: type = assoc_entity_type @GenericInterface, %F.type.1 [template]
-// CHECK:STDOUT:   %.4: %.3 = assoc_entity element0, @GenericInterface.%F.decl [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @GenericInterface, %F.type.1 [template]
+// CHECK:STDOUT:   %.3: %.2 = assoc_entity element0, @GenericInterface.%F.decl [template]
 // CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.4: type = interface_type @GenericInterface, (%T) [symbolic]
 // CHECK:STDOUT:   %F.type.2: type = fn_type @F.2 [template]
 // CHECK:STDOUT:   %F.2: %F.type.2 = struct_value () [template]
-// CHECK:STDOUT:   %.5: type = struct_type {} [template]
+// CHECK:STDOUT:   %.5: <witness> = interface_witness (%F.2) [template]
+// CHECK:STDOUT:   %.6: type = struct_type {} [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -53,7 +48,7 @@ class C {
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core: <namespace> = namespace [template] {}
-// CHECK:STDOUT:   %GenericInterface.decl: type = interface_decl @GenericInterface [template = constants.%.1] {
+// CHECK:STDOUT:   %GenericInterface.decl: %GenericInterface.type = interface_decl @GenericInterface [template = constants.%GenericInterface] {
 // CHECK:STDOUT:     %T.loc11_28.1: type = param T
 // CHECK:STDOUT:     %T.loc11_28.2: type = bind_symbolic_name T 0, %T.loc11_28.1 [symbolic = constants.%T]
 // CHECK:STDOUT:   }
@@ -61,13 +56,13 @@ class C {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @GenericInterface {
-// CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 1 [symbolic = constants.%Self]
+// CHECK:STDOUT:   %Self: %GenericInterface = bind_symbolic_name Self 1 [symbolic = constants.%Self]
 // CHECK:STDOUT:   %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {
 // CHECK:STDOUT:     %T.ref: type = name_ref T, file.%T.loc11_28.2 [symbolic = constants.%T]
 // CHECK:STDOUT:     %x.loc12_8.1: %T = param x
 // CHECK:STDOUT:     %x.loc12_8.2: %T = bind_name x, %x.loc12_8.1
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc12: %.3 = assoc_entity element0, %F.decl [template = constants.%.4]
+// CHECK:STDOUT:   %.loc12: %.2 = assoc_entity element0, %F.decl [template = constants.%.3]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = %Self
@@ -75,24 +70,26 @@ class C {
 // CHECK:STDOUT:   witness = (%F.decl)
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl @impl: %C as <error> {
+// CHECK:STDOUT: impl @impl: %C as %.4 {
 // CHECK:STDOUT:   %F.decl: %F.type.2 = fn_decl @F.2 [template = constants.%F.2] {
-// CHECK:STDOUT:     %T.ref: type = name_ref T, @C.%T.loc27_23.2 [symbolic = constants.%T]
-// CHECK:STDOUT:     %x.loc28_10.1: %T = param x
-// CHECK:STDOUT:     %x.loc28_10.2: %T = bind_name x, %x.loc28_10.1
+// CHECK:STDOUT:     %T.ref: type = name_ref T, @C.%T.loc19_23.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %x.loc20_10.1: %T = param x
+// CHECK:STDOUT:     %x.loc20_10.2: %T = bind_name x, %x.loc20_10.1
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%F.decl) [template = constants.%.5]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .F = %F.decl
-// CHECK:STDOUT:   witness = <error>
+// CHECK:STDOUT:   witness = %.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT:   impl_decl @impl {
-// CHECK:STDOUT:     %T.loc27_23.1: type = param T
-// CHECK:STDOUT:     %T.loc27_23.2: type = bind_symbolic_name T 0, %T.loc27_23.1 [symbolic = constants.%T]
-// CHECK:STDOUT:     %GenericInterface.ref: type = name_ref GenericInterface, file.%GenericInterface.decl [template = constants.%.1]
-// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc27_23.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %T.loc19_23.1: type = param T
+// CHECK:STDOUT:     %T.loc19_23.2: type = bind_symbolic_name T 0, %T.loc19_23.1 [symbolic = constants.%T]
+// CHECK:STDOUT:     %GenericInterface.ref: %GenericInterface.type = name_ref GenericInterface, file.%GenericInterface.decl [template = constants.%GenericInterface]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc19_23.2 [symbolic = constants.%T]
+// CHECK:STDOUT:     %.loc19: type = interface_type @GenericInterface, (%T.ref) [symbolic = constants.%.4]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -102,7 +99,7 @@ class C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F.1(@GenericInterface.%x.loc12_8.2: %T);
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F.2(@impl.%x.loc28_10.2: %T) {
+// CHECK:STDOUT: fn @F.2(@impl.%x.loc20_10.2: %T) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 22 - 18
toolchain/check/testdata/interface/no_prelude/fail_generic_redeclaration.carbon

@@ -42,16 +42,20 @@ interface DifferentParams(T:! ()) {}
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %.1: type = interface_type @NotGeneric [template]
 // CHECK:STDOUT:   %T.1: type = bind_symbolic_name T 0 [symbolic]
-// CHECK:STDOUT:   %.2: type = interface_type @.1 [template]
-// CHECK:STDOUT:   %Self.1: %.2 = bind_symbolic_name Self 1 [symbolic]
-// CHECK:STDOUT:   %.3: type = interface_type @Generic [template]
+// CHECK:STDOUT:   %.type.1: type = generic_interface_type @.1 [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %.3: %.type.1 = struct_value () [template]
+// CHECK:STDOUT:   %Self.1: %.3 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %Generic.type: type = generic_interface_type @Generic [template]
+// CHECK:STDOUT:   %Generic: %Generic.type = struct_value () [template]
 // CHECK:STDOUT:   %.4: type = interface_type @.2 [template]
 // CHECK:STDOUT:   %Self.2: %.4 = bind_symbolic_name Self 0 [symbolic]
-// CHECK:STDOUT:   %.5: type = interface_type @DifferentParams [template]
-// CHECK:STDOUT:   %.6: type = tuple_type () [template]
-// CHECK:STDOUT:   %T.2: %.6 = bind_symbolic_name T 0 [symbolic]
-// CHECK:STDOUT:   %.7: type = interface_type @.3 [template]
-// CHECK:STDOUT:   %Self.3: %.7 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %DifferentParams.type: type = generic_interface_type @DifferentParams [template]
+// CHECK:STDOUT:   %DifferentParams: %DifferentParams.type = struct_value () [template]
+// CHECK:STDOUT:   %T.2: %.2 = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %.type.2: type = generic_interface_type @.3 [template]
+// CHECK:STDOUT:   %.5: %.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %Self.3: %.5 = bind_symbolic_name Self 1 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -61,31 +65,31 @@ interface DifferentParams(T:! ()) {}
 // CHECK:STDOUT:     .DifferentParams = %DifferentParams.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %NotGeneric.decl: type = interface_decl @NotGeneric [template = constants.%.1] {}
-// CHECK:STDOUT:   %.decl.loc19: type = interface_decl @.1 [template = constants.%.2] {
+// CHECK:STDOUT:   %.decl.loc19: %.type.1 = interface_decl @.1 [template = constants.%.3] {
 // CHECK:STDOUT:     %T.loc19_22.1: type = param T
 // CHECK:STDOUT:     %T.loc19_22.2: type = bind_symbolic_name T 0, %T.loc19_22.1 [symbolic = constants.%T.1]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Generic.decl: type = interface_decl @Generic [template = constants.%.3] {
+// CHECK:STDOUT:   %Generic.decl: %Generic.type = interface_decl @Generic [template = constants.%Generic] {
 // CHECK:STDOUT:     %T.loc21_19.1: type = param T
 // CHECK:STDOUT:     %T.loc21_19.2: type = bind_symbolic_name T 0, %T.loc21_19.1 [symbolic = constants.%T.1]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.decl.loc29: type = interface_decl @.2 [template = constants.%.4] {}
-// CHECK:STDOUT:   %DifferentParams.decl: type = interface_decl @DifferentParams [template = constants.%.5] {
+// CHECK:STDOUT:   %DifferentParams.decl: %DifferentParams.type = interface_decl @DifferentParams [template = constants.%DifferentParams] {
 // CHECK:STDOUT:     %T.loc31_27.1: type = param T
 // CHECK:STDOUT:     %T.loc31_27.2: type = bind_symbolic_name T 0, %T.loc31_27.1 [symbolic = constants.%T.1]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.decl.loc38: type = interface_decl @.3 [template = constants.%.7] {
-// CHECK:STDOUT:     %.loc38_32.1: %.6 = tuple_literal ()
-// CHECK:STDOUT:     %.loc38_32.2: type = converted %.loc38_32.1, constants.%.6 [template = constants.%.6]
-// CHECK:STDOUT:     %T.loc38_27.1: %.6 = param T
-// CHECK:STDOUT:     %T.loc38_27.2: %.6 = bind_symbolic_name T 0, %T.loc38_27.1 [symbolic = constants.%T.2]
+// CHECK:STDOUT:   %.decl.loc38: %.type.2 = interface_decl @.3 [template = constants.%.5] {
+// CHECK:STDOUT:     %.loc38_32.1: %.2 = tuple_literal ()
+// CHECK:STDOUT:     %.loc38_32.2: type = converted %.loc38_32.1, constants.%.2 [template = constants.%.2]
+// CHECK:STDOUT:     %T.loc38_27.1: %.2 = param T
+// CHECK:STDOUT:     %T.loc38_27.2: %.2 = bind_symbolic_name T 0, %T.loc38_27.1 [symbolic = constants.%T.2]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @NotGeneric;
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @.1 {
-// CHECK:STDOUT:   %Self: %.2 = bind_symbolic_name Self 1 [symbolic = constants.%Self.1]
+// CHECK:STDOUT:   %Self: %.3 = bind_symbolic_name Self 1 [symbolic = constants.%Self.1]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = %Self
@@ -105,7 +109,7 @@ interface DifferentParams(T:! ()) {}
 // CHECK:STDOUT: interface @DifferentParams;
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @.3 {
-// CHECK:STDOUT:   %Self: %.7 = bind_symbolic_name Self 1 [symbolic = constants.%Self.3]
+// CHECK:STDOUT:   %Self: %.5 = bind_symbolic_name Self 1 [symbolic = constants.%Self.3]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = %Self

+ 46 - 36
toolchain/check/testdata/interface/no_prelude/fail_todo_generic_default_fn.carbon

@@ -9,82 +9,92 @@
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interface/no_prelude/fail_todo_generic_default_fn.carbon
 
 interface I(T:! type) {
+  // TODO: Declare `Self` as having type `I(T)` instead of `I`.
   // TODO: Use `default` here.
+  // CHECK:STDERR: fail_todo_generic_default_fn.carbon:[[@LINE+8]]:14: ERROR: Cannot implicitly convert from `<cannot stringify inst+7>` to `type`.
+  // CHECK:STDERR:   fn F[self: Self]() -> Self;
+  // CHECK:STDERR:              ^~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_todo_generic_default_fn.carbon:[[@LINE+4]]:25: ERROR: Cannot implicitly convert from `<cannot stringify inst+7>` to `type`.
+  // CHECK:STDERR:   fn F[self: Self]() -> Self;
+  // CHECK:STDERR:                         ^~~~
+  // CHECK:STDERR:
   fn F[self: Self]() -> Self;
 }
 
-// CHECK:STDERR: fail_todo_generic_default_fn.carbon:[[@LINE+6]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fail_todo_generic_default_fn.carbon:[[@LINE+14]]:1: ERROR: Duplicate name being declared in the same scope.
 // CHECK:STDERR: fn I(T:! type).F[self: Self]() -> Self { return self; }
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR: fail_todo_generic_default_fn.carbon:[[@LINE-6]]:3: Name is previously declared here.
 // CHECK:STDERR:   fn F[self: Self]() -> Self;
 // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_todo_generic_default_fn.carbon:[[@LINE+7]]:24: ERROR: Cannot implicitly convert from `<cannot stringify inst+7>` to `type`.
+// CHECK:STDERR: fn I(T:! type).F[self: Self]() -> Self { return self; }
+// CHECK:STDERR:                        ^~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_todo_generic_default_fn.carbon:[[@LINE+3]]:35: ERROR: Cannot implicitly convert from `<cannot stringify inst+7>` to `type`.
+// CHECK:STDERR: fn I(T:! type).F[self: Self]() -> Self { return self; }
+// CHECK:STDERR:                                   ^~~~
 fn I(T:! type).F[self: Self]() -> Self { return self; }
 
 // CHECK:STDOUT: --- fail_todo_generic_default_fn.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
-// CHECK:STDOUT:   %.1: type = interface_type @I [template]
-// CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %I.type: type = generic_interface_type @I [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %I: %I.type = struct_value () [template]
+// CHECK:STDOUT:   %Self: %I = bind_symbolic_name Self 1 [symbolic]
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
-// CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
-// CHECK:STDOUT:   %.3: type = assoc_entity_type @I, %F.type [template]
-// CHECK:STDOUT:   %.4: %.3 = assoc_entity element0, @I.%F.decl [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @I, %F.type [template]
+// CHECK:STDOUT:   %.3: %.2 = assoc_entity element0, @I.%F.decl [template]
 // CHECK:STDOUT:   %.type: type = fn_type @.1 [template]
-// CHECK:STDOUT:   %.5: %.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: %.type = struct_value () [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
 // CHECK:STDOUT:     .I = %I.decl
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %I.decl: type = interface_decl @I [template = constants.%.1] {
+// CHECK:STDOUT:   %I.decl: %I.type = interface_decl @I [template = constants.%I] {
 // 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:   }
-// CHECK:STDOUT:   %.decl: %.type = fn_decl @.1 [template = constants.%.5] {
-// CHECK:STDOUT:     %T.loc22_6.1: type = param T
-// CHECK:STDOUT:     %T.loc22_6.2: type = bind_symbolic_name T 0, %T.loc22_6.1 [symbolic = constants.%T]
-// CHECK:STDOUT:     %Self.ref.loc22_24: %.1 = name_ref Self, @I.%Self [symbolic = constants.%Self]
-// CHECK:STDOUT:     %.loc22_24.1: type = facet_type_access %Self.ref.loc22_24 [symbolic = constants.%Self]
-// CHECK:STDOUT:     %.loc22_24.2: type = converted %Self.ref.loc22_24, %.loc22_24.1 [symbolic = constants.%Self]
-// CHECK:STDOUT:     %self.loc22_18.1: %Self = param self
-// CHECK:STDOUT:     @.1.%self: %Self = bind_name self, %self.loc22_18.1
-// CHECK:STDOUT:     %Self.ref.loc22_35: %.1 = name_ref Self, @I.%Self [symbolic = constants.%Self]
-// CHECK:STDOUT:     %.loc22_35.1: type = facet_type_access %Self.ref.loc22_35 [symbolic = constants.%Self]
-// CHECK:STDOUT:     %.loc22_35.2: type = converted %Self.ref.loc22_35, %.loc22_35.1 [symbolic = constants.%Self]
-// CHECK:STDOUT:     @.1.%return: ref %Self = var <return slot>
+// CHECK:STDOUT:   %.decl: %.type = fn_decl @.1 [template = constants.%.4] {
+// CHECK:STDOUT:     %T.loc39_6.1: type = param T
+// CHECK:STDOUT:     %T.loc39_6.2: type = bind_symbolic_name T 0, %T.loc39_6.1 [symbolic = constants.%T]
+// CHECK:STDOUT:     %Self.ref.loc39_24: %I = name_ref Self, @I.%Self [symbolic = constants.%Self]
+// CHECK:STDOUT:     %self.loc39_18.1: <error> = param self
+// CHECK:STDOUT:     @.1.%self: <error> = bind_name self, %self.loc39_18.1
+// CHECK:STDOUT:     %Self.ref.loc39_35: %I = name_ref Self, @I.%Self [symbolic = constants.%Self]
+// CHECK:STDOUT:     @.1.%return: ref <error> = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @I {
-// CHECK:STDOUT:   %Self: %.1 = bind_symbolic_name Self 1 [symbolic = constants.%Self]
+// CHECK:STDOUT:   %Self: %I = bind_symbolic_name Self 1 [symbolic = constants.%Self]
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
-// CHECK:STDOUT:     %Self.ref.loc13_14: %.1 = name_ref Self, %Self [symbolic = constants.%Self]
-// CHECK:STDOUT:     %.loc13_14.1: type = facet_type_access %Self.ref.loc13_14 [symbolic = constants.%Self]
-// CHECK:STDOUT:     %.loc13_14.2: type = converted %Self.ref.loc13_14, %.loc13_14.1 [symbolic = constants.%Self]
-// CHECK:STDOUT:     %self.loc13_8.1: %Self = param self
-// CHECK:STDOUT:     %self.loc13_8.2: %Self = bind_name self, %self.loc13_8.1
-// CHECK:STDOUT:     %Self.ref.loc13_25: %.1 = name_ref Self, %Self [symbolic = constants.%Self]
-// CHECK:STDOUT:     %.loc13_25.1: type = facet_type_access %Self.ref.loc13_25 [symbolic = constants.%Self]
-// CHECK:STDOUT:     %.loc13_25.2: type = converted %Self.ref.loc13_25, %.loc13_25.1 [symbolic = constants.%Self]
-// CHECK:STDOUT:     %return.var: ref %Self = var <return slot>
+// CHECK:STDOUT:     %Self.ref.loc22_14: %I = name_ref Self, %Self [symbolic = constants.%Self]
+// CHECK:STDOUT:     %self.loc22_8.1: <error> = param self
+// CHECK:STDOUT:     %self.loc22_8.2: <error> = bind_name self, %self.loc22_8.1
+// CHECK:STDOUT:     %Self.ref.loc22_25: %I = name_ref Self, %Self [symbolic = constants.%Self]
+// CHECK:STDOUT:     %return.var: ref <error> = var <return slot>
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc13_29: %.3 = assoc_entity element0, %F.decl [template = constants.%.4]
+// CHECK:STDOUT:   %.loc22: %.2 = assoc_entity element0, %F.decl [template = constants.%.3]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = %Self
-// CHECK:STDOUT:   .F = %.loc13_29
+// CHECK:STDOUT:   .F = %.loc22
 // CHECK:STDOUT:   witness = (%F.decl)
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @F[@I.%self.loc13_8.2: %Self]() -> %Self;
+// CHECK:STDOUT: fn @F[@I.%self.loc22_8.2: <error>]() -> <error>;
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @.1[%self: %Self]() -> %Self {
+// CHECK:STDOUT: fn @.1[%self: <error>]() -> <error> {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %self.ref: %Self = name_ref self, %self
-// CHECK:STDOUT:   return %self.ref
+// CHECK:STDOUT:   %self.ref: <error> = name_ref self, %self
+// CHECK:STDOUT:   return <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 279 - 12
toolchain/check/testdata/interface/no_prelude/generic.carbon

@@ -8,28 +8,295 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interface/no_prelude/generic.carbon
 
-interface I[T:! type](N:! T);
+// --- generic.carbon
+
+library "generic";
+
+interface Simple(T:! type) {}
+
+class X {}
+
+interface WithAssocFn(T:! type) {
+  // TODO: Take `Self`, return `T`, once that works.
+  fn F() -> X;
+}
+
+class C {
+  impl as Simple(C) {}
+  impl as WithAssocFn(C) {
+    fn F() -> X {
+      return {};
+    }
+  }
+}
+
+interface WithImplicitArgs[T:! type](N:! T);
+
+fn Receive(T:! Simple(C));
+fn Pass(T:! Simple(C)) {
+  Receive(T);
+}
+
+// --- fail_mismatched_args.carbon
+
+library "fail_mismatched_args";
+
+interface Generic(T:! type) {}
+
+class A {}
+class B {}
+
+fn F(T:! Generic(A));
+fn G(T:! Generic(B)) {
+  // TODO: Include generic arguments in the type name.
+  // CHECK:STDERR: fail_mismatched_args.carbon:[[@LINE+6]]:3: ERROR: Cannot implicitly convert from `Generic` to `Generic`.
+  // CHECK:STDERR:   F(T);
+  // CHECK:STDERR:   ^~
+  // CHECK:STDERR: fail_mismatched_args.carbon:[[@LINE-6]]:1: Initializing parameter 1 of function declared here.
+  // CHECK:STDERR: fn F(T:! Generic(A));
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
+  F(T);
+}
 
 // CHECK:STDOUT: --- generic.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
-// CHECK:STDOUT:   %N: %T = bind_symbolic_name N 1 [symbolic]
-// CHECK:STDOUT:   %.1: type = interface_type @I [template]
+// CHECK:STDOUT:   %T.1: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Simple.type: type = generic_interface_type @Simple [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Simple: %Simple.type = struct_value () [template]
+// CHECK:STDOUT:   %Self.1: %Simple = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %X: type = class_type @X [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %WithAssocFn.type: type = generic_interface_type @WithAssocFn [template]
+// CHECK:STDOUT:   %WithAssocFn: %WithAssocFn.type = struct_value () [template]
+// CHECK:STDOUT:   %Self.2: %WithAssocFn = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %F.type.1: type = fn_type @F.1 [template]
+// CHECK:STDOUT:   %F.1: %F.type.1 = struct_value () [template]
+// CHECK:STDOUT:   %.3: type = assoc_entity_type @WithAssocFn, %F.type.1 [template]
+// CHECK:STDOUT:   %.4: %.3 = assoc_entity element0, @WithAssocFn.%F.decl [template]
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.5: type = interface_type @Simple, (%C) [template]
+// CHECK:STDOUT:   %.6: <witness> = interface_witness () [template]
+// CHECK:STDOUT:   %.7: type = interface_type @WithAssocFn, (%C) [template]
+// CHECK:STDOUT:   %F.type.2: type = fn_type @F.2 [template]
+// CHECK:STDOUT:   %F.2: %F.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.8: <witness> = interface_witness (%F.2) [template]
+// CHECK:STDOUT:   %.9: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %struct: %X = struct_value () [template]
+// CHECK:STDOUT:   %N: %T.1 = bind_symbolic_name N 1 [symbolic]
+// CHECK:STDOUT:   %WithImplicitArgs.type: type = generic_interface_type @WithImplicitArgs [template]
+// CHECK:STDOUT:   %WithImplicitArgs: %WithImplicitArgs.type = struct_value () [template]
+// CHECK:STDOUT:   %T.2: %.5 = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Receive.type: type = fn_type @Receive [template]
+// CHECK:STDOUT:   %Receive: %Receive.type = struct_value () [template]
+// CHECK:STDOUT:   %Pass.type: type = fn_type @Pass [template]
+// CHECK:STDOUT:   %Pass: %Pass.type = struct_value () [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [template] {
-// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:     .Simple = %Simple.decl
+// CHECK:STDOUT:     .X = %X.decl
+// CHECK:STDOUT:     .WithAssocFn = %WithAssocFn.decl
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .WithImplicitArgs = %WithImplicitArgs.decl
+// CHECK:STDOUT:     .Receive = %Receive.decl
+// CHECK:STDOUT:     .Pass = %Pass.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Simple.decl: %Simple.type = interface_decl @Simple [template = constants.%Simple] {
+// CHECK:STDOUT:     %T.loc4_18.1: type = param T
+// CHECK:STDOUT:     %T.loc4_18.2: type = bind_symbolic_name T 0, %T.loc4_18.1 [symbolic = constants.%T.1]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %I.decl: type = interface_decl @I [template = constants.%.1] {
-// 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.ref: type = name_ref T, %T.loc11_13.2 [symbolic = constants.%T]
-// CHECK:STDOUT:     %N.loc11_23.1: %T = param N
-// CHECK:STDOUT:     %N.loc11_23.2: %T = bind_symbolic_name N 1, %N.loc11_23.1 [symbolic = constants.%N]
+// CHECK:STDOUT:   %X.decl: type = class_decl @X [template = constants.%X] {}
+// CHECK:STDOUT:   %WithAssocFn.decl: %WithAssocFn.type = interface_decl @WithAssocFn [template = constants.%WithAssocFn] {
+// CHECK:STDOUT:     %T.loc8_23.1: type = param T
+// CHECK:STDOUT:     %T.loc8_23.2: type = bind_symbolic_name T 0, %T.loc8_23.1 [symbolic = constants.%T.1]
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %WithImplicitArgs.decl: %WithImplicitArgs.type = interface_decl @WithImplicitArgs [template = constants.%WithImplicitArgs] {
+// CHECK:STDOUT:     %T.loc22_28.1: type = param T
+// CHECK:STDOUT:     %T.loc22_28.2: type = bind_symbolic_name T 0, %T.loc22_28.1 [symbolic = constants.%T.1]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc22_28.2 [symbolic = constants.%T.1]
+// CHECK:STDOUT:     %N.loc22_38.1: %T.1 = param N
+// CHECK:STDOUT:     %N.loc22_38.2: %T.1 = bind_symbolic_name N 1, %N.loc22_38.1 [symbolic = constants.%N]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Receive.decl: %Receive.type = fn_decl @Receive [template = constants.%Receive] {
+// CHECK:STDOUT:     %Simple.ref.loc24: %Simple.type = name_ref Simple, %Simple.decl [template = constants.%Simple]
+// CHECK:STDOUT:     %C.ref.loc24: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc24: type = interface_type @Simple, (%C.ref.loc24) [template = constants.%.5]
+// CHECK:STDOUT:     %T.loc24_12.1: %.5 = param T
+// CHECK:STDOUT:     @Receive.%T: %.5 = bind_symbolic_name T 0, %T.loc24_12.1 [symbolic = constants.%T.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Pass.decl: %Pass.type = fn_decl @Pass [template = constants.%Pass] {
+// CHECK:STDOUT:     %Simple.ref.loc25: %Simple.type = name_ref Simple, %Simple.decl [template = constants.%Simple]
+// CHECK:STDOUT:     %C.ref.loc25: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc25: type = interface_type @Simple, (%C.ref.loc25) [template = constants.%.5]
+// CHECK:STDOUT:     %T.loc25_9.1: %.5 = param T
+// CHECK:STDOUT:     @Pass.%T: %.5 = bind_symbolic_name T 0, %T.loc25_9.1 [symbolic = constants.%T.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Simple {
+// CHECK:STDOUT:   %Self: %Simple = bind_symbolic_name Self 1 [symbolic = constants.%Self.1]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @WithAssocFn {
+// CHECK:STDOUT:   %Self: %WithAssocFn = bind_symbolic_name Self 1 [symbolic = constants.%Self.2]
+// CHECK:STDOUT:   %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {
+// CHECK:STDOUT:     %X.ref: type = name_ref X, file.%X.decl [template = constants.%X]
+// CHECK:STDOUT:     %return.var: ref %X = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc10: %.3 = assoc_entity element0, %F.decl [template = constants.%.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .F = %.loc10
+// CHECK:STDOUT:   witness = (%F.decl)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @WithImplicitArgs;
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.1: %C as %.5 {
+// CHECK:STDOUT:   %.1: <witness> = interface_witness () [template = constants.%.6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.2: %C as %.7 {
+// CHECK:STDOUT:   %F.decl: %F.type.2 = fn_decl @F.2 [template = constants.%F.2] {
+// CHECK:STDOUT:     %X.ref: type = name_ref X, file.%X.decl [template = constants.%X]
+// CHECK:STDOUT:     %return.var: ref %X = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%F.decl) [template = constants.%.8]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F.decl
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @X {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%X
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: interface @I;
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   impl_decl @impl.1 {
+// CHECK:STDOUT:     %Simple.ref: %Simple.type = name_ref Simple, file.%Simple.decl [template = constants.%Simple]
+// CHECK:STDOUT:     %C.ref.loc14: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc14: type = interface_type @Simple, (%C.ref.loc14) [template = constants.%.5]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.2 {
+// CHECK:STDOUT:     %WithAssocFn.ref: %WithAssocFn.type = name_ref WithAssocFn, file.%WithAssocFn.decl [template = constants.%WithAssocFn]
+// CHECK:STDOUT:     %C.ref.loc15: type = name_ref C, file.%C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc15: type = interface_type @WithAssocFn, (%C.ref.loc15) [template = constants.%.7]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1() -> %X;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2() -> @impl.2.%return.var: %X {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc17_15.1: %.2 = struct_literal ()
+// CHECK:STDOUT:   %.loc17_15.2: init %X = class_init (), @impl.2.%return.var [template = constants.%struct]
+// CHECK:STDOUT:   %.loc17_16: init %X = converted %.loc17_15.1, %.loc17_15.2 [template = constants.%struct]
+// CHECK:STDOUT:   return %.loc17_16 to @impl.2.%return.var
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Receive(%T: %.5);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Pass(%T: %.5) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Receive.ref: %Receive.type = name_ref Receive, file.%Receive.decl [template = constants.%Receive]
+// CHECK:STDOUT:   %T.ref: %.5 = name_ref T, %T [symbolic = constants.%T.2]
+// CHECK:STDOUT:   %Receive.call: init %.1 = call %Receive.ref(%T.ref)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_mismatched_args.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T.1: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %Generic.type: type = generic_interface_type @Generic [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Generic: %Generic.type = struct_value () [template]
+// CHECK:STDOUT:   %Self: %Generic = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %A: type = class_type @A [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT:   %B: type = class_type @B [template]
+// CHECK:STDOUT:   %.3: type = interface_type @Generic, (%A) [template]
+// CHECK:STDOUT:   %T.2: %.3 = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = interface_type @Generic, (%B) [template]
+// CHECK:STDOUT:   %T.3: %.4 = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [template]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Generic = %Generic.decl
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:     .B = %B.decl
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Generic.decl: %Generic.type = interface_decl @Generic [template = constants.%Generic] {
+// CHECK:STDOUT:     %T.loc4_19.1: type = param T
+// CHECK:STDOUT:     %T.loc4_19.2: type = bind_symbolic_name T 0, %T.loc4_19.1 [symbolic = constants.%T.1]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %A.decl: type = class_decl @A [template = constants.%A] {}
+// CHECK:STDOUT:   %B.decl: type = class_decl @B [template = constants.%B] {}
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
+// CHECK:STDOUT:     %Generic.ref.loc9: %Generic.type = name_ref Generic, %Generic.decl [template = constants.%Generic]
+// CHECK:STDOUT:     %A.ref: type = name_ref A, %A.decl [template = constants.%A]
+// CHECK:STDOUT:     %.loc9: type = interface_type @Generic, (%A.ref) [template = constants.%.3]
+// CHECK:STDOUT:     %T.loc9_6.1: %.3 = param T
+// CHECK:STDOUT:     @F.%T: %.3 = bind_symbolic_name T 0, %T.loc9_6.1 [symbolic = constants.%T.2]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {
+// CHECK:STDOUT:     %Generic.ref.loc10: %Generic.type = name_ref Generic, %Generic.decl [template = constants.%Generic]
+// CHECK:STDOUT:     %B.ref: type = name_ref B, %B.decl [template = constants.%B]
+// CHECK:STDOUT:     %.loc10: type = interface_type @Generic, (%B.ref) [template = constants.%.4]
+// CHECK:STDOUT:     %T.loc10_6.1: %.4 = param T
+// CHECK:STDOUT:     @G.%T: %.4 = bind_symbolic_name T 0, %T.loc10_6.1 [symbolic = constants.%T.3]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Generic {
+// CHECK:STDOUT:   %Self: %Generic = bind_symbolic_name Self 1 [symbolic = constants.%Self]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @A {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%A
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @B {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%B
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%T: %.3);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G(%T: %.4) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:   %T.ref: %.4 = name_ref T, %T [symbolic = constants.%T.3]
+// CHECK:STDOUT:   %F.call: init %.1 = call %F.ref(<invalid>) [template = <error>]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 134 - 0
toolchain/check/testdata/interface/no_prelude/generic_import.carbon

@@ -0,0 +1,134 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interface/no_prelude/generic_import.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interface/no_prelude/generic_import.carbon
+
+// --- a.carbon
+
+library "a";
+
+interface AddWith(T:! type) {
+  fn F();
+}
+
+// --- fail_todo_b.carbon
+
+library "b";
+// CHECK:STDERR: fail_todo_b.carbon:[[@LINE+4]]:1: In import.
+// CHECK:STDERR: import library "a";
+// CHECK:STDERR: ^~~~~~
+// CHECK:STDERR: a.carbon: ERROR: Semantics TODO: `TryResolveInst on StructValue`.
+import library "a";
+
+class C {}
+
+impl C as AddWith(C) {
+  fn F() {}
+}
+
+// CHECK:STDOUT: --- a.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0 [symbolic]
+// CHECK:STDOUT:   %AddWith.type: type = generic_interface_type @AddWith [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %AddWith: %AddWith.type = struct_value () [template]
+// CHECK:STDOUT:   %Self: %AddWith = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [template]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type @AddWith, %F.type [template]
+// CHECK:STDOUT:   %.3: %.2 = assoc_entity element0, @AddWith.%F.decl [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .AddWith = %AddWith.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %AddWith.decl: %AddWith.type = interface_decl @AddWith [template = constants.%AddWith] {
+// CHECK:STDOUT:     %T.loc4_19.1: type = param T
+// CHECK:STDOUT:     %T.loc4_19.2: type = bind_symbolic_name T 0, %T.loc4_19.1 [symbolic = constants.%T]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddWith {
+// CHECK:STDOUT:   %Self: %AddWith = bind_symbolic_name Self 1 [symbolic = constants.%Self]
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {}
+// CHECK:STDOUT:   %.loc5: %.2 = assoc_entity element0, %F.decl [template = constants.%.3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .F = %.loc5
+// CHECK:STDOUT:   witness = (%F.decl)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_b.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %AddWith.type: type = generic_interface_type @AddWith [template]
+// CHECK:STDOUT:   %.2: type = tuple_type () [template]
+// CHECK:STDOUT:   %AddWith: %AddWith.type = struct_value () [template]
+// CHECK:STDOUT:   %Self: <error> = bind_symbolic_name Self 1 [symbolic]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+14> [symbolic]
+// CHECK:STDOUT:   %.3: type = interface_type @AddWith, (%C) [template]
+// CHECK:STDOUT:   %F.type.1: type = fn_type @F.1 [template]
+// CHECK:STDOUT:   %F.1: %F.type.1 = struct_value () [template]
+// CHECK:STDOUT:   %F.type.2: type = fn_type @F.2 [template]
+// CHECK:STDOUT:   %F.2: %F.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.4: <witness> = interface_witness (%F.1) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .AddWith = %import_ref.1
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: %AddWith.type = import_ref ir1, inst+4, loaded [template = constants.%AddWith]
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir1, inst+8, unloaded
+// CHECK:STDOUT:   %import_ref.3 = import_ref ir1, inst+14, unloaded
+// CHECK:STDOUT:   %import_ref.4: %F.type.2 = import_ref ir1, inst+10, loaded [template = constants.%F.2]
+// CHECK:STDOUT:   impl_decl @impl {
+// CHECK:STDOUT:     %C.ref.loc11_6: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %AddWith.ref: %AddWith.type = name_ref AddWith, %import_ref.1 [template = constants.%AddWith]
+// CHECK:STDOUT:     %C.ref.loc11_19: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:     %.loc11: type = interface_type @AddWith, (%C.ref.loc11_19) [template = constants.%.3]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @AddWith {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT:   .F = file.%import_ref.3
+// CHECK:STDOUT:   witness = (file.%import_ref.4)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: %C as %.3 {
+// CHECK:STDOUT:   %F.decl: %F.type.1 = fn_decl @F.1 [template = constants.%F.1] {}
+// CHECK:STDOUT:   %.1: <witness> = interface_witness (%F.decl) [template = constants.%.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .F = %F.decl
+// CHECK:STDOUT:   witness = %.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F.2();
+// CHECK:STDOUT:

+ 9 - 7
toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon

@@ -65,10 +65,12 @@ export C2(T:! type);
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %C1.type: type = generic_class_type @C1 [template]
 // CHECK:STDOUT:   %.1: type = tuple_type () [template]
-// CHECK:STDOUT:   %C1: %C1.type = struct_value () [template]
-// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+17> [symbolic]
+// CHECK:STDOUT:   %C1.1: %C1.type = struct_value () [template]
+// CHECK:STDOUT:   %C1.2: type = class_type @C1 [template]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T 0, <unexpected instref inst+19> [symbolic]
 // CHECK:STDOUT:   %C2.type: type = generic_class_type @C2 [template]
-// CHECK:STDOUT:   %C2: %C2.type = struct_value () [template]
+// CHECK:STDOUT:   %C2.1: %C2.type = struct_value () [template]
+// CHECK:STDOUT:   %C2.2: type = class_type @C2 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -76,12 +78,12 @@ export C2(T:! type);
 // CHECK:STDOUT:     .C1 = %C1
 // CHECK:STDOUT:     .C2 = %C2
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %import_ref.1: %C1.type = import_ref ir1, inst+4, loaded [template = constants.%C1]
-// CHECK:STDOUT:   %import_ref.2: %C2.type = import_ref ir1, inst+11, loaded [template = constants.%C2]
-// CHECK:STDOUT:   %C1: %C1.type = export C1, %import_ref.1 [template = constants.%C1]
+// CHECK:STDOUT:   %import_ref.1: %C1.type = import_ref ir1, inst+4, loaded [template = constants.%C1.1]
+// CHECK:STDOUT:   %import_ref.2: %C2.type = import_ref ir1, inst+11, loaded [template = constants.%C2.1]
+// CHECK:STDOUT:   %C1: %C1.type = export C1, %import_ref.1 [template = constants.%C1.1]
 // CHECK:STDOUT:   %T.loc11_11.1: type = param T
 // CHECK:STDOUT:   %T.loc11_11.2: type = bind_symbolic_name T 0, %T.loc11_11.1 [symbolic = constants.%T]
-// CHECK:STDOUT:   %C2: %C2.type = export C2, %import_ref.2 [template = constants.%C2]
+// CHECK:STDOUT:   %C2: %C2.type = export C2, %import_ref.2 [template = constants.%C2.1]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C1;

+ 1 - 0
toolchain/lower/file_context.cpp

@@ -387,6 +387,7 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
     case SemIR::InterfaceType::Kind:
     case SemIR::FunctionType::Kind:
     case SemIR::GenericClassType::Kind:
+    case SemIR::GenericInterfaceType::Kind:
     case SemIR::UnboundElementType::Kind: {
       // Return an empty struct as a placeholder.
       // TODO: Should we model an interface as a witness table, or an associated

+ 9 - 0
toolchain/sem_ir/file.cpp

@@ -180,6 +180,7 @@ static auto GetTypePrecedence(InstKind kind) -> int {
     case FloatType::Kind:
     case FunctionType::Kind:
     case GenericClassType::Kind:
+    case GenericInterfaceType::Kind:
     case InterfaceType::Kind:
     case InterfaceWitnessAccess::Kind:
     case IntType::Kind:
@@ -327,6 +328,13 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir,
         out << "<type of " << sem_ir.names().GetFormatted(class_name_id) << ">";
         break;
       }
+      case CARBON_KIND(GenericInterfaceType inst): {
+        auto interface_name_id =
+            sem_ir.interfaces().Get(inst.interface_id).name_id;
+        out << "<type of " << sem_ir.names().GetFormatted(interface_name_id)
+            << ">";
+        break;
+      }
       case CARBON_KIND(InterfaceType inst): {
         auto interface_name_id =
             sem_ir.interfaces().Get(inst.interface_id).name_id;
@@ -567,6 +575,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
       case FloatType::Kind:
       case FunctionType::Kind:
       case GenericClassType::Kind:
+      case GenericInterfaceType::Kind:
       case InterfaceDecl::Kind:
       case InterfaceType::Kind:
       case InterfaceWitness::Kind:

+ 8 - 0
toolchain/sem_ir/formatter.cpp

@@ -636,6 +636,14 @@ class Formatter {
     FormatTrailingBlock(inst.decl_block_id);
   }
 
+  auto FormatInstructionRHS(InterfaceType inst) -> void {
+    if (inst.args_id.is_valid()) {
+      FormatArgs(inst.interface_id, inst.args_id);
+    } else {
+      FormatArgs(inst.interface_id);
+    }
+  }
+
   auto FormatInstructionRHS(IntLiteral inst) -> void {
     out_ << " ";
     sem_ir_.ints()

+ 2 - 0
toolchain/sem_ir/inst_kind.def

@@ -113,6 +113,8 @@ CARBON_SEM_IR_INST_KIND_IMPL(FunctionDecl, TYPE_NEVER, CONSTANT_NEVER)
 CARBON_SEM_IR_INST_KIND_IMPL(FunctionType, TYPE_ALWAYS, CONSTANT_CONDITIONAL)
 CARBON_SEM_IR_INST_KIND_IMPL(GenericClassType, TYPE_ALWAYS,
                              CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(GenericInterfaceType, TYPE_ALWAYS,
+                             CONSTANT_CONDITIONAL)
 CARBON_SEM_IR_INST_KIND_IMPL(ImplDecl, TYPE_NEVER, CONSTANT_NEVER)
 CARBON_SEM_IR_INST_KIND_IMPL(ImportRefUnloaded, TYPE_NEVER, CONSTANT_NEVER)
 CARBON_SEM_IR_INST_KIND_IMPL(ImportRefLoaded, TYPE_NEVER, CONSTANT_NEVER)

+ 11 - 0
toolchain/sem_ir/inst_namer.cpp

@@ -441,6 +441,11 @@ auto InstNamer::CollectNamesInBlock(ScopeId scope_id,
         add_inst_name_id(sem_ir_.classes().Get(inst.class_id).name_id, ".type");
         continue;
       }
+      case CARBON_KIND(GenericInterfaceType inst): {
+        add_inst_name_id(sem_ir_.interfaces().Get(inst.interface_id).name_id,
+                         ".type");
+        continue;
+      }
       case CARBON_KIND(ImplDecl inst): {
         CollectNamesInBlock(scope_id, inst.decl_block_id);
         break;
@@ -489,6 +494,12 @@ auto InstNamer::CollectNamesInBlock(ScopeId scope_id,
                            inst.type_id)) {
           add_inst_name_id(
               sem_ir_.classes().Get(generic_class_ty->class_id).name_id);
+        } else if (auto generic_interface_ty =
+                       sem_ir_.types().TryGetAs<GenericInterfaceType>(
+                           inst.type_id)) {
+          add_inst_name_id(sem_ir_.interfaces()
+                               .Get(generic_interface_ty->interface_id)
+                               .name_id);
         } else {
           add_inst_name("struct");
         }

+ 5 - 0
toolchain/sem_ir/interface.h

@@ -25,6 +25,11 @@ struct Interface : public Printable<Interface> {
     return definition_id.is_valid() && !is_defined();
   }
 
+  // Determines whether this is a generic interface.
+  auto is_generic() const -> bool {
+    return implicit_param_refs_id.is_valid() || param_refs_id.is_valid();
+  }
+
   // The following members always have values, and do not change throughout the
   // lifetime of the interface.
 

+ 13 - 2
toolchain/sem_ir/typed_insts.h

@@ -525,6 +525,18 @@ struct GenericClassType {
   ClassId class_id;
 };
 
+// The type of the name of a generic interface. The corresponding value is an
+// empty `StructValue`.
+struct GenericInterfaceType {
+  // This is only ever created as a constant, so doesn't have a location.
+  static constexpr auto Kind =
+      InstKind::GenericInterfaceType.Define<Parse::InvalidNodeId>(
+          "generic_interface_type");
+
+  TypeId type_id;
+  InterfaceId interface_id;
+};
+
 struct ImplDecl {
   static constexpr auto Kind =
       InstKind::ImplDecl.Define<Parse::AnyImplDeclId>("impl_decl");
@@ -602,8 +614,7 @@ struct InterfaceType {
 
   TypeId type_id;
   InterfaceId interface_id;
-  // TODO: Once we support generic interfaces, include the interface's arguments
-  // here.
+  InstBlockId args_id = InstBlockId::Invalid;
 };
 
 // A witness that a type implements an interface.