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

Track and resolve the specific callee in a call to a generic function (#4395)

Add a new `specific_function` instruction that represents a generic
function plus its deduced argument list as a callee in a function call.
The new instruction can only appear as the immediate operand of a call
instruction, so we give it a builtin placeholder type.

At the end of each file, require definitions for all specific functions
used in that file. Resolve the generic with the argument list to produce
those specific function definitions as needed, and diagnose if the
generic doesn't have a definition available.

A few tests are updated in cases where they declared and used generic
functions but didn't previously provide a function definition.
Richard Smith 1 год назад
Родитель
Сommit
1a1bfd2eb2
35 измененных файлов с 1591 добавлено и 358 удалено
  1. 15 3
      toolchain/check/call.cpp
  2. 31 7
      toolchain/check/check.cpp
  3. 1 0
      toolchain/check/context.cpp
  4. 7 2
      toolchain/check/eval.cpp
  5. 2 0
      toolchain/check/testdata/basics/builtin_insts.carbon
  6. 8 4
      toolchain/check/testdata/class/generic/import.carbon
  7. 39 20
      toolchain/check/testdata/class/generic/member_access.carbon
  8. 132 28
      toolchain/check/testdata/class/generic/method_deduce.carbon
  9. 53 12
      toolchain/check/testdata/class/generic/self.carbon
  10. 24 16
      toolchain/check/testdata/deduce/array.carbon
  11. 123 20
      toolchain/check/testdata/deduce/generic_type.carbon
  12. 5 1
      toolchain/check/testdata/deduce/int_float.carbon
  13. 47 15
      toolchain/check/testdata/deduce/tuple.carbon
  14. 162 61
      toolchain/check/testdata/deduce/type_operator.carbon
  15. 141 62
      toolchain/check/testdata/function/generic/deduce.carbon
  16. 89 45
      toolchain/check/testdata/function/generic/no_prelude/call.carbon
  17. 12 1
      toolchain/check/testdata/function/generic/redeclare.carbon
  18. 127 0
      toolchain/check/testdata/function/generic/resolve_used.carbon
  19. 77 24
      toolchain/check/testdata/function/generic/return_slot.carbon
  20. 332 0
      toolchain/check/testdata/function/generic/undefined.carbon
  21. 25 10
      toolchain/check/testdata/impl/lookup/generic.carbon
  22. 10 2
      toolchain/check/testdata/impl/lookup/no_prelude/impl_forall.carbon
  23. 27 11
      toolchain/check/testdata/interface/no_prelude/assoc_const_in_generic.carbon
  24. 17 3
      toolchain/check/testdata/interface/no_prelude/generic.carbon
  25. 37 7
      toolchain/check/testdata/operators/overloaded/implicit_as.carbon
  26. 2 0
      toolchain/diagnostics/diagnostic_kind.def
  27. 5 0
      toolchain/lower/constant.cpp
  28. 1 0
      toolchain/lower/file_context.cpp
  29. 3 0
      toolchain/sem_ir/builtin_inst_kind.def
  30. 1 0
      toolchain/sem_ir/file.cpp
  31. 9 2
      toolchain/sem_ir/function.cpp
  32. 3 1
      toolchain/sem_ir/function.h
  33. 2 1
      toolchain/sem_ir/inst_kind.def
  34. 1 0
      toolchain/sem_ir/stringify_type.cpp
  35. 21 0
      toolchain/sem_ir/typed_insts.h

+ 15 - 3
toolchain/check/call.cpp

@@ -9,6 +9,8 @@
 #include "toolchain/check/convert.h"
 #include "toolchain/check/deduce.h"
 #include "toolchain/check/function.h"
+#include "toolchain/sem_ir/builtin_function_kind.h"
+#include "toolchain/sem_ir/builtin_inst_kind.h"
 #include "toolchain/sem_ir/entity_with_params_base.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
@@ -24,8 +26,8 @@ namespace Carbon::Check {
 // `self_id` and `arg_ids` are the self argument and explicit arguments in the
 // call.
 //
-// Returns a SpecificId for the specific callee, or `nullopt` if an error has
-// been diagnosed.
+// Returns a `SpecificId` for the specific callee, `SpecificId::Invalid` if the
+// callee is not generic, or `nullopt` if an error has been diagnosed.
 static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
                                 const SemIR::EntityWithParamsBase& entity,
                                 llvm::StringLiteral entity_kind_for_diagnostic,
@@ -141,10 +143,20 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
   // for the call.
   auto callee_specific_id = ResolveCalleeInCall(
       context, loc_id, callable, "function", callable.generic_id,
-      callee_function.specific_id, callee_function.self_id, arg_ids);
+      callee_function.enclosing_specific_id, callee_function.self_id, arg_ids);
   if (!callee_specific_id) {
     return SemIR::InstId::BuiltinError;
   }
+  if (callee_specific_id->is_valid()) {
+    callee_id =
+        context.AddInst(context.insts().GetLocId(callee_id),
+                        SemIR::SpecificFunction{
+                            .type_id = context.GetBuiltinType(
+                                SemIR::BuiltinInstKind::SpecificFunctionType),
+                            .callee_id = callee_id,
+                            .specific_id = *callee_specific_id});
+    context.definitions_required().push_back(callee_id);
+  }
 
   // If there is a return slot, build storage for the result.
   SemIR::InstId return_storage_id = SemIR::InstId::Invalid;

+ 31 - 7
toolchain/check/check.cpp

@@ -16,6 +16,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/diagnostic_helpers.h"
 #include "toolchain/check/function.h"
+#include "toolchain/check/generic.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/import.h"
 #include "toolchain/check/import_ref.h"
@@ -783,14 +784,18 @@ auto NodeIdTraversal::Next() -> std::optional<Parse::NodeId> {
   }
 }
 
-// Emits a diagnostic for each declaration in context.definitions_required()
-// that doesn't have a definition.
-static auto DiagnoseMissingDefinitions(Context& context,
-                                       Context::DiagnosticEmitter& emitter)
+// Checks that each required definition is available. If the definition can be
+// generated by resolving a specific, does so, otherwise emits a diagnostic for
+// each declaration in context.definitions_required() that doesn't have a
+// definition.
+static auto CheckRequiredDefinitions(Context& context,
+                                     Context::DiagnosticEmitter& emitter)
     -> void {
   CARBON_DIAGNOSTIC(MissingDefinitionInImpl, Error,
                     "no definition found for declaration in impl file");
-  for (SemIR::InstId decl_inst_id : context.definitions_required()) {
+  // Note that more required definitions can be added during this loop.
+  for (size_t i = 0; i != context.definitions_required().size(); ++i) {
+    SemIR::InstId decl_inst_id = context.definitions_required()[i];
     SemIR::Inst decl_inst = context.insts().Get(decl_inst_id);
     CARBON_KIND_SWITCH(context.insts().Get(decl_inst_id)) {
       case CARBON_KIND(SemIR::ClassDecl class_decl): {
@@ -817,6 +822,25 @@ static auto DiagnoseMissingDefinitions(Context& context,
         // triggering https://github.com/carbon-language/carbon-lang/issues/4071
         CARBON_FATAL("TODO: Support interfaces in DiagnoseMissingDefinitions");
       }
+      case CARBON_KIND(SemIR::SpecificFunction specific_function): {
+        if (!ResolveSpecificDefinition(context,
+                                       specific_function.specific_id)) {
+          CARBON_DIAGNOSTIC(MissingGenericFunctionDefinition, Error,
+                            "use of undefined generic function");
+          CARBON_DIAGNOSTIC(MissingGenericFunctionDefinitionHere, Note,
+                            "generic function declared here");
+          auto generic_decl_id =
+              context.generics()
+                  .Get(context.specifics()
+                           .Get(specific_function.specific_id)
+                           .generic_id)
+                  .decl_id;
+          emitter.Build(decl_inst_id, MissingGenericFunctionDefinition)
+              .Note(generic_decl_id, MissingGenericFunctionDefinitionHere)
+              .Emit();
+        }
+        break;
+      }
       default: {
         CARBON_FATAL("Unexpected inst in definitions_required: {0}", decl_inst);
       }
@@ -908,9 +932,9 @@ static auto CheckParseTree(
     return;
   }
 
-  context.Finalize();
+  CheckRequiredDefinitions(context, emitter);
 
-  DiagnoseMissingDefinitions(context, emitter);
+  context.Finalize();
 
   context.VerifyOnFinish();
 

+ 1 - 0
toolchain/check/context.cpp

@@ -918,6 +918,7 @@ class TypeCompleter {
       case SemIR::BuiltinInstKind::NamespaceType:
       case SemIR::BuiltinInstKind::BoundMethodType:
       case SemIR::BuiltinInstKind::WitnessType:
+      case SemIR::BuiltinInstKind::SpecificFunctionType:
         return MakeCopyValueRepr(type_id);
 
       case SemIR::BuiltinInstKind::StringType:

+ 7 - 2
toolchain/check/eval.cpp

@@ -1190,8 +1190,9 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
       return RebuildAndValidateIfFieldsAreConstant(
           eval_context, inst,
           [&](SemIR::IntType result) {
-            return ValidateIntType(eval_context.context(),
-                                   int_type.bit_width_id, result);
+            return ValidateIntType(
+                eval_context.context(),
+                inst_id.is_valid() ? inst_id : int_type.bit_width_id, result);
           },
           &SemIR::IntType::bit_width_id);
     }
@@ -1207,6 +1208,10 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
           },
           &SemIR::FloatType::bit_width_id);
     }
+    case SemIR::SpecificFunction::Kind:
+      return RebuildIfFieldsAreConstant(eval_context, inst,
+                                        &SemIR::SpecificFunction::callee_id,
+                                        &SemIR::SpecificFunction::specific_id);
     case SemIR::StructType::Kind:
       return RebuildIfFieldsAreConstant(eval_context, inst,
                                         &SemIR::StructType::fields_id);

+ 2 - 0
toolchain/check/testdata/basics/builtin_insts.carbon

@@ -36,6 +36,7 @@
 // CHECK:STDOUT:     instFloatType:   {kind: BuiltinInst, arg0: FloatType, type: typeTypeType}
 // CHECK:STDOUT:     instStringType:  {kind: BuiltinInst, arg0: StringType, type: typeTypeType}
 // CHECK:STDOUT:     instBoundMethodType: {kind: BuiltinInst, arg0: BoundMethodType, type: typeTypeType}
+// CHECK:STDOUT:     instSpecificFunctionType: {kind: BuiltinInst, arg0: SpecificFunctionType, type: typeTypeType}
 // CHECK:STDOUT:     instNamespaceType: {kind: BuiltinInst, arg0: NamespaceType, type: typeTypeType}
 // CHECK:STDOUT:     instWitnessType: {kind: BuiltinInst, arg0: WitnessType, type: typeTypeType}
 // CHECK:STDOUT:     'inst+0':          {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type(instNamespaceType)}
@@ -47,6 +48,7 @@
 // CHECK:STDOUT:     instFloatType:   templateConstant(instFloatType)
 // CHECK:STDOUT:     instStringType:  templateConstant(instStringType)
 // CHECK:STDOUT:     instBoundMethodType: templateConstant(instBoundMethodType)
+// CHECK:STDOUT:     instSpecificFunctionType: templateConstant(instSpecificFunctionType)
 // CHECK:STDOUT:     instNamespaceType: templateConstant(instNamespaceType)
 // CHECK:STDOUT:     instWitnessType: templateConstant(instWitnessType)
 // CHECK:STDOUT:     'inst+0':          templateConstant(inst+0)

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

@@ -419,6 +419,7 @@ class Class(U:! type) {
 // CHECK:STDOUT:   %.6: type = ptr_type %.2 [template]
 // CHECK:STDOUT:   %F.type.3: type = fn_type @F.2 [template]
 // CHECK:STDOUT:   %F.3: %F.type.3 = struct_value () [template]
+// CHECK:STDOUT:   %.7: <specific function> = specific_function %F.2, @F.1(i32) [template]
 // CHECK:STDOUT:   %UseField.type: type = fn_type @UseField [template]
 // CHECK:STDOUT:   %UseField: %UseField.type = struct_value () [template]
 // CHECK:STDOUT: }
@@ -502,9 +503,10 @@ class Class(U:! type) {
 // 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:   %.loc7_11: %F.type.2 = specific_constant imports.%import_ref.7, @CompleteClass(i32) [template = constants.%F.2]
-// CHECK:STDOUT:   %F.ref.loc7: %F.type.2 = name_ref F, %.loc7_11 [template = constants.%F.2]
-// CHECK:STDOUT:   %F.call.loc7: init i32 = call %F.ref.loc7()
+// CHECK:STDOUT:   %.loc7_11.1: %F.type.2 = specific_constant imports.%import_ref.7, @CompleteClass(i32) [template = constants.%F.2]
+// CHECK:STDOUT:   %F.ref.loc7: %F.type.2 = name_ref F, %.loc7_11.1 [template = constants.%F.2]
+// CHECK:STDOUT:   %.loc7_11.2: <specific function> = specific_function %F.ref.loc7, @F.1(i32) [template = constants.%.7]
+// CHECK:STDOUT:   %F.call.loc7: init i32 = call %.loc7_11.2()
 // 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
@@ -564,7 +566,9 @@ class Class(U:! type) {
 // CHECK:STDOUT:   %F => constants.%F.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @F.1(i32) {}
+// CHECK:STDOUT: specific @F.1(i32) {
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_generic_arg_mismatch.carbon
 // CHECK:STDOUT:

+ 39 - 20
toolchain/check/testdata/class/generic/member_access.carbon

@@ -268,7 +268,8 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT:   %.loc15_11.1: %Get.type.2 = specific_constant @Class.%Get.decl, @Class(i32) [template = constants.%Get.2]
 // CHECK:STDOUT:   %Get.ref: %Get.type.2 = name_ref Get, %.loc15_11.1 [template = constants.%Get.2]
 // CHECK:STDOUT:   %.loc15_11.2: <bound method> = bound_method %x.ref, %Get.ref
-// CHECK:STDOUT:   %Get.call: init i32 = call %.loc15_11.2(%x.ref)
+// CHECK:STDOUT:   %.loc15_11.3: <specific function> = specific_function %.loc15_11.2, @Get(i32)
+// CHECK:STDOUT:   %Get.call: init i32 = call %.loc15_11.3(%x.ref)
 // CHECK:STDOUT:   %.loc15_17.1: i32 = value_of_initializer %Get.call
 // CHECK:STDOUT:   %.loc15_17.2: i32 = converted %Get.call, %.loc15_17.1
 // CHECK:STDOUT:   return %.loc15_17.2
@@ -281,8 +282,9 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT:   %.loc19_12.2: %GetAddr.type.2 = specific_constant @Class.%GetAddr.decl, @Class(i32) [template = constants.%GetAddr.2]
 // CHECK:STDOUT:   %GetAddr.ref: %GetAddr.type.2 = name_ref GetAddr, %.loc19_12.2 [template = constants.%GetAddr.2]
 // CHECK:STDOUT:   %.loc19_12.3: <bound method> = bound_method %.loc19_12.1, %GetAddr.ref
-// CHECK:STDOUT:   %.loc19_12.4: %.12 = addr_of %.loc19_12.1
-// CHECK:STDOUT:   %GetAddr.call: init %.13 = call %.loc19_12.3(%.loc19_12.4)
+// CHECK:STDOUT:   %.loc19_12.4: <specific function> = specific_function %.loc19_12.3, @GetAddr(i32)
+// CHECK:STDOUT:   %.loc19_12.5: %.12 = addr_of %.loc19_12.1
+// CHECK:STDOUT:   %GetAddr.call: init %.13 = call %.loc19_12.4(%.loc19_12.5)
 // CHECK:STDOUT:   %.loc19_21.1: %.13 = value_of_initializer %GetAddr.call
 // CHECK:STDOUT:   %.loc19_21.2: %.13 = converted %GetAddr.call, %.loc19_21.1
 // CHECK:STDOUT:   %.loc19_10.1: ref i32 = deref %.loc19_21.2
@@ -345,6 +347,9 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT: specific @Get(i32) {
 // CHECK:STDOUT:   %T => i32
 // CHECK:STDOUT:   %Class => constants.%Class.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc5_42.3 => constants.%.8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @GetAddr(i32) {
@@ -352,6 +357,9 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT:   %Class => constants.%Class.3
 // CHECK:STDOUT:   %.loc7_29.1 => constants.%.12
 // CHECK:STDOUT:   %.loc7_38.1 => constants.%.13
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_54.3 => constants.%.8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_todo_static_member_fn_call.carbon
@@ -370,6 +378,7 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT:   %struct: %Class.2 = struct_value () [symbolic]
 // CHECK:STDOUT:   %StaticMemberFunctionCall.type: type = fn_type @StaticMemberFunctionCall [template]
 // CHECK:STDOUT:   %StaticMemberFunctionCall: %StaticMemberFunctionCall.type = struct_value () [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %Make, @Make(%T) [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.1: type = generic_interface_type @ImplicitAs [template]
 // CHECK:STDOUT:   %ImplicitAs: %ImplicitAs.type.1 = struct_value () [template]
 // CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest, 0 [symbolic]
@@ -378,14 +387,14 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT:   %Self.2: %ImplicitAs.type.2 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
-// CHECK:STDOUT:   %.5: type = assoc_entity_type %ImplicitAs.type.2, %Convert.type.1 [symbolic]
-// CHECK:STDOUT:   %.6: %.5 = assoc_entity element0, imports.%import_ref.5 [symbolic]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type %ImplicitAs.type.2, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.7: %.6 = assoc_entity element0, imports.%import_ref.5 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.3: type = interface_type @ImplicitAs, @ImplicitAs(%Class.2) [symbolic]
 // CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @ImplicitAs(%Class.2) [symbolic]
 // CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [symbolic]
-// CHECK:STDOUT:   %.7: type = assoc_entity_type %ImplicitAs.type.3, %Convert.type.2 [symbolic]
-// CHECK:STDOUT:   %.8: %.7 = assoc_entity element0, imports.%import_ref.5 [symbolic]
-// CHECK:STDOUT:   %.9: %.5 = assoc_entity element0, imports.%import_ref.6 [symbolic]
+// CHECK:STDOUT:   %.8: type = assoc_entity_type %ImplicitAs.type.3, %Convert.type.2 [symbolic]
+// CHECK:STDOUT:   %.9: %.8 = assoc_entity element0, imports.%import_ref.5 [symbolic]
+// CHECK:STDOUT:   %.10: %.6 = assoc_entity element0, imports.%import_ref.6 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -402,7 +411,7 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: %ImplicitAs.type.1 = import_ref Core//prelude/operators/as, inst+40, loaded [template = constants.%ImplicitAs]
 // CHECK:STDOUT:   %import_ref.2 = import_ref Core//prelude/operators/as, inst+45, unloaded
-// CHECK:STDOUT:   %import_ref.3: @ImplicitAs.%.1 (%.5) = import_ref Core//prelude/operators/as, inst+63, loaded [symbolic = @ImplicitAs.%.2 (constants.%.9)]
+// CHECK:STDOUT:   %import_ref.3: @ImplicitAs.%.1 (%.6) = import_ref Core//prelude/operators/as, inst+63, loaded [symbolic = @ImplicitAs.%.2 (constants.%.10)]
 // CHECK:STDOUT:   %import_ref.4 = import_ref Core//prelude/operators/as, inst+56, unloaded
 // CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+56, unloaded
 // CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+56, unloaded
@@ -441,8 +450,8 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT:   %Self: %ImplicitAs.type.2 = bind_symbolic_name Self, 1 [symbolic = %Self (constants.%Self.2)]
 // CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
 // CHECK:STDOUT:   %Convert: @ImplicitAs.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
-// CHECK:STDOUT:   %.1: type = assoc_entity_type @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.2), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.1 (constants.%.5)]
-// CHECK:STDOUT:   %.2: @ImplicitAs.%.1 (%.5) = assoc_entity element0, imports.%import_ref.5 [symbolic = %.2 (constants.%.6)]
+// CHECK:STDOUT:   %.1: type = assoc_entity_type @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.2), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.1 (constants.%.6)]
+// CHECK:STDOUT:   %.2: @ImplicitAs.%.1 (%.6) = assoc_entity element0, imports.%import_ref.5 [symbolic = %.2 (constants.%.7)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   interface {
 // CHECK:STDOUT:   !members:
@@ -497,23 +506,25 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %Make.type: type = fn_type @Make, @Class(%T.loc8_29.2) [symbolic = %Make.type (constants.%Make.type)]
 // CHECK:STDOUT:   %Make: @StaticMemberFunctionCall.%Make.type (%Make.type) = struct_value () [symbolic = %Make (constants.%Make)]
+// CHECK:STDOUT:   %.loc15_18.3: <specific function> = specific_function %Make, @Make(%T.loc8_29.2) [symbolic = %.loc15_18.3 (constants.%.5)]
 // CHECK:STDOUT:   %ImplicitAs.type.loc15_25.2: type = interface_type @ImplicitAs, @ImplicitAs(%Class.loc8_47.2) [symbolic = %ImplicitAs.type.loc15_25.2 (constants.%ImplicitAs.type.3)]
 // CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @ImplicitAs(%Class.loc8_47.2) [symbolic = %Convert.type (constants.%Convert.type.2)]
-// CHECK:STDOUT:   %.loc15_25.3: type = assoc_entity_type @StaticMemberFunctionCall.%ImplicitAs.type.loc15_25.2 (%ImplicitAs.type.3), @StaticMemberFunctionCall.%Convert.type (%Convert.type.2) [symbolic = %.loc15_25.3 (constants.%.7)]
-// CHECK:STDOUT:   %.loc15_25.4: @StaticMemberFunctionCall.%.loc15_25.3 (%.7) = assoc_entity element0, imports.%import_ref.5 [symbolic = %.loc15_25.4 (constants.%.8)]
+// CHECK:STDOUT:   %.loc15_25.3: type = assoc_entity_type @StaticMemberFunctionCall.%ImplicitAs.type.loc15_25.2 (%ImplicitAs.type.3), @StaticMemberFunctionCall.%Convert.type (%Convert.type.2) [symbolic = %.loc15_25.3 (constants.%.8)]
+// CHECK:STDOUT:   %.loc15_25.4: @StaticMemberFunctionCall.%.loc15_25.3 (%.8) = assoc_entity element0, imports.%import_ref.5 [symbolic = %.loc15_25.4 (constants.%.9)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%T.loc8_29.1: type) -> %return: @StaticMemberFunctionCall.%Class.loc8_47.2 (%Class.2) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %Class.ref.loc15: %Class.type = name_ref Class, file.%Class.decl [template = constants.%Class.1]
 // CHECK:STDOUT:     %T.ref.loc15: type = name_ref T, %T.loc8_29.1 [symbolic = %T.loc8_29.2 (constants.%T)]
 // CHECK:STDOUT:     %Class.loc15: type = class_type @Class, @Class(constants.%T) [symbolic = %Class.loc8_47.2 (constants.%Class.2)]
-// CHECK:STDOUT:     %.loc15_18: @StaticMemberFunctionCall.%Make.type (%Make.type) = specific_constant @Class.%Make.decl, @Class(constants.%T) [symbolic = %Make (constants.%Make)]
-// CHECK:STDOUT:     %Make.ref: @StaticMemberFunctionCall.%Make.type (%Make.type) = name_ref Make, %.loc15_18 [symbolic = %Make (constants.%Make)]
+// CHECK:STDOUT:     %.loc15_18.1: @StaticMemberFunctionCall.%Make.type (%Make.type) = specific_constant @Class.%Make.decl, @Class(constants.%T) [symbolic = %Make (constants.%Make)]
+// CHECK:STDOUT:     %Make.ref: @StaticMemberFunctionCall.%Make.type (%Make.type) = name_ref Make, %.loc15_18.1 [symbolic = %Make (constants.%Make)]
+// CHECK:STDOUT:     %.loc15_18.2: <specific function> = specific_function %Make.ref, @Make(constants.%T) [symbolic = %.loc15_18.3 (constants.%.5)]
 // CHECK:STDOUT:     %.loc15_23.1: ref @StaticMemberFunctionCall.%Class.loc8_47.2 (%Class.2) = temporary_storage
-// CHECK:STDOUT:     %Make.call: init @StaticMemberFunctionCall.%Class.loc8_47.2 (%Class.2) = call %Make.ref() to %.loc15_23.1
+// CHECK:STDOUT:     %Make.call: init @StaticMemberFunctionCall.%Class.loc8_47.2 (%Class.2) = call %.loc15_18.2() to %.loc15_23.1
 // CHECK:STDOUT:     %ImplicitAs.type.loc15_25.1: type = interface_type @ImplicitAs, @ImplicitAs(constants.%Class.2) [symbolic = %ImplicitAs.type.loc15_25.2 (constants.%ImplicitAs.type.3)]
-// CHECK:STDOUT:     %.loc15_25.1: @StaticMemberFunctionCall.%.loc15_25.3 (%.7) = specific_constant imports.%import_ref.3, @ImplicitAs(constants.%Class.2) [symbolic = %.loc15_25.4 (constants.%.8)]
-// CHECK:STDOUT:     %Convert.ref: @StaticMemberFunctionCall.%.loc15_25.3 (%.7) = name_ref Convert, %.loc15_25.1 [symbolic = %.loc15_25.4 (constants.%.8)]
+// CHECK:STDOUT:     %.loc15_25.1: @StaticMemberFunctionCall.%.loc15_25.3 (%.8) = specific_constant imports.%import_ref.3, @ImplicitAs(constants.%Class.2) [symbolic = %.loc15_25.4 (constants.%.9)]
+// CHECK:STDOUT:     %Convert.ref: @StaticMemberFunctionCall.%.loc15_25.3 (%.8) = name_ref Convert, %.loc15_25.1 [symbolic = %.loc15_25.4 (constants.%.9)]
 // CHECK:STDOUT:     %.loc15_23.2: ref @StaticMemberFunctionCall.%Class.loc8_47.2 (%Class.2) = temporary %.loc15_23.1, %Make.call
 // CHECK:STDOUT:     %.loc15_25.2: @StaticMemberFunctionCall.%Class.loc8_47.2 (%Class.2) = converted %Make.call, <error> [template = <error>]
 // CHECK:STDOUT:     return <error> to %return
@@ -543,6 +554,9 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT: specific @Make(constants.%T) {
 // CHECK:STDOUT:   %T => constants.%T
 // CHECK:STDOUT:   %Class.loc5_21.1 => constants.%Class.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %struct => constants.%struct
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Class(@Class.%T.loc4_13.2) {
@@ -584,8 +598,13 @@ fn StaticMemberFunctionCall(T:! type) -> Class(T) {
 // CHECK:STDOUT:   %Self => constants.%Self.2
 // CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
 // CHECK:STDOUT:   %Convert => constants.%Convert.2
-// CHECK:STDOUT:   %.1 => constants.%.7
-// CHECK:STDOUT:   %.2 => constants.%.8
+// CHECK:STDOUT:   %.1 => constants.%.8
+// CHECK:STDOUT:   %.2 => constants.%.9
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Make(@StaticMemberFunctionCall.%T.loc8_29.2) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %Class.loc5_21.1 => constants.%Class.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @ImplicitAs(@StaticMemberFunctionCall.%Class.loc8_47.2) {

+ 132 - 28
toolchain/check/testdata/class/generic/method_deduce.carbon

@@ -12,8 +12,8 @@ class A {}
 class B {}
 
 class Class(T:! type) {
-  fn Get(U:! type) -> (T, U);
-  fn GetNoDeduce(x: T, U:! type) -> (T, U);
+  fn Get(U:! type) -> (T, U) { return Get(U); }
+  fn GetNoDeduce(x: T, U:! type) -> (T, U) { return GetNoDeduce(x, U); }
 }
 
 fn CallGenericMethod(c: Class(A)) -> (A, B) {
@@ -43,19 +43,24 @@ fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) {
 // CHECK:STDOUT:   %Get.1: %Get.type.1 = struct_value () [symbolic]
 // CHECK:STDOUT:   %GetNoDeduce.type.1: type = fn_type @GetNoDeduce, @Class(%T) [symbolic]
 // CHECK:STDOUT:   %GetNoDeduce.1: %GetNoDeduce.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.6: type = ptr_type %.5 [symbolic]
+// CHECK:STDOUT:   %.7: <specific function> = specific_function %Get.1, @Get(%T, %U) [symbolic]
+// CHECK:STDOUT:   %.8: <specific function> = specific_function %GetNoDeduce.1, @GetNoDeduce(%T, %U) [symbolic]
 // CHECK:STDOUT:   %Class.3: type = class_type @Class, @Class(%A) [template]
-// CHECK:STDOUT:   %.6: type = tuple_type (%A, %B) [template]
+// CHECK:STDOUT:   %.9: type = tuple_type (%A, %B) [template]
 // CHECK:STDOUT:   %CallGenericMethod.type: type = fn_type @CallGenericMethod [template]
 // CHECK:STDOUT:   %CallGenericMethod: %CallGenericMethod.type = struct_value () [template]
-// CHECK:STDOUT:   %.7: type = ptr_type %.1 [template]
-// CHECK:STDOUT:   %.8: type = tuple_type (%.7, %.7) [template]
-// CHECK:STDOUT:   %.9: type = ptr_type %.8 [template]
+// CHECK:STDOUT:   %.10: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.11: type = tuple_type (%.10, %.10) [template]
+// CHECK:STDOUT:   %.12: type = ptr_type %.11 [template]
 // CHECK:STDOUT:   %Get.type.2: type = fn_type @Get, @Class(%A) [template]
 // CHECK:STDOUT:   %Get.2: %Get.type.2 = struct_value () [template]
 // CHECK:STDOUT:   %GetNoDeduce.type.2: type = fn_type @GetNoDeduce, @Class(%A) [template]
 // CHECK:STDOUT:   %GetNoDeduce.2: %GetNoDeduce.type.2 = struct_value () [template]
+// CHECK:STDOUT:   %.13: <specific function> = specific_function %Get.2, @Get(%A, %B) [template]
 // CHECK:STDOUT:   %CallGenericMethodWithNonDeducedParam.type: type = fn_type @CallGenericMethodWithNonDeducedParam [template]
 // CHECK:STDOUT:   %CallGenericMethodWithNonDeducedParam: %CallGenericMethodWithNonDeducedParam.type = struct_value () [template]
+// CHECK:STDOUT:   %.14: <specific function> = specific_function %GetNoDeduce.2, @GetNoDeduce(%A, %B) [template]
 // CHECK:STDOUT:   %struct: %A = struct_value () [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -101,8 +106,8 @@ fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) {
 // CHECK:STDOUT:     %A.ref.loc19_39: type = name_ref A, file.%A.decl [template = constants.%A]
 // CHECK:STDOUT:     %B.ref.loc19: type = name_ref B, file.%B.decl [template = constants.%B]
 // CHECK:STDOUT:     %.loc19_43.1: %.4 = tuple_literal (%A.ref.loc19_39, %B.ref.loc19)
-// CHECK:STDOUT:     %.loc19_43.2: type = converted %.loc19_43.1, constants.%.6 [template = constants.%.6]
-// CHECK:STDOUT:     %return: ref %.6 = var <return slot>
+// CHECK:STDOUT:     %.loc19_43.2: type = converted %.loc19_43.1, constants.%.9 [template = constants.%.9]
+// CHECK:STDOUT:     %return: ref %.9 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %CallGenericMethodWithNonDeducedParam.decl: %CallGenericMethodWithNonDeducedParam.type = fn_decl @CallGenericMethodWithNonDeducedParam [template = constants.%CallGenericMethodWithNonDeducedParam] {
 // CHECK:STDOUT:     %c.patt: %Class.3 = binding_pattern c
@@ -115,8 +120,8 @@ fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) {
 // CHECK:STDOUT:     %A.ref.loc23_58: type = name_ref A, file.%A.decl [template = constants.%A]
 // CHECK:STDOUT:     %B.ref.loc23: type = name_ref B, file.%B.decl [template = constants.%B]
 // CHECK:STDOUT:     %.loc23_62.1: %.4 = tuple_literal (%A.ref.loc23_58, %B.ref.loc23)
-// CHECK:STDOUT:     %.loc23_62.2: type = converted %.loc23_62.1, constants.%.6 [template = constants.%.6]
-// CHECK:STDOUT:     %return: ref %.6 = var <return slot>
+// CHECK:STDOUT:     %.loc23_62.2: type = converted %.loc23_62.1, constants.%.9 [template = constants.%.9]
+// CHECK:STDOUT:     %return: ref %.9 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -150,8 +155,8 @@ fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) {
 // CHECK:STDOUT:       %U.param: type = param U, runtime_param<invalid>
 // CHECK:STDOUT:       %U.loc15_10.1: type = bind_symbolic_name U, 1, %U.param [symbolic = %U.loc15_10.2 (constants.%U)]
 // CHECK:STDOUT:       %T.ref: type = name_ref T, @Class.%T.loc14_13.1 [symbolic = %T (constants.%T)]
-// CHECK:STDOUT:       %U.ref: type = name_ref U, %U.loc15_10.1 [symbolic = %U.loc15_10.2 (constants.%U)]
-// CHECK:STDOUT:       %.loc15_28.2: %.4 = tuple_literal (%T.ref, %U.ref)
+// CHECK:STDOUT:       %U.ref.loc15_27: type = name_ref U, %U.loc15_10.1 [symbolic = %U.loc15_10.2 (constants.%U)]
+// CHECK:STDOUT:       %.loc15_28.2: %.4 = tuple_literal (%T.ref, %U.ref.loc15_27)
 // CHECK:STDOUT:       %.loc15_28.3: type = converted %.loc15_28.2, constants.%.5 [symbolic = %.loc15_28.1 (constants.%.5)]
 // CHECK:STDOUT:       %return: ref @Get.%.loc15_28.1 (%.5) = var <return slot>
 // CHECK:STDOUT:     }
@@ -165,8 +170,8 @@ fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) {
 // CHECK:STDOUT:       %U.param: type = param U, runtime_param<invalid>
 // CHECK:STDOUT:       %U.loc16_24.1: type = bind_symbolic_name U, 1, %U.param [symbolic = %U.loc16_24.2 (constants.%U)]
 // CHECK:STDOUT:       %T.ref.loc16_38: type = name_ref T, @Class.%T.loc14_13.1 [symbolic = %T (constants.%T)]
-// CHECK:STDOUT:       %U.ref: type = name_ref U, %U.loc16_24.1 [symbolic = %U.loc16_24.2 (constants.%U)]
-// CHECK:STDOUT:       %.loc16_42.2: %.4 = tuple_literal (%T.ref.loc16_38, %U.ref)
+// CHECK:STDOUT:       %U.ref.loc16_41: type = name_ref U, %U.loc16_24.1 [symbolic = %U.loc16_24.2 (constants.%U)]
+// CHECK:STDOUT:       %.loc16_42.2: %.4 = tuple_literal (%T.ref.loc16_38, %U.ref.loc16_41)
 // CHECK:STDOUT:       %.loc16_42.3: type = converted %.loc16_42.2, constants.%.5 [symbolic = %.loc16_42.1 (constants.%.5)]
 // CHECK:STDOUT:       %return: ref @GetNoDeduce.%.loc16_42.1 (%.5) = var <return slot>
 // CHECK:STDOUT:     }
@@ -184,7 +189,32 @@ fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:   %.loc15_28.1: type = tuple_type (@Get.%T (%T), @Get.%U.loc15_10.2 (%U)) [symbolic = %.loc15_28.1 (constants.%.5)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%U.loc15_10.1: type) -> @Get.%.loc15_28.1 (%.5);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Get.type: type = fn_type @Get, @Class(%T) [symbolic = %Get.type (constants.%Get.type.1)]
+// CHECK:STDOUT:   %Get: @Get.%Get.type (%Get.type.1) = struct_value () [symbolic = %Get (constants.%Get.1)]
+// CHECK:STDOUT:   %.loc15_39.3: <specific function> = specific_function %Get, @Get(%T, %U.loc15_10.2) [symbolic = %.loc15_39.3 (constants.%.7)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%U.loc15_10.1: type) -> %return: @Get.%.loc15_28.1 (%.5) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %.loc15_39.1: @Get.%Get.type (%Get.type.1) = specific_constant @Class.%Get.decl, @Class(constants.%T) [symbolic = %Get (constants.%Get.1)]
+// CHECK:STDOUT:     %Get.ref: @Get.%Get.type (%Get.type.1) = name_ref Get, %.loc15_39.1 [symbolic = %Get (constants.%Get.1)]
+// CHECK:STDOUT:     %U.ref.loc15_43: type = name_ref U, %U.loc15_10.1 [symbolic = %U.loc15_10.2 (constants.%U)]
+// CHECK:STDOUT:     %.loc15_39.2: <specific function> = specific_function %Get.ref, @Get(constants.%T, constants.%U) [symbolic = %.loc15_39.3 (constants.%.7)]
+// CHECK:STDOUT:     %.loc15_42.1: ref @Get.%.loc15_28.1 (%.5) = temporary_storage
+// CHECK:STDOUT:     %Get.call: init @Get.%.loc15_28.1 (%.5) = call %.loc15_39.2() to %.loc15_42.1
+// CHECK:STDOUT:     %.loc15_42.2: ref @Get.%.loc15_28.1 (%.5) = temporary %.loc15_42.1, %Get.call
+// CHECK:STDOUT:     %.loc15_42.3: ref @Get.%T (%T) = tuple_access %.loc15_42.2, element0
+// CHECK:STDOUT:     %.loc15_42.4: @Get.%T (%T) = bind_value %.loc15_42.3
+// CHECK:STDOUT:     %.loc15_42.5: ref @Get.%T (%T) = tuple_access %return, element0
+// CHECK:STDOUT:     %.loc15_42.6: init @Get.%T (%T) = initialize_from %.loc15_42.4 to %.loc15_42.5
+// CHECK:STDOUT:     %.loc15_42.7: ref @Get.%U.loc15_10.2 (%U) = tuple_access %.loc15_42.2, element1
+// CHECK:STDOUT:     %.loc15_42.8: @Get.%U.loc15_10.2 (%U) = bind_value %.loc15_42.7
+// CHECK:STDOUT:     %.loc15_42.9: ref @Get.%U.loc15_10.2 (%U) = tuple_access %return, element1
+// CHECK:STDOUT:     %.loc15_42.10: init @Get.%U.loc15_10.2 (%U) = initialize_from %.loc15_42.8 to %.loc15_42.9
+// CHECK:STDOUT:     %.loc15_42.11: init @Get.%.loc15_28.1 (%.5) = tuple_init (%.loc15_42.6, %.loc15_42.10) to %return
+// CHECK:STDOUT:     %.loc15_45: init @Get.%.loc15_28.1 (%.5) = converted %Get.call, %.loc15_42.11
+// CHECK:STDOUT:     return %.loc15_45 to %return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @GetNoDeduce(@Class.%T.loc14_13.1: type, %U.loc16_24.1: type) {
@@ -192,57 +222,121 @@ fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) {
 // CHECK:STDOUT:   %U.loc16_24.2: type = bind_symbolic_name U, 1 [symbolic = %U.loc16_24.2 (constants.%U)]
 // CHECK:STDOUT:   %.loc16_42.1: type = tuple_type (@GetNoDeduce.%T (%T), @GetNoDeduce.%U.loc16_24.2 (%U)) [symbolic = %.loc16_42.1 (constants.%.5)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%x: @GetNoDeduce.%T (%T), %U.loc16_24.1: type) -> @GetNoDeduce.%.loc16_42.1 (%.5);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %GetNoDeduce.type: type = fn_type @GetNoDeduce, @Class(%T) [symbolic = %GetNoDeduce.type (constants.%GetNoDeduce.type.1)]
+// CHECK:STDOUT:   %GetNoDeduce: @GetNoDeduce.%GetNoDeduce.type (%GetNoDeduce.type.1) = struct_value () [symbolic = %GetNoDeduce (constants.%GetNoDeduce.1)]
+// CHECK:STDOUT:   %.loc16_53.3: <specific function> = specific_function %GetNoDeduce, @GetNoDeduce(%T, %U.loc16_24.2) [symbolic = %.loc16_53.3 (constants.%.8)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%x: @GetNoDeduce.%T (%T), %U.loc16_24.1: type) -> %return: @GetNoDeduce.%.loc16_42.1 (%.5) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %.loc16_53.1: @GetNoDeduce.%GetNoDeduce.type (%GetNoDeduce.type.1) = specific_constant @Class.%GetNoDeduce.decl, @Class(constants.%T) [symbolic = %GetNoDeduce (constants.%GetNoDeduce.1)]
+// CHECK:STDOUT:     %GetNoDeduce.ref: @GetNoDeduce.%GetNoDeduce.type (%GetNoDeduce.type.1) = name_ref GetNoDeduce, %.loc16_53.1 [symbolic = %GetNoDeduce (constants.%GetNoDeduce.1)]
+// CHECK:STDOUT:     %x.ref: @GetNoDeduce.%T (%T) = name_ref x, %x
+// CHECK:STDOUT:     %U.ref.loc16_68: type = name_ref U, %U.loc16_24.1 [symbolic = %U.loc16_24.2 (constants.%U)]
+// CHECK:STDOUT:     %.loc16_53.2: <specific function> = specific_function %GetNoDeduce.ref, @GetNoDeduce(constants.%T, constants.%U) [symbolic = %.loc16_53.3 (constants.%.8)]
+// CHECK:STDOUT:     %.loc16_64.1: ref @GetNoDeduce.%.loc16_42.1 (%.5) = temporary_storage
+// CHECK:STDOUT:     %GetNoDeduce.call: init @GetNoDeduce.%.loc16_42.1 (%.5) = call %.loc16_53.2(%x.ref) to %.loc16_64.1
+// CHECK:STDOUT:     %.loc16_64.2: ref @GetNoDeduce.%.loc16_42.1 (%.5) = temporary %.loc16_64.1, %GetNoDeduce.call
+// CHECK:STDOUT:     %.loc16_64.3: ref @GetNoDeduce.%T (%T) = tuple_access %.loc16_64.2, element0
+// CHECK:STDOUT:     %.loc16_64.4: @GetNoDeduce.%T (%T) = bind_value %.loc16_64.3
+// CHECK:STDOUT:     %.loc16_64.5: ref @GetNoDeduce.%T (%T) = tuple_access %return, element0
+// CHECK:STDOUT:     %.loc16_64.6: init @GetNoDeduce.%T (%T) = initialize_from %.loc16_64.4 to %.loc16_64.5
+// CHECK:STDOUT:     %.loc16_64.7: ref @GetNoDeduce.%U.loc16_24.2 (%U) = tuple_access %.loc16_64.2, element1
+// CHECK:STDOUT:     %.loc16_64.8: @GetNoDeduce.%U.loc16_24.2 (%U) = bind_value %.loc16_64.7
+// CHECK:STDOUT:     %.loc16_64.9: ref @GetNoDeduce.%U.loc16_24.2 (%U) = tuple_access %return, element1
+// CHECK:STDOUT:     %.loc16_64.10: init @GetNoDeduce.%U.loc16_24.2 (%U) = initialize_from %.loc16_64.8 to %.loc16_64.9
+// CHECK:STDOUT:     %.loc16_64.11: init @GetNoDeduce.%.loc16_42.1 (%.5) = tuple_init (%.loc16_64.6, %.loc16_64.10) to %return
+// CHECK:STDOUT:     %.loc16_70: init @GetNoDeduce.%.loc16_42.1 (%.5) = converted %GetNoDeduce.call, %.loc16_64.11
+// CHECK:STDOUT:     return %.loc16_70 to %return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @CallGenericMethod(%c: %Class.3) -> %return: %.6 {
+// CHECK:STDOUT: fn @CallGenericMethod(%c: %Class.3) -> %return: %.9 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %c.ref: %Class.3 = name_ref c, %c
-// CHECK:STDOUT:   %.loc20: %Get.type.2 = specific_constant @Class.%Get.decl, @Class(constants.%A) [template = constants.%Get.2]
-// CHECK:STDOUT:   %Get.ref: %Get.type.2 = name_ref Get, %.loc20 [template = constants.%Get.2]
+// CHECK:STDOUT:   %.loc20_11.1: %Get.type.2 = specific_constant @Class.%Get.decl, @Class(constants.%A) [template = constants.%Get.2]
+// CHECK:STDOUT:   %Get.ref: %Get.type.2 = name_ref Get, %.loc20_11.1 [template = constants.%Get.2]
 // CHECK:STDOUT:   %B.ref.loc20: type = name_ref B, file.%B.decl [template = constants.%B]
-// CHECK:STDOUT:   %.loc19_35: ref %.6 = splice_block %return {}
-// CHECK:STDOUT:   %Get.call: init %.6 = call %Get.ref() to %.loc19_35
+// CHECK:STDOUT:   %.loc20_11.2: <specific function> = specific_function %Get.ref, @Get(constants.%A, constants.%B) [template = constants.%.13]
+// CHECK:STDOUT:   %.loc19_35: ref %.9 = splice_block %return {}
+// CHECK:STDOUT:   %Get.call: init %.9 = call %.loc20_11.2() to %.loc19_35
 // CHECK:STDOUT:   return %Get.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @CallGenericMethodWithNonDeducedParam(%c: %Class.3) -> %return: %.6 {
+// CHECK:STDOUT: fn @CallGenericMethodWithNonDeducedParam(%c: %Class.3) -> %return: %.9 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %c.ref: %Class.3 = name_ref c, %c
-// CHECK:STDOUT:   %.loc24_11: %GetNoDeduce.type.2 = specific_constant @Class.%GetNoDeduce.decl, @Class(constants.%A) [template = constants.%GetNoDeduce.2]
-// CHECK:STDOUT:   %GetNoDeduce.ref: %GetNoDeduce.type.2 = name_ref GetNoDeduce, %.loc24_11 [template = constants.%GetNoDeduce.2]
+// CHECK:STDOUT:   %.loc24_11.1: %GetNoDeduce.type.2 = specific_constant @Class.%GetNoDeduce.decl, @Class(constants.%A) [template = constants.%GetNoDeduce.2]
+// CHECK:STDOUT:   %GetNoDeduce.ref: %GetNoDeduce.type.2 = name_ref GetNoDeduce, %.loc24_11.1 [template = constants.%GetNoDeduce.2]
 // CHECK:STDOUT:   %.loc24_25.1: %.1 = struct_literal ()
 // CHECK:STDOUT:   %B.ref.loc24: type = name_ref B, file.%B.decl [template = constants.%B]
-// CHECK:STDOUT:   %.loc23_54: ref %.6 = splice_block %return {}
+// CHECK:STDOUT:   %.loc24_11.2: <specific function> = specific_function %GetNoDeduce.ref, @GetNoDeduce(constants.%A, constants.%B) [template = constants.%.14]
+// CHECK:STDOUT:   %.loc23_54: ref %.9 = splice_block %return {}
 // CHECK:STDOUT:   %.loc24_25.2: ref %A = temporary_storage
 // CHECK:STDOUT:   %.loc24_25.3: init %A = class_init (), %.loc24_25.2 [template = constants.%struct]
 // CHECK:STDOUT:   %.loc24_25.4: ref %A = temporary %.loc24_25.2, %.loc24_25.3
 // CHECK:STDOUT:   %.loc24_23.1: ref %A = converted %.loc24_25.1, %.loc24_25.4
 // CHECK:STDOUT:   %.loc24_23.2: %A = bind_value %.loc24_23.1
-// CHECK:STDOUT:   %GetNoDeduce.call: init %.6 = call %GetNoDeduce.ref(%.loc24_23.2) to %.loc23_54
+// CHECK:STDOUT:   %GetNoDeduce.call: init %.9 = call %.loc24_11.2(%.loc24_23.2) to %.loc23_54
 // CHECK:STDOUT:   return %GetNoDeduce.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Class(constants.%T) {
 // CHECK:STDOUT:   %T.loc14_13.2 => constants.%T
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Get.type => constants.%Get.type.1
+// CHECK:STDOUT:   %Get => constants.%Get.1
+// CHECK:STDOUT:   %GetNoDeduce.type => constants.%GetNoDeduce.type.1
+// CHECK:STDOUT:   %GetNoDeduce => constants.%GetNoDeduce.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Get(constants.%T, constants.%U) {
 // CHECK:STDOUT:   %U.loc15_10.2 => constants.%U
 // CHECK:STDOUT:   %T => constants.%T
 // CHECK:STDOUT:   %.loc15_28.1 => constants.%.5
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Get.type => constants.%Get.type.1
+// CHECK:STDOUT:   %Get => constants.%Get.1
+// CHECK:STDOUT:   %.loc15_39.3 => constants.%.7
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @GetNoDeduce(constants.%T, constants.%U) {
 // CHECK:STDOUT:   %T => constants.%T
 // CHECK:STDOUT:   %U.loc16_24.2 => constants.%U
 // CHECK:STDOUT:   %.loc16_42.1 => constants.%.5
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %GetNoDeduce.type => constants.%GetNoDeduce.type.1
+// CHECK:STDOUT:   %GetNoDeduce => constants.%GetNoDeduce.1
+// CHECK:STDOUT:   %.loc16_53.3 => constants.%.8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Class(@Class.%T.loc14_13.2) {
 // CHECK:STDOUT:   %T.loc14_13.2 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @Class(@Get.%T) {
+// CHECK:STDOUT:   %T.loc14_13.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Get(@Get.%T, @Get.%U.loc15_10.2) {
+// CHECK:STDOUT:   %U.loc15_10.2 => constants.%U
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %.loc15_28.1 => constants.%.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Class(@GetNoDeduce.%T) {
+// CHECK:STDOUT:   %T.loc14_13.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GetNoDeduce(@GetNoDeduce.%T, @GetNoDeduce.%U.loc16_24.2) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %U.loc16_24.2 => constants.%U
+// CHECK:STDOUT:   %.loc16_42.1 => constants.%.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @Class(constants.%A) {
 // CHECK:STDOUT:   %T.loc14_13.2 => constants.%A
 // CHECK:STDOUT:
@@ -256,12 +350,22 @@ fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) {
 // CHECK:STDOUT: specific @Get(constants.%A, constants.%B) {
 // CHECK:STDOUT:   %U.loc15_10.2 => constants.%B
 // CHECK:STDOUT:   %T => constants.%A
-// CHECK:STDOUT:   %.loc15_28.1 => constants.%.6
+// CHECK:STDOUT:   %.loc15_28.1 => constants.%.9
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Get.type => constants.%Get.type.2
+// CHECK:STDOUT:   %Get => constants.%Get.2
+// CHECK:STDOUT:   %.loc15_39.3 => constants.%.13
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @GetNoDeduce(constants.%A, constants.%B) {
 // CHECK:STDOUT:   %T => constants.%A
 // CHECK:STDOUT:   %U.loc16_24.2 => constants.%B
-// CHECK:STDOUT:   %.loc16_42.1 => constants.%.6
+// CHECK:STDOUT:   %.loc16_42.1 => constants.%.9
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %GetNoDeduce.type => constants.%GetNoDeduce.type.2
+// CHECK:STDOUT:   %GetNoDeduce => constants.%GetNoDeduce.2
+// CHECK:STDOUT:   %.loc16_53.3 => constants.%.14
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 53 - 12
toolchain/check/testdata/class/generic/self.carbon

@@ -11,8 +11,8 @@
 class Class(T:! type) {
   // `Self` is the same as `Class(T)` here.
   // TODO: Find a better way to test two types are the same.
-  fn MakeSelf() -> Self;
-  fn MakeClass() -> Class(T);
+  fn MakeSelf() -> Self { return {}; }
+  fn MakeClass() -> Class(T) { return {}; }
   fn F() {
     var c: Class(T) = MakeSelf();
     var s: Self = MakeClass();
@@ -36,6 +36,9 @@ class Class(T:! type) {
 // CHECK:STDOUT:   %.2: type = struct_type {} [template]
 // CHECK:STDOUT:   %.3: <witness> = complete_type_witness %.2 [template]
 // CHECK:STDOUT:   %.4: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %struct: %Class.2 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %MakeSelf, @MakeSelf(%T) [symbolic]
+// CHECK:STDOUT:   %.6: <specific function> = specific_function %MakeClass, @MakeClass(%T) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -78,8 +81,8 @@ class Class(T:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
 // CHECK:STDOUT:     %MakeSelf.decl: @Class.%MakeSelf.type (%MakeSelf.type) = fn_decl @MakeSelf [symbolic = @Class.%MakeSelf (constants.%MakeSelf)] {} {
-// CHECK:STDOUT:       %.loc14: type = specific_constant constants.%Class.2, @Class(constants.%T) [symbolic = %Class (constants.%Class.2)]
-// CHECK:STDOUT:       %Self.ref: type = name_ref Self, %.loc14 [symbolic = %Class (constants.%Class.2)]
+// CHECK:STDOUT:       %.loc14_20: type = specific_constant constants.%Class.2, @Class(constants.%T) [symbolic = %Class (constants.%Class.2)]
+// CHECK:STDOUT:       %Self.ref: type = name_ref Self, %.loc14_20 [symbolic = %Class (constants.%Class.2)]
 // CHECK:STDOUT:       %return: ref @MakeSelf.%Class (%Class.2) = var <return slot>
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %MakeClass.decl: @Class.%MakeClass.type (%MakeClass.type) = fn_decl @MakeClass [symbolic = @Class.%MakeClass (constants.%MakeClass)] {} {
@@ -103,14 +106,32 @@ class Class(T:! type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:   %Class: type = class_type @Class, @Class(%T) [symbolic = %Class (constants.%Class.2)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn() -> @MakeSelf.%Class (%Class.2);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %struct: @MakeSelf.%Class (%Class.2) = struct_value () [symbolic = %struct (constants.%struct)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn() -> %return: @MakeSelf.%Class (%Class.2) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %.loc14_35.1: %.2 = struct_literal ()
+// CHECK:STDOUT:     %.loc14_35.2: init @MakeSelf.%Class (%Class.2) = class_init (), %return [symbolic = %struct (constants.%struct)]
+// CHECK:STDOUT:     %.loc14_36: init @MakeSelf.%Class (%Class.2) = converted %.loc14_35.1, %.loc14_35.2 [symbolic = %struct (constants.%struct)]
+// CHECK:STDOUT:     return %.loc14_36 to %return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @MakeClass(@Class.%T.loc11_13.1: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:   %Class.loc15_26.1: type = class_type @Class, @Class(%T) [symbolic = %Class.loc15_26.1 (constants.%Class.2)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn() -> @MakeClass.%Class.loc15_26.1 (%Class.2);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %struct: @MakeClass.%Class.loc15_26.1 (%Class.2) = struct_value () [symbolic = %struct (constants.%struct)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn() -> %return: @MakeClass.%Class.loc15_26.1 (%Class.2) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %.loc15_40.1: %.2 = struct_literal ()
+// CHECK:STDOUT:     %.loc15_40.2: init @MakeClass.%Class.loc15_26.1 (%Class.2) = class_init (), %return [symbolic = %struct (constants.%struct)]
+// CHECK:STDOUT:     %.loc15_41: init @MakeClass.%Class.loc15_26.1 (%Class.2) = converted %.loc15_40.1, %.loc15_40.2 [symbolic = %struct (constants.%struct)]
+// CHECK:STDOUT:     return %.loc15_41 to %return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @F(@Class.%T.loc11_13.1: type) {
@@ -119,8 +140,10 @@ class Class(T:! type) {
 // CHECK:STDOUT:   %Class.loc17_17.2: type = class_type @Class, @Class(%T) [symbolic = %Class.loc17_17.2 (constants.%Class.2)]
 // CHECK:STDOUT:   %MakeSelf.type: type = fn_type @MakeSelf, @Class(%T) [symbolic = %MakeSelf.type (constants.%MakeSelf.type)]
 // CHECK:STDOUT:   %MakeSelf: @F.%MakeSelf.type (%MakeSelf.type) = struct_value () [symbolic = %MakeSelf (constants.%MakeSelf)]
+// CHECK:STDOUT:   %.loc17_23.3: <specific function> = specific_function %MakeSelf, @MakeSelf(%T) [symbolic = %.loc17_23.3 (constants.%.5)]
 // CHECK:STDOUT:   %MakeClass.type: type = fn_type @MakeClass, @Class(%T) [symbolic = %MakeClass.type (constants.%MakeClass.type)]
 // CHECK:STDOUT:   %MakeClass: @F.%MakeClass.type (%MakeClass.type) = struct_value () [symbolic = %MakeClass (constants.%MakeClass)]
+// CHECK:STDOUT:   %.loc18_19.3: <specific function> = specific_function %MakeClass, @MakeClass(%T) [symbolic = %.loc18_19.3 (constants.%.6)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn() {
 // CHECK:STDOUT:   !entry:
@@ -129,19 +152,21 @@ class Class(T:! type) {
 // CHECK:STDOUT:     %Class.loc17_17.1: type = class_type @Class, @Class(constants.%T) [symbolic = %Class.loc17_17.2 (constants.%Class.2)]
 // CHECK:STDOUT:     %c.var: ref @F.%Class.loc17_17.2 (%Class.2) = var c
 // CHECK:STDOUT:     %c: ref @F.%Class.loc17_17.2 (%Class.2) = bind_name c, %c.var
-// CHECK:STDOUT:     %.loc17_23: @F.%MakeSelf.type (%MakeSelf.type) = specific_constant @Class.%MakeSelf.decl, @Class(constants.%T) [symbolic = %MakeSelf (constants.%MakeSelf)]
-// CHECK:STDOUT:     %MakeSelf.ref: @F.%MakeSelf.type (%MakeSelf.type) = name_ref MakeSelf, %.loc17_23 [symbolic = %MakeSelf (constants.%MakeSelf)]
+// CHECK:STDOUT:     %.loc17_23.1: @F.%MakeSelf.type (%MakeSelf.type) = specific_constant @Class.%MakeSelf.decl, @Class(constants.%T) [symbolic = %MakeSelf (constants.%MakeSelf)]
+// CHECK:STDOUT:     %MakeSelf.ref: @F.%MakeSelf.type (%MakeSelf.type) = name_ref MakeSelf, %.loc17_23.1 [symbolic = %MakeSelf (constants.%MakeSelf)]
+// CHECK:STDOUT:     %.loc17_23.2: <specific function> = specific_function %MakeSelf.ref, @MakeSelf(constants.%T) [symbolic = %.loc17_23.3 (constants.%.5)]
 // CHECK:STDOUT:     %.loc17_9: ref @F.%Class.loc17_17.2 (%Class.2) = splice_block %c.var {}
-// CHECK:STDOUT:     %MakeSelf.call: init @F.%Class.loc17_17.2 (%Class.2) = call %MakeSelf.ref() to %.loc17_9
+// CHECK:STDOUT:     %MakeSelf.call: init @F.%Class.loc17_17.2 (%Class.2) = call %.loc17_23.2() to %.loc17_9
 // CHECK:STDOUT:     assign %c.var, %MakeSelf.call
 // CHECK:STDOUT:     %.loc18_12: type = specific_constant constants.%Class.2, @Class(constants.%T) [symbolic = %Class.loc17_17.2 (constants.%Class.2)]
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, %.loc18_12 [symbolic = %Class.loc17_17.2 (constants.%Class.2)]
 // CHECK:STDOUT:     %s.var: ref @F.%Class.loc17_17.2 (%Class.2) = var s
 // CHECK:STDOUT:     %s: ref @F.%Class.loc17_17.2 (%Class.2) = bind_name s, %s.var
-// CHECK:STDOUT:     %.loc18_19: @F.%MakeClass.type (%MakeClass.type) = specific_constant @Class.%MakeClass.decl, @Class(constants.%T) [symbolic = %MakeClass (constants.%MakeClass)]
-// CHECK:STDOUT:     %MakeClass.ref: @F.%MakeClass.type (%MakeClass.type) = name_ref MakeClass, %.loc18_19 [symbolic = %MakeClass (constants.%MakeClass)]
+// CHECK:STDOUT:     %.loc18_19.1: @F.%MakeClass.type (%MakeClass.type) = specific_constant @Class.%MakeClass.decl, @Class(constants.%T) [symbolic = %MakeClass (constants.%MakeClass)]
+// CHECK:STDOUT:     %MakeClass.ref: @F.%MakeClass.type (%MakeClass.type) = name_ref MakeClass, %.loc18_19.1 [symbolic = %MakeClass (constants.%MakeClass)]
+// CHECK:STDOUT:     %.loc18_19.2: <specific function> = specific_function %MakeClass.ref, @MakeClass(constants.%T) [symbolic = %.loc18_19.3 (constants.%.6)]
 // CHECK:STDOUT:     %.loc18_9: ref @F.%Class.loc17_17.2 (%Class.2) = splice_block %s.var {}
-// CHECK:STDOUT:     %MakeClass.call: init @F.%Class.loc17_17.2 (%Class.2) = call %MakeClass.ref() to %.loc18_9
+// CHECK:STDOUT:     %MakeClass.call: init @F.%Class.loc17_17.2 (%Class.2) = call %.loc18_19.2() to %.loc18_9
 // CHECK:STDOUT:     assign %s.var, %MakeClass.call
 // CHECK:STDOUT:     return
 // CHECK:STDOUT:   }
@@ -166,6 +191,9 @@ class Class(T:! type) {
 // CHECK:STDOUT: specific @MakeSelf(constants.%T) {
 // CHECK:STDOUT:   %T => constants.%T
 // CHECK:STDOUT:   %Class => constants.%Class.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %struct => constants.%struct
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Class(@MakeClass.%T) {
@@ -175,6 +203,9 @@ class Class(T:! type) {
 // CHECK:STDOUT: specific @MakeClass(constants.%T) {
 // CHECK:STDOUT:   %T => constants.%T
 // CHECK:STDOUT:   %Class.loc15_26.1 => constants.%Class.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %struct => constants.%struct
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%T) {}
@@ -187,3 +218,13 @@ class Class(T:! type) {
 // CHECK:STDOUT:   %T.loc11_13.2 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @MakeSelf(@F.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %Class => constants.%Class.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @MakeClass(@F.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %Class.loc15_26.1 => constants.%Class.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 24 - 16
toolchain/check/testdata/deduce/array.carbon

@@ -121,6 +121,7 @@ fn G() -> C {
 // CHECK:STDOUT:   %.12: i32 = int_literal 1 [template]
 // CHECK:STDOUT:   %.13: i32 = int_literal 2 [template]
 // CHECK:STDOUT:   %array: %.9 = tuple_value (%struct, %struct, %struct) [template]
+// CHECK:STDOUT:   %.14: <specific function> = specific_function %F, @F(%C) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -217,9 +218,10 @@ fn G() -> C {
 // CHECK:STDOUT:   assign %a.var, %.loc9_31
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %a.ref: ref %.9 = name_ref a, %a
+// CHECK:STDOUT:   %.loc10_10: <specific function> = specific_function %F.ref, @F(constants.%C) [template = constants.%.14]
 // CHECK:STDOUT:   %.loc8: ref %C = splice_block %return {}
-// CHECK:STDOUT:   %.loc10: %.9 = bind_value %a.ref
-// CHECK:STDOUT:   %F.call: init %C = call %F.ref(%.loc10) to %.loc8
+// CHECK:STDOUT:   %.loc10_12: %.9 = bind_value %a.ref
+// CHECK:STDOUT:   %F.call: init %C = call %.loc10_10(%.loc10_12) to %.loc8
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -231,6 +233,8 @@ fn G() -> C {
 // CHECK:STDOUT: specific @F(constants.%C) {
 // CHECK:STDOUT:   %T.loc6_6.2 => constants.%C
 // CHECK:STDOUT:   %.loc6_24.2 => constants.%.9
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_todo_bound_only.carbon
@@ -518,7 +522,8 @@ fn G() -> C {
 // CHECK:STDOUT:   %.13: i32 = int_literal 1 [template]
 // CHECK:STDOUT:   %array: %.10 = tuple_value (%struct, %struct, %struct) [template]
 // CHECK:STDOUT:   %.14: type = array_type %.3, %C [template]
-// CHECK:STDOUT:   %.15: type = ptr_type %.14 [template]
+// CHECK:STDOUT:   %.15: <specific function> = specific_function %F, @F(%C) [template]
+// CHECK:STDOUT:   %.16: type = ptr_type %.14 [template]
 // CHECK:STDOUT:   %ImplicitAs.type.1: type = generic_interface_type @ImplicitAs [template]
 // CHECK:STDOUT:   %ImplicitAs: %ImplicitAs.type.1 = struct_value () [template]
 // CHECK:STDOUT:   %Dest: type = bind_symbolic_name Dest, 0 [symbolic]
@@ -527,14 +532,14 @@ fn G() -> C {
 // CHECK:STDOUT:   %Self.2: %ImplicitAs.type.2 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.1: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.1: %Convert.type.1 = struct_value () [symbolic]
-// CHECK:STDOUT:   %.16: type = assoc_entity_type %ImplicitAs.type.2, %Convert.type.1 [symbolic]
-// CHECK:STDOUT:   %.17: %.16 = assoc_entity element0, imports.%import_ref.5 [symbolic]
+// CHECK:STDOUT:   %.17: type = assoc_entity_type %ImplicitAs.type.2, %Convert.type.1 [symbolic]
+// CHECK:STDOUT:   %.18: %.17 = assoc_entity element0, imports.%import_ref.5 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.3: type = interface_type @ImplicitAs, @ImplicitAs(%.14) [template]
 // CHECK:STDOUT:   %Convert.type.2: type = fn_type @Convert, @ImplicitAs(%.14) [template]
 // CHECK:STDOUT:   %Convert.2: %Convert.type.2 = struct_value () [template]
-// CHECK:STDOUT:   %.18: type = assoc_entity_type %ImplicitAs.type.3, %Convert.type.2 [template]
-// CHECK:STDOUT:   %.19: %.18 = assoc_entity element0, imports.%import_ref.5 [template]
-// CHECK:STDOUT:   %.20: %.16 = assoc_entity element0, imports.%import_ref.6 [symbolic]
+// CHECK:STDOUT:   %.19: type = assoc_entity_type %ImplicitAs.type.3, %Convert.type.2 [template]
+// CHECK:STDOUT:   %.20: %.19 = assoc_entity element0, imports.%import_ref.5 [template]
+// CHECK:STDOUT:   %.21: %.17 = assoc_entity element0, imports.%import_ref.6 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -551,7 +556,7 @@ fn G() -> C {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %import_ref.1: %ImplicitAs.type.1 = import_ref Core//prelude/operators/as, inst+40, loaded [template = constants.%ImplicitAs]
 // CHECK:STDOUT:   %import_ref.2 = import_ref Core//prelude/operators/as, inst+45, unloaded
-// CHECK:STDOUT:   %import_ref.3: @ImplicitAs.%.1 (%.16) = import_ref Core//prelude/operators/as, inst+63, loaded [symbolic = @ImplicitAs.%.2 (constants.%.20)]
+// CHECK:STDOUT:   %import_ref.3: @ImplicitAs.%.1 (%.17) = import_ref Core//prelude/operators/as, inst+63, loaded [symbolic = @ImplicitAs.%.2 (constants.%.21)]
 // CHECK:STDOUT:   %import_ref.4 = import_ref Core//prelude/operators/as, inst+56, unloaded
 // CHECK:STDOUT:   %import_ref.5 = import_ref Core//prelude/operators/as, inst+56, unloaded
 // CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+56, unloaded
@@ -594,8 +599,8 @@ fn G() -> C {
 // CHECK:STDOUT:   %Self: %ImplicitAs.type.2 = bind_symbolic_name Self, 1 [symbolic = %Self (constants.%Self.2)]
 // CHECK:STDOUT:   %Convert.type: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic = %Convert.type (constants.%Convert.type.1)]
 // CHECK:STDOUT:   %Convert: @ImplicitAs.%Convert.type (%Convert.type.1) = struct_value () [symbolic = %Convert (constants.%Convert.1)]
-// CHECK:STDOUT:   %.1: type = assoc_entity_type @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.2), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.1 (constants.%.16)]
-// CHECK:STDOUT:   %.2: @ImplicitAs.%.1 (%.16) = assoc_entity element0, imports.%import_ref.5 [symbolic = %.2 (constants.%.17)]
+// CHECK:STDOUT:   %.1: type = assoc_entity_type @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.2), @ImplicitAs.%Convert.type (%Convert.type.1) [symbolic = %.1 (constants.%.17)]
+// CHECK:STDOUT:   %.2: @ImplicitAs.%.1 (%.17) = assoc_entity element0, imports.%import_ref.5 [symbolic = %.2 (constants.%.18)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   interface {
 // CHECK:STDOUT:   !members:
@@ -657,12 +662,13 @@ fn G() -> C {
 // CHECK:STDOUT:   assign %a.var, %.loc10_31
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %a.ref: ref %.10 = name_ref a, %a
+// CHECK:STDOUT:   %.loc21_10: <specific function> = specific_function %F.ref, @F(constants.%C) [template = constants.%.15]
 // CHECK:STDOUT:   %.loc21_11.1: ref %C = temporary_storage
 // CHECK:STDOUT:   %ImplicitAs.type: type = interface_type @ImplicitAs, @ImplicitAs(constants.%.14) [template = constants.%ImplicitAs.type.3]
-// CHECK:STDOUT:   %.loc21_11.2: %.18 = specific_constant imports.%import_ref.3, @ImplicitAs(constants.%.14) [template = constants.%.19]
-// CHECK:STDOUT:   %Convert.ref: %.18 = name_ref Convert, %.loc21_11.2 [template = constants.%.19]
+// CHECK:STDOUT:   %.loc21_11.2: %.19 = specific_constant imports.%import_ref.3, @ImplicitAs(constants.%.14) [template = constants.%.20]
+// CHECK:STDOUT:   %Convert.ref: %.19 = name_ref Convert, %.loc21_11.2 [template = constants.%.20]
 // CHECK:STDOUT:   %.loc21_11.3: %.14 = converted %a.ref, <error> [template = <error>]
-// CHECK:STDOUT:   %F.call: init %C = call %F.ref(<invalid>) [template = <error>]
+// CHECK:STDOUT:   %F.call: init %C = call %.loc21_10(<invalid>) [template = <error>]
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -682,6 +688,8 @@ fn G() -> C {
 // CHECK:STDOUT: specific @F(constants.%C) {
 // CHECK:STDOUT:   %T.loc6_6.2 => constants.%C
 // CHECK:STDOUT:   %.loc6_24.2 => constants.%.14
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) {
@@ -710,8 +718,8 @@ fn G() -> C {
 // CHECK:STDOUT:   %Self => constants.%Self.2
 // CHECK:STDOUT:   %Convert.type => constants.%Convert.type.2
 // CHECK:STDOUT:   %Convert => constants.%Convert.2
-// CHECK:STDOUT:   %.1 => constants.%.18
-// CHECK:STDOUT:   %.2 => constants.%.19
+// CHECK:STDOUT:   %.1 => constants.%.19
+// CHECK:STDOUT:   %.2 => constants.%.20
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_type_mismatch.carbon

+ 123 - 20
toolchain/check/testdata/deduce/generic_type.carbon

@@ -15,7 +15,7 @@ library "[[@TEST_NAME]]";
 class C(T:! type) {}
 class D {}
 
-fn F[T:! type](p: C(T)) -> T;
+fn F[T:! type](p: C(T)) -> T { return F(p); }
 
 fn G(p: C(D)) -> D {
   return F(p);
@@ -28,7 +28,7 @@ library "[[@TEST_NAME]]";
 class I(T:! type) {}
 class C {}
 
-fn F[T:! type](p: I(T)) -> C;
+fn F[T:! type](p: I(T)) -> C { return F(p); }
 
 fn G(p: I(C)) -> C {
   return F(p);
@@ -47,7 +47,7 @@ class D {}
 
 // C++ doesn't permit deducing `T` here because `Outer` might be specialized.
 // But that's not possible in Carbon, so we can deduce `T`.
-fn F[T:! type, U:! type](p: Outer(T).Inner(U)) -> (T, U);
+fn F[T:! type, U:! type](p: Outer(T).Inner(U)) -> (T, U) { return F(p); }
 
 fn G(p: Outer(C).Inner(D)) -> (C, D) {
   return F(p);
@@ -78,10 +78,12 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %D: type = class_type @D [template]
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %F, @F(%T) [symbolic]
 // CHECK:STDOUT:   %C.3: type = class_type @C, @C(%D) [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %.6: <specific function> = specific_function %F, @F(%D) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -164,20 +166,35 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %T.loc7_6.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc7_6.2 (constants.%T)]
 // CHECK:STDOUT:   %C.loc7_20.2: type = class_type @C, @C(%T.loc7_6.2) [symbolic = %C.loc7_20.2 (constants.%C.2)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc7_6.1: type](%p: @F.%C.loc7_20.2 (%C.2)) -> @F.%T.loc7_6.2 (%T);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_39.2: <specific function> = specific_function constants.%F, @F(%T.loc7_6.2) [symbolic = %.loc7_39.2 (constants.%.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc7_6.1: type](%p: @F.%C.loc7_20.2 (%C.2)) -> @F.%T.loc7_6.2 (%T) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:     %p.ref: @F.%C.loc7_20.2 (%C.2) = name_ref p, %p
+// CHECK:STDOUT:     %.loc7_39.1: <specific function> = specific_function %F.ref, @F(constants.%T) [symbolic = %.loc7_39.2 (constants.%.5)]
+// CHECK:STDOUT:     %F.call: init @F.%T.loc7_6.2 (%T) = call %.loc7_39.1(%p.ref)
+// CHECK:STDOUT:     %.loc7_43.1: @F.%T.loc7_6.2 (%T) = value_of_initializer %F.call
+// CHECK:STDOUT:     %.loc7_43.2: @F.%T.loc7_6.2 (%T) = converted %F.call, %.loc7_43.1
+// CHECK:STDOUT:     return %.loc7_43.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(%p: %C.3) -> %return: %D {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %p.ref: %C.3 = name_ref p, %p
+// CHECK:STDOUT:   %.loc10: <specific function> = specific_function %F.ref, @F(constants.%D) [template = constants.%.6]
 // CHECK:STDOUT:   %.loc9: ref %D = splice_block %return {}
-// CHECK:STDOUT:   %F.call: init %D = call %F.ref(%p.ref) to %.loc9
+// CHECK:STDOUT:   %F.call: init %D = call %.loc10(%p.ref) to %.loc9
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @C(constants.%T) {
 // CHECK:STDOUT:   %T.loc4_9.2 => constants.%T
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @C(@F.%T.loc7_6.2) {
@@ -187,6 +204,14 @@ fn G() -> i32 {
 // CHECK:STDOUT: specific @F(constants.%T) {
 // CHECK:STDOUT:   %T.loc7_6.2 => constants.%T
 // CHECK:STDOUT:   %C.loc7_20.2 => constants.%C.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_39.2 => constants.%.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(@F.%T.loc7_6.2) {
+// CHECK:STDOUT:   %T.loc7_6.2 => constants.%T
+// CHECK:STDOUT:   %C.loc7_20.2 => constants.%C.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @C(constants.%D) {
@@ -198,6 +223,9 @@ fn G() -> i32 {
 // CHECK:STDOUT: specific @F(constants.%D) {
 // CHECK:STDOUT:   %T.loc7_6.2 => constants.%D
 // CHECK:STDOUT:   %C.loc7_20.2 => constants.%C.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_39.2 => constants.%.6
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- interface.carbon
@@ -213,10 +241,12 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %F, @F(%T) [symbolic]
 // CHECK:STDOUT:   %I.3: type = class_type @I, @I(%C) [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %.6: <specific function> = specific_function %F, @F(%C) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -299,20 +329,34 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %T.loc7_6.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc7_6.2 (constants.%T)]
 // CHECK:STDOUT:   %I.loc7_20.2: type = class_type @I, @I(%T.loc7_6.2) [symbolic = %I.loc7_20.2 (constants.%I.2)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc7_6.1: type](%p: @F.%I.loc7_20.2 (%I.2)) -> %C;
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_39.2: <specific function> = specific_function constants.%F, @F(%T.loc7_6.2) [symbolic = %.loc7_39.2 (constants.%.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc7_6.1: type](%p: @F.%I.loc7_20.2 (%I.2)) -> %return: %C {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:     %p.ref: @F.%I.loc7_20.2 (%I.2) = name_ref p, %p
+// CHECK:STDOUT:     %.loc7_39.1: <specific function> = specific_function %F.ref, @F(constants.%T) [symbolic = %.loc7_39.2 (constants.%.5)]
+// CHECK:STDOUT:     %.loc7_25: ref %C = splice_block %return {}
+// CHECK:STDOUT:     %F.call: init %C = call %.loc7_39.1(%p.ref) to %.loc7_25
+// CHECK:STDOUT:     return %F.call to %return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @G(%p: %I.3) -> %return: %C {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %p.ref: %I.3 = name_ref p, %p
+// CHECK:STDOUT:   %.loc10: <specific function> = specific_function %F.ref, @F(constants.%C) [template = constants.%.6]
 // CHECK:STDOUT:   %.loc9: ref %C = splice_block %return {}
-// CHECK:STDOUT:   %F.call: init %C = call %F.ref(%p.ref) to %.loc9
+// CHECK:STDOUT:   %F.call: init %C = call %.loc10(%p.ref) to %.loc9
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @I(constants.%T) {
 // CHECK:STDOUT:   %T.loc4_9.2 => constants.%T
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @I(@F.%T.loc7_6.2) {
@@ -322,6 +366,14 @@ fn G() -> i32 {
 // CHECK:STDOUT: specific @F(constants.%T) {
 // CHECK:STDOUT:   %T.loc7_6.2 => constants.%T
 // CHECK:STDOUT:   %I.loc7_20.2 => constants.%I.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_39.2 => constants.%.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(@F.%T.loc7_6.2) {
+// CHECK:STDOUT:   %T.loc7_6.2 => constants.%T
+// CHECK:STDOUT:   %I.loc7_20.2 => constants.%I.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @I(constants.%C) {
@@ -333,6 +385,9 @@ fn G() -> i32 {
 // CHECK:STDOUT: specific @F(constants.%C) {
 // CHECK:STDOUT:   %T.loc7_6.2 => constants.%C
 // CHECK:STDOUT:   %I.loc7_20.2 => constants.%I.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_39.2 => constants.%.6
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- nested.carbon
@@ -356,15 +411,18 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %.6: type = tuple_type (%T, %U) [symbolic]
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.6 [symbolic]
+// CHECK:STDOUT:   %.8: <specific function> = specific_function %F, @F(%T, %U) [symbolic]
 // CHECK:STDOUT:   %Outer.3: type = class_type @Outer, @Outer(%C) [template]
 // CHECK:STDOUT:   %Inner.type.2: type = generic_class_type @Inner, @Outer(%C) [template]
 // CHECK:STDOUT:   %Inner.3: %Inner.type.2 = struct_value () [template]
 // CHECK:STDOUT:   %Inner.4: type = class_type @Inner, @Inner(%C, %D) [template]
-// CHECK:STDOUT:   %.7: type = tuple_type (%C, %D) [template]
+// CHECK:STDOUT:   %.9: type = tuple_type (%C, %D) [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
-// CHECK:STDOUT:   %.8: type = tuple_type (%.4, %.4) [template]
-// CHECK:STDOUT:   %.9: type = ptr_type %.8 [template]
+// CHECK:STDOUT:   %.10: type = tuple_type (%.4, %.4) [template]
+// CHECK:STDOUT:   %.11: type = ptr_type %.10 [template]
+// CHECK:STDOUT:   %.12: <specific function> = specific_function %F, @F(%C, %D) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -437,8 +495,8 @@ fn G() -> i32 {
 // CHECK:STDOUT:     %C.ref.loc15_32: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:     %D.ref.loc15_35: type = name_ref D, file.%D.decl [template = constants.%D]
 // CHECK:STDOUT:     %.loc15_36.1: %.5 = tuple_literal (%C.ref.loc15_32, %D.ref.loc15_35)
-// CHECK:STDOUT:     %.loc15_36.2: type = converted %.loc15_36.1, constants.%.7 [template = constants.%.7]
-// CHECK:STDOUT:     %return: ref %.7 = var <return slot>
+// CHECK:STDOUT:     %.loc15_36.2: type = converted %.loc15_36.1, constants.%.9 [template = constants.%.9]
+// CHECK:STDOUT:     %return: ref %.9 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -500,15 +558,38 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %Inner.loc13_43.2: type = class_type @Inner, @Inner(%T.loc13_6.2, %U.loc13_16.2) [symbolic = %Inner.loc13_43.2 (constants.%Inner.2)]
 // CHECK:STDOUT:   %.loc13_56.3: type = tuple_type (@F.%T.loc13_6.2 (%T), @F.%U.loc13_16.2 (%U)) [symbolic = %.loc13_56.3 (constants.%.6)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc13_6.1: type, %U.loc13_16.1: type](%p: @F.%Inner.loc13_43.2 (%Inner.2)) -> @F.%.loc13_56.3 (%.6);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc13_67.2: <specific function> = specific_function constants.%F, @F(%T.loc13_6.2, %U.loc13_16.2) [symbolic = %.loc13_67.2 (constants.%.8)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc13_6.1: type, %U.loc13_16.1: type](%p: @F.%Inner.loc13_43.2 (%Inner.2)) -> %return: @F.%.loc13_56.3 (%.6) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:     %p.ref: @F.%Inner.loc13_43.2 (%Inner.2) = name_ref p, %p
+// CHECK:STDOUT:     %.loc13_67.1: <specific function> = specific_function %F.ref, @F(constants.%T, constants.%U) [symbolic = %.loc13_67.2 (constants.%.8)]
+// CHECK:STDOUT:     %.loc13_68.1: ref @F.%.loc13_56.3 (%.6) = temporary_storage
+// CHECK:STDOUT:     %F.call: init @F.%.loc13_56.3 (%.6) = call %.loc13_67.1(%p.ref) to %.loc13_68.1
+// CHECK:STDOUT:     %.loc13_68.2: ref @F.%.loc13_56.3 (%.6) = temporary %.loc13_68.1, %F.call
+// CHECK:STDOUT:     %.loc13_68.3: ref @F.%T.loc13_6.2 (%T) = tuple_access %.loc13_68.2, element0
+// CHECK:STDOUT:     %.loc13_68.4: @F.%T.loc13_6.2 (%T) = bind_value %.loc13_68.3
+// CHECK:STDOUT:     %.loc13_68.5: ref @F.%T.loc13_6.2 (%T) = tuple_access %return, element0
+// CHECK:STDOUT:     %.loc13_68.6: init @F.%T.loc13_6.2 (%T) = initialize_from %.loc13_68.4 to %.loc13_68.5
+// CHECK:STDOUT:     %.loc13_68.7: ref @F.%U.loc13_16.2 (%U) = tuple_access %.loc13_68.2, element1
+// CHECK:STDOUT:     %.loc13_68.8: @F.%U.loc13_16.2 (%U) = bind_value %.loc13_68.7
+// CHECK:STDOUT:     %.loc13_68.9: ref @F.%U.loc13_16.2 (%U) = tuple_access %return, element1
+// CHECK:STDOUT:     %.loc13_68.10: init @F.%U.loc13_16.2 (%U) = initialize_from %.loc13_68.8 to %.loc13_68.9
+// CHECK:STDOUT:     %.loc13_68.11: init @F.%.loc13_56.3 (%.6) = tuple_init (%.loc13_68.6, %.loc13_68.10) to %return
+// CHECK:STDOUT:     %.loc13_71: init @F.%.loc13_56.3 (%.6) = converted %F.call, %.loc13_68.11
+// CHECK:STDOUT:     return %.loc13_71 to %return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G(%p: %Inner.4) -> %return: %.7 {
+// CHECK:STDOUT: fn @G(%p: %Inner.4) -> %return: %.9 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %p.ref: %Inner.4 = name_ref p, %p
-// CHECK:STDOUT:   %.loc15_28: ref %.7 = splice_block %return {}
-// CHECK:STDOUT:   %F.call: init %.7 = call %F.ref(%p.ref) to %.loc15_28
+// CHECK:STDOUT:   %.loc16: <specific function> = specific_function %F.ref, @F(constants.%C, constants.%D) [template = constants.%.12]
+// CHECK:STDOUT:   %.loc15_28: ref %.9 = splice_block %return {}
+// CHECK:STDOUT:   %F.call: init %.9 = call %.loc16(%p.ref) to %.loc15_28
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -522,6 +603,8 @@ fn G() -> i32 {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Inner(constants.%T, constants.%U) {
 // CHECK:STDOUT:   %U.loc5_15.2 => constants.%U
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Outer(@Outer.%T.loc4_13.2) {
@@ -544,6 +627,19 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %Inner.loc13_37 => constants.%Inner.1
 // CHECK:STDOUT:   %Inner.loc13_43.2 => constants.%Inner.2
 // CHECK:STDOUT:   %.loc13_56.3 => constants.%.6
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc13_67.2 => constants.%.8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(@F.%T.loc13_6.2, @F.%U.loc13_16.2) {
+// CHECK:STDOUT:   %T.loc13_6.2 => constants.%T
+// CHECK:STDOUT:   %U.loc13_16.2 => constants.%U
+// CHECK:STDOUT:   %Outer.loc13_34.2 => constants.%Outer.2
+// CHECK:STDOUT:   %Inner.type => constants.%Inner.type.1
+// CHECK:STDOUT:   %Inner.loc13_37 => constants.%Inner.1
+// CHECK:STDOUT:   %Inner.loc13_43.2 => constants.%Inner.2
+// CHECK:STDOUT:   %.loc13_56.3 => constants.%.6
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Outer(constants.%C) {
@@ -567,7 +663,10 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %Inner.type => constants.%Inner.type.2
 // CHECK:STDOUT:   %Inner.loc13_37 => constants.%Inner.3
 // CHECK:STDOUT:   %Inner.loc13_43.2 => constants.%Inner.4
-// CHECK:STDOUT:   %.loc13_56.3 => constants.%.7
+// CHECK:STDOUT:   %.loc13_56.3 => constants.%.9
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc13_67.2 => constants.%.12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- nontype.carbon
@@ -590,6 +689,7 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %.5: i32 = int_literal 0 [template]
 // CHECK:STDOUT:   %WithNontype.3: type = class_type @WithNontype, @WithNontype(%.5) [template]
 // CHECK:STDOUT:   %struct: %WithNontype.3 = struct_value () [template]
+// CHECK:STDOUT:   %.6: <specific function> = specific_function %F, @F(%.5) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -690,8 +790,9 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %.loc9_13.3: init %WithNontype.3 = class_init (), %.loc9_13.2 [template = constants.%struct]
 // CHECK:STDOUT:   %.loc9_13.4: ref %WithNontype.3 = temporary %.loc9_13.2, %.loc9_13.3
 // CHECK:STDOUT:   %.loc9_15.1: ref %WithNontype.3 = converted %.loc9_13.1, %.loc9_13.4
+// CHECK:STDOUT:   %.loc9_10: <specific function> = specific_function %F.ref, @F(constants.%.5) [template = constants.%.6]
 // CHECK:STDOUT:   %.loc9_15.2: %WithNontype.3 = bind_value %.loc9_15.1
-// CHECK:STDOUT:   %F.call: init i32 = call %F.ref(%.loc9_15.2)
+// CHECK:STDOUT:   %F.call: init i32 = call %.loc9_10(%.loc9_15.2)
 // CHECK:STDOUT:   %.loc9_33.1: i32 = value_of_initializer %F.call
 // CHECK:STDOUT:   %.loc9_33.2: i32 = converted %F.call, %.loc9_33.1
 // CHECK:STDOUT:   return %.loc9_33.2
@@ -721,5 +822,7 @@ fn G() -> i32 {
 // CHECK:STDOUT: specific @F(constants.%.5) {
 // CHECK:STDOUT:   %N.loc6_6.2 => constants.%.5
 // CHECK:STDOUT:   %WithNontype.loc6_29.2 => constants.%WithNontype.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 5 - 1
toolchain/check/testdata/deduce/int_float.carbon

@@ -52,6 +52,7 @@ fn G(a: f64) -> i32 {
 // CHECK:STDOUT:   %.4: type = int_type signed, %.3 [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %F, @F(%.3) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -137,7 +138,8 @@ fn G(a: f64) -> i32 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %a.ref: %.4 = name_ref a, %a
-// CHECK:STDOUT:   %F.call: init i32 = call %F.ref(%a.ref)
+// CHECK:STDOUT:   %.loc9_10: <specific function> = specific_function %F.ref, @F(constants.%.3) [template = constants.%.5]
+// CHECK:STDOUT:   %F.call: init i32 = call %.loc9_10(%a.ref)
 // CHECK:STDOUT:   %.loc9_14.1: i32 = value_of_initializer %F.call
 // CHECK:STDOUT:   %.loc9_14.2: i32 = converted %F.call, %.loc9_14.1
 // CHECK:STDOUT:   return %.loc9_14.2
@@ -151,6 +153,8 @@ fn G(a: f64) -> i32 {
 // CHECK:STDOUT: specific @F(constants.%.3) {
 // CHECK:STDOUT:   %N.loc4_6.2 => constants.%.3
 // CHECK:STDOUT:   %.loc4_26 => constants.%.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_todo_float.carbon

+ 47 - 15
toolchain/check/testdata/deduce/tuple.carbon

@@ -15,7 +15,7 @@ library "[[@TEST_NAME]]";
 class C {}
 class D {}
 
-fn F[T:! type, U:! type](pair: (T, U)) -> U;
+fn F[T:! type, U:! type](pair: (T, U)) -> U { return F(pair); }
 
 fn G(pair: (C, D)) -> D {
   return F(pair);
@@ -66,12 +66,15 @@ fn G(pair: (C, D)) -> D {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %.5: type = tuple_type () [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
-// CHECK:STDOUT:   %.6: type = tuple_type (%C, %D) [template]
+// CHECK:STDOUT:   %.6: type = ptr_type %.4 [symbolic]
+// CHECK:STDOUT:   %.7: <specific function> = specific_function %F, @F(%T, %U) [symbolic]
+// CHECK:STDOUT:   %.8: type = tuple_type (%C, %D) [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
-// CHECK:STDOUT:   %.7: type = ptr_type %.1 [template]
-// CHECK:STDOUT:   %.8: type = tuple_type (%.7, %.7) [template]
-// CHECK:STDOUT:   %.9: type = ptr_type %.8 [template]
+// CHECK:STDOUT:   %.9: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.10: type = tuple_type (%.9, %.9) [template]
+// CHECK:STDOUT:   %.11: type = ptr_type %.10 [template]
+// CHECK:STDOUT:   %.12: <specific function> = specific_function %F, @F(%C, %D) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -117,14 +120,14 @@ fn G(pair: (C, D)) -> D {
 // CHECK:STDOUT:     %return: ref @F.%U.loc7_16.2 (%U) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {
-// CHECK:STDOUT:     %pair.patt: %.6 = binding_pattern pair
+// CHECK:STDOUT:     %pair.patt: %.8 = binding_pattern pair
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:     %D.ref.loc9_16: type = name_ref D, file.%D.decl [template = constants.%D]
 // CHECK:STDOUT:     %.loc9_17.1: %.3 = tuple_literal (%C.ref, %D.ref.loc9_16)
-// CHECK:STDOUT:     %.loc9_17.2: type = converted %.loc9_17.1, constants.%.6 [template = constants.%.6]
-// CHECK:STDOUT:     %pair.param: %.6 = param pair, runtime_param0
-// CHECK:STDOUT:     %pair: %.6 = bind_name pair, %pair.param
+// CHECK:STDOUT:     %.loc9_17.2: type = converted %.loc9_17.1, constants.%.8 [template = constants.%.8]
+// CHECK:STDOUT:     %pair.param: %.8 = param pair, runtime_param0
+// CHECK:STDOUT:     %pair: %.8 = bind_name pair, %pair.param
 // CHECK:STDOUT:     %D.ref.loc9_23: type = name_ref D, file.%D.decl [template = constants.%D]
 // CHECK:STDOUT:     %return: ref %D = var <return slot>
 // CHECK:STDOUT:   }
@@ -149,15 +152,28 @@ fn G(pair: (C, D)) -> D {
 // CHECK:STDOUT:   %U.loc7_16.2: type = bind_symbolic_name U, 1 [symbolic = %U.loc7_16.2 (constants.%U)]
 // CHECK:STDOUT:   %.loc7_37.3: type = tuple_type (@F.%T.loc7_6.2 (%T), @F.%U.loc7_16.2 (%U)) [symbolic = %.loc7_37.3 (constants.%.4)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc7_6.1: type, %U.loc7_16.1: type](%pair: @F.%.loc7_37.3 (%.4)) -> @F.%U.loc7_16.2 (%U);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_54.2: <specific function> = specific_function constants.%F, @F(%T.loc7_6.2, %U.loc7_16.2) [symbolic = %.loc7_54.2 (constants.%.7)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc7_6.1: type, %U.loc7_16.1: type](%pair: @F.%.loc7_37.3 (%.4)) -> @F.%U.loc7_16.2 (%U) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:     %pair.ref: @F.%.loc7_37.3 (%.4) = name_ref pair, %pair
+// CHECK:STDOUT:     %.loc7_54.1: <specific function> = specific_function %F.ref, @F(constants.%T, constants.%U) [symbolic = %.loc7_54.2 (constants.%.7)]
+// CHECK:STDOUT:     %F.call: init @F.%U.loc7_16.2 (%U) = call %.loc7_54.1(%pair.ref)
+// CHECK:STDOUT:     %.loc7_61.1: @F.%U.loc7_16.2 (%U) = value_of_initializer %F.call
+// CHECK:STDOUT:     %.loc7_61.2: @F.%U.loc7_16.2 (%U) = converted %F.call, %.loc7_61.1
+// CHECK:STDOUT:     return %.loc7_61.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G(%pair: %.6) -> %return: %D {
+// CHECK:STDOUT: fn @G(%pair: %.8) -> %return: %D {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
-// CHECK:STDOUT:   %pair.ref: %.6 = name_ref pair, %pair
+// CHECK:STDOUT:   %pair.ref: %.8 = name_ref pair, %pair
+// CHECK:STDOUT:   %.loc10: <specific function> = specific_function %F.ref, @F(constants.%C, constants.%D) [template = constants.%.12]
 // CHECK:STDOUT:   %.loc9_20: ref %D = splice_block %return {}
-// CHECK:STDOUT:   %F.call: init %D = call %F.ref(%pair.ref) to %.loc9_20
+// CHECK:STDOUT:   %F.call: init %D = call %.loc10(%pair.ref) to %.loc9_20
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -165,12 +181,24 @@ fn G(pair: (C, D)) -> D {
 // CHECK:STDOUT:   %T.loc7_6.2 => constants.%T
 // CHECK:STDOUT:   %U.loc7_16.2 => constants.%U
 // CHECK:STDOUT:   %.loc7_37.3 => constants.%.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_54.2 => constants.%.7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(@F.%T.loc7_6.2, @F.%U.loc7_16.2) {
+// CHECK:STDOUT:   %T.loc7_6.2 => constants.%T
+// CHECK:STDOUT:   %U.loc7_16.2 => constants.%U
+// CHECK:STDOUT:   %.loc7_37.3 => constants.%.4
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%C, constants.%D) {
 // CHECK:STDOUT:   %T.loc7_6.2 => constants.%C
 // CHECK:STDOUT:   %U.loc7_16.2 => constants.%D
-// CHECK:STDOUT:   %.loc7_37.3 => constants.%.6
+// CHECK:STDOUT:   %.loc7_37.3 => constants.%.8
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_54.2 => constants.%.12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- tuple_value.carbon
@@ -201,6 +229,7 @@ fn G(pair: (C, D)) -> D {
 // CHECK:STDOUT:   %HasPair.4: type = class_type @HasPair, @HasPair(%tuple.2) [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
+// CHECK:STDOUT:   %.10: <specific function> = specific_function %F, @F(%.8, %.9) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -322,7 +351,8 @@ fn G(pair: (C, D)) -> D {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:   %h.ref: %HasPair.4 = name_ref h, %h
-// CHECK:STDOUT:   %F.call: init i32 = call %F.ref(%h.ref)
+// CHECK:STDOUT:   %.loc9_10: <specific function> = specific_function %F.ref, @F(constants.%.8, constants.%.9) [template = constants.%.10]
+// CHECK:STDOUT:   %F.call: init i32 = call %.loc9_10(%h.ref)
 // CHECK:STDOUT:   %.loc9_14.1: i32 = value_of_initializer %F.call
 // CHECK:STDOUT:   %.loc9_14.2: i32 = converted %F.call, %.loc9_14.1
 // CHECK:STDOUT:   return %.loc9_14.2
@@ -360,6 +390,8 @@ fn G(pair: (C, D)) -> D {
 // CHECK:STDOUT:   %B.loc6_15.2 => constants.%.9
 // CHECK:STDOUT:   %tuple.loc6_40.2 => constants.%tuple.2
 // CHECK:STDOUT:   %HasPair.loc6_34.2 => constants.%HasPair.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_inconsistent.carbon

+ 162 - 61
toolchain/check/testdata/deduce/type_operator.carbon

@@ -14,7 +14,7 @@ library "[[@TEST_NAME]]";
 
 class C {}
 
-fn F[T:! type](p: T*) -> T;
+fn F[T:! type](p: T*) -> T { return F(p); }
 
 fn G(p: C*) -> C {
   return F(p);
@@ -26,7 +26,7 @@ library "[[@TEST_NAME]]";
 
 class C {}
 
-fn F[T:! type](p: const T*) -> T;
+fn F[T:! type](p: const T*) -> T { return F(p); }
 
 fn G(p: const C*) -> C {
   return F(p);
@@ -38,7 +38,7 @@ library "[[@TEST_NAME]]";
 
 class C {}
 
-fn F[T:! type](p: T*) -> T;
+fn F[T:! type](p: T*) -> T { return F(p); }
 
 fn G(p: const C*) -> const C {
   return F(p);
@@ -50,15 +50,15 @@ library "[[@TEST_NAME]]";
 
 class C {}
 
-fn F[T:! type](p: const T*) -> T;
+fn F[T:! type](p: const T*) -> T { return F(p); }
 
 fn G(p: C*) -> const C {
   // CHECK:STDERR: fail_const_from_nonconst.carbon:[[@LINE+6]]:10: error: cannot deduce value for generic parameter `T`
   // CHECK:STDERR:   return F(p);
   // CHECK:STDERR:          ^~
   // CHECK:STDERR: fail_const_from_nonconst.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here
-  // CHECK:STDERR: fn F[T:! type](p: const T*) -> T;
-  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fn F[T:! type](p: const T*) -> T { return F(p); }
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   return F(p);
 }
 
@@ -73,10 +73,12 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %.4: type = tuple_type () [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
-// CHECK:STDOUT:   %.5: type = ptr_type %C [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %F, @F(%T) [symbolic]
+// CHECK:STDOUT:   %.6: type = ptr_type %C [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
-// CHECK:STDOUT:   %.6: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.8: <specific function> = specific_function %F, @F(%C) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -115,12 +117,12 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:     %return: ref @F.%T.loc6_6.2 (%T) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {
-// CHECK:STDOUT:     %p.patt: %.5 = binding_pattern p
+// CHECK:STDOUT:     %p.patt: %.6 = binding_pattern p
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %C.ref.loc8_9: type = name_ref C, file.%C.decl [template = constants.%C]
-// CHECK:STDOUT:     %.loc8_10: type = ptr_type %C [template = constants.%.5]
-// CHECK:STDOUT:     %p.param: %.5 = param p, runtime_param0
-// CHECK:STDOUT:     %p: %.5 = bind_name p, %p.param
+// CHECK:STDOUT:     %.loc8_10: type = ptr_type %C [template = constants.%.6]
+// CHECK:STDOUT:     %p.param: %.6 = param p, runtime_param0
+// CHECK:STDOUT:     %p: %.6 = bind_name p, %p.param
 // CHECK:STDOUT:     %C.ref.loc8_16: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:     %return: ref %C = var <return slot>
 // CHECK:STDOUT:   }
@@ -137,26 +139,50 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %T.loc6_6.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc6_6.2 (constants.%T)]
 // CHECK:STDOUT:   %.loc6_20.2: type = ptr_type @F.%T.loc6_6.2 (%T) [symbolic = %.loc6_20.2 (constants.%.3)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc6_6.1: type](%p: @F.%.loc6_20.2 (%.3)) -> @F.%T.loc6_6.2 (%T);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_37.2: <specific function> = specific_function constants.%F, @F(%T.loc6_6.2) [symbolic = %.loc6_37.2 (constants.%.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc6_6.1: type](%p: @F.%.loc6_20.2 (%.3)) -> @F.%T.loc6_6.2 (%T) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:     %p.ref: @F.%.loc6_20.2 (%.3) = name_ref p, %p
+// CHECK:STDOUT:     %.loc6_37.1: <specific function> = specific_function %F.ref, @F(constants.%T) [symbolic = %.loc6_37.2 (constants.%.5)]
+// CHECK:STDOUT:     %F.call: init @F.%T.loc6_6.2 (%T) = call %.loc6_37.1(%p.ref)
+// CHECK:STDOUT:     %.loc6_41.1: @F.%T.loc6_6.2 (%T) = value_of_initializer %F.call
+// CHECK:STDOUT:     %.loc6_41.2: @F.%T.loc6_6.2 (%T) = converted %F.call, %.loc6_41.1
+// CHECK:STDOUT:     return %.loc6_41.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G(%p: %.5) -> %return: %C {
+// CHECK:STDOUT: fn @G(%p: %.6) -> %return: %C {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
-// CHECK:STDOUT:   %p.ref: %.5 = name_ref p, %p
+// CHECK:STDOUT:   %p.ref: %.6 = name_ref p, %p
+// CHECK:STDOUT:   %.loc9: <specific function> = specific_function %F.ref, @F(constants.%C) [template = constants.%.8]
 // CHECK:STDOUT:   %.loc8_13: ref %C = splice_block %return {}
-// CHECK:STDOUT:   %F.call: init %C = call %F.ref(%p.ref) to %.loc8_13
+// CHECK:STDOUT:   %F.call: init %C = call %.loc9(%p.ref) to %.loc8_13
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%T) {
 // CHECK:STDOUT:   %T.loc6_6.2 => constants.%T
 // CHECK:STDOUT:   %.loc6_20.2 => constants.%.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_37.2 => constants.%.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(@F.%T.loc6_6.2) {
+// CHECK:STDOUT:   %T.loc6_6.2 => constants.%T
+// CHECK:STDOUT:   %.loc6_20.2 => constants.%.3
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%C) {
 // CHECK:STDOUT:   %T.loc6_6.2 => constants.%C
-// CHECK:STDOUT:   %.loc6_20.2 => constants.%.5
+// CHECK:STDOUT:   %.loc6_20.2 => constants.%.6
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_37.2 => constants.%.8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- const.carbon
@@ -171,11 +197,13 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %.5: type = tuple_type () [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
-// CHECK:STDOUT:   %.6: type = const_type %C [template]
-// CHECK:STDOUT:   %.7: type = ptr_type %.6 [template]
+// CHECK:STDOUT:   %.6: <specific function> = specific_function %F, @F(%T) [symbolic]
+// CHECK:STDOUT:   %.7: type = const_type %C [template]
+// CHECK:STDOUT:   %.8: type = ptr_type %.7 [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
-// CHECK:STDOUT:   %.8: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.9: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.10: <specific function> = specific_function %F, @F(%C) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -215,13 +243,13 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:     %return: ref @F.%T.loc6_6.2 (%T) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {
-// CHECK:STDOUT:     %p.patt: %.7 = binding_pattern p
+// CHECK:STDOUT:     %p.patt: %.8 = binding_pattern p
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %C.ref.loc8_15: type = name_ref C, file.%C.decl [template = constants.%C]
-// CHECK:STDOUT:     %.loc8_9: type = const_type %C [template = constants.%.6]
-// CHECK:STDOUT:     %.loc8_16: type = ptr_type %.6 [template = constants.%.7]
-// CHECK:STDOUT:     %p.param: %.7 = param p, runtime_param0
-// CHECK:STDOUT:     %p: %.7 = bind_name p, %p.param
+// CHECK:STDOUT:     %.loc8_9: type = const_type %C [template = constants.%.7]
+// CHECK:STDOUT:     %.loc8_16: type = ptr_type %.7 [template = constants.%.8]
+// CHECK:STDOUT:     %p.param: %.8 = param p, runtime_param0
+// CHECK:STDOUT:     %p: %.8 = bind_name p, %p.param
 // CHECK:STDOUT:     %C.ref.loc8_22: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:     %return: ref %C = var <return slot>
 // CHECK:STDOUT:   }
@@ -239,15 +267,28 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %.loc6_19.2: type = const_type @F.%T.loc6_6.2 (%T) [symbolic = %.loc6_19.2 (constants.%.3)]
 // CHECK:STDOUT:   %.loc6_26.2: type = ptr_type @F.%.loc6_19.2 (%.3) [symbolic = %.loc6_26.2 (constants.%.4)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc6_6.1: type](%p: @F.%.loc6_26.2 (%.4)) -> @F.%T.loc6_6.2 (%T);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_43.2: <specific function> = specific_function constants.%F, @F(%T.loc6_6.2) [symbolic = %.loc6_43.2 (constants.%.6)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc6_6.1: type](%p: @F.%.loc6_26.2 (%.4)) -> @F.%T.loc6_6.2 (%T) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:     %p.ref: @F.%.loc6_26.2 (%.4) = name_ref p, %p
+// CHECK:STDOUT:     %.loc6_43.1: <specific function> = specific_function %F.ref, @F(constants.%T) [symbolic = %.loc6_43.2 (constants.%.6)]
+// CHECK:STDOUT:     %F.call: init @F.%T.loc6_6.2 (%T) = call %.loc6_43.1(%p.ref)
+// CHECK:STDOUT:     %.loc6_47.1: @F.%T.loc6_6.2 (%T) = value_of_initializer %F.call
+// CHECK:STDOUT:     %.loc6_47.2: @F.%T.loc6_6.2 (%T) = converted %F.call, %.loc6_47.1
+// CHECK:STDOUT:     return %.loc6_47.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G(%p: %.7) -> %return: %C {
+// CHECK:STDOUT: fn @G(%p: %.8) -> %return: %C {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
-// CHECK:STDOUT:   %p.ref: %.7 = name_ref p, %p
+// CHECK:STDOUT:   %p.ref: %.8 = name_ref p, %p
+// CHECK:STDOUT:   %.loc9: <specific function> = specific_function %F.ref, @F(constants.%C) [template = constants.%.10]
 // CHECK:STDOUT:   %.loc8_19: ref %C = splice_block %return {}
-// CHECK:STDOUT:   %F.call: init %C = call %F.ref(%p.ref) to %.loc8_19
+// CHECK:STDOUT:   %F.call: init %C = call %.loc9(%p.ref) to %.loc8_19
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -255,12 +296,24 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %T.loc6_6.2 => constants.%T
 // CHECK:STDOUT:   %.loc6_19.2 => constants.%.3
 // CHECK:STDOUT:   %.loc6_26.2 => constants.%.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_43.2 => constants.%.6
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(@F.%T.loc6_6.2) {
+// CHECK:STDOUT:   %T.loc6_6.2 => constants.%T
+// CHECK:STDOUT:   %.loc6_19.2 => constants.%.3
+// CHECK:STDOUT:   %.loc6_26.2 => constants.%.4
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%C) {
 // CHECK:STDOUT:   %T.loc6_6.2 => constants.%C
-// CHECK:STDOUT:   %.loc6_19.2 => constants.%.6
-// CHECK:STDOUT:   %.loc6_26.2 => constants.%.7
+// CHECK:STDOUT:   %.loc6_19.2 => constants.%.7
+// CHECK:STDOUT:   %.loc6_26.2 => constants.%.8
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_43.2 => constants.%.10
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- nonconst_from_const.carbon
@@ -274,11 +327,13 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %.4: type = tuple_type () [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
-// CHECK:STDOUT:   %.5: type = const_type %C [template]
-// CHECK:STDOUT:   %.6: type = ptr_type %.5 [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %F, @F(%T) [symbolic]
+// CHECK:STDOUT:   %.6: type = const_type %C [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.6 [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
-// CHECK:STDOUT:   %.7: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.8: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.9: <specific function> = specific_function %F, @F(%.6) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -317,16 +372,16 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:     %return: ref @F.%T.loc6_6.2 (%T) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {
-// CHECK:STDOUT:     %p.patt: %.6 = binding_pattern p
+// CHECK:STDOUT:     %p.patt: %.7 = binding_pattern p
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %C.ref.loc8_15: type = name_ref C, file.%C.decl [template = constants.%C]
-// CHECK:STDOUT:     %.loc8_9: type = const_type %C [template = constants.%.5]
-// CHECK:STDOUT:     %.loc8_16: type = ptr_type %.5 [template = constants.%.6]
-// CHECK:STDOUT:     %p.param: %.6 = param p, runtime_param0
-// CHECK:STDOUT:     %p: %.6 = bind_name p, %p.param
+// CHECK:STDOUT:     %.loc8_9: type = const_type %C [template = constants.%.6]
+// CHECK:STDOUT:     %.loc8_16: type = ptr_type %.6 [template = constants.%.7]
+// CHECK:STDOUT:     %p.param: %.7 = param p, runtime_param0
+// CHECK:STDOUT:     %p: %.7 = bind_name p, %p.param
 // CHECK:STDOUT:     %C.ref.loc8_28: type = name_ref C, file.%C.decl [template = constants.%C]
-// CHECK:STDOUT:     %.loc8_22: type = const_type %C [template = constants.%.5]
-// CHECK:STDOUT:     %return: ref %.5 = var <return slot>
+// CHECK:STDOUT:     %.loc8_22: type = const_type %C [template = constants.%.6]
+// CHECK:STDOUT:     %return: ref %.6 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -341,26 +396,50 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %T.loc6_6.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc6_6.2 (constants.%T)]
 // CHECK:STDOUT:   %.loc6_20.2: type = ptr_type @F.%T.loc6_6.2 (%T) [symbolic = %.loc6_20.2 (constants.%.3)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc6_6.1: type](%p: @F.%.loc6_20.2 (%.3)) -> @F.%T.loc6_6.2 (%T);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_37.2: <specific function> = specific_function constants.%F, @F(%T.loc6_6.2) [symbolic = %.loc6_37.2 (constants.%.5)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc6_6.1: type](%p: @F.%.loc6_20.2 (%.3)) -> @F.%T.loc6_6.2 (%T) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:     %p.ref: @F.%.loc6_20.2 (%.3) = name_ref p, %p
+// CHECK:STDOUT:     %.loc6_37.1: <specific function> = specific_function %F.ref, @F(constants.%T) [symbolic = %.loc6_37.2 (constants.%.5)]
+// CHECK:STDOUT:     %F.call: init @F.%T.loc6_6.2 (%T) = call %.loc6_37.1(%p.ref)
+// CHECK:STDOUT:     %.loc6_41.1: @F.%T.loc6_6.2 (%T) = value_of_initializer %F.call
+// CHECK:STDOUT:     %.loc6_41.2: @F.%T.loc6_6.2 (%T) = converted %F.call, %.loc6_41.1
+// CHECK:STDOUT:     return %.loc6_41.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G(%p: %.6) -> %return: %.5 {
+// CHECK:STDOUT: fn @G(%p: %.7) -> %return: %.6 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
-// CHECK:STDOUT:   %p.ref: %.6 = name_ref p, %p
-// CHECK:STDOUT:   %.loc8_19: ref %.5 = splice_block %return {}
-// CHECK:STDOUT:   %F.call: init %.5 = call %F.ref(%p.ref) to %.loc8_19
+// CHECK:STDOUT:   %p.ref: %.7 = name_ref p, %p
+// CHECK:STDOUT:   %.loc9: <specific function> = specific_function %F.ref, @F(constants.%.6) [template = constants.%.9]
+// CHECK:STDOUT:   %.loc8_19: ref %.6 = splice_block %return {}
+// CHECK:STDOUT:   %F.call: init %.6 = call %.loc9(%p.ref) to %.loc8_19
 // CHECK:STDOUT:   return %F.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%T) {
 // CHECK:STDOUT:   %T.loc6_6.2 => constants.%T
 // CHECK:STDOUT:   %.loc6_20.2 => constants.%.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_37.2 => constants.%.5
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @F(constants.%.5) {
-// CHECK:STDOUT:   %T.loc6_6.2 => constants.%.5
-// CHECK:STDOUT:   %.loc6_20.2 => constants.%.6
+// CHECK:STDOUT: specific @F(@F.%T.loc6_6.2) {
+// CHECK:STDOUT:   %T.loc6_6.2 => constants.%T
+// CHECK:STDOUT:   %.loc6_20.2 => constants.%.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(constants.%.6) {
+// CHECK:STDOUT:   %T.loc6_6.2 => constants.%.6
+// CHECK:STDOUT:   %.loc6_20.2 => constants.%.7
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_37.2 => constants.%.9
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_const_from_nonconst.carbon
@@ -375,11 +454,12 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [template]
 // CHECK:STDOUT:   %.5: type = tuple_type () [template]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [template]
-// CHECK:STDOUT:   %.6: type = ptr_type %C [template]
-// CHECK:STDOUT:   %.7: type = const_type %C [template]
+// CHECK:STDOUT:   %.6: <specific function> = specific_function %F, @F(%T) [symbolic]
+// CHECK:STDOUT:   %.7: type = ptr_type %C [template]
+// CHECK:STDOUT:   %.8: type = const_type %C [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
-// CHECK:STDOUT:   %.8: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %.9: type = ptr_type %.1 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -419,15 +499,15 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:     %return: ref @F.%T.loc6_6.2 (%T) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {
-// CHECK:STDOUT:     %p.patt: %.6 = binding_pattern p
+// CHECK:STDOUT:     %p.patt: %.7 = binding_pattern p
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %C.ref.loc8_9: type = name_ref C, file.%C.decl [template = constants.%C]
-// CHECK:STDOUT:     %.loc8_10: type = ptr_type %C [template = constants.%.6]
-// CHECK:STDOUT:     %p.param: %.6 = param p, runtime_param0
-// CHECK:STDOUT:     %p: %.6 = bind_name p, %p.param
+// CHECK:STDOUT:     %.loc8_10: type = ptr_type %C [template = constants.%.7]
+// CHECK:STDOUT:     %p.param: %.7 = param p, runtime_param0
+// CHECK:STDOUT:     %p: %.7 = bind_name p, %p.param
 // CHECK:STDOUT:     %C.ref.loc8_22: type = name_ref C, file.%C.decl [template = constants.%C]
-// CHECK:STDOUT:     %.loc8_16: type = const_type %C [template = constants.%.7]
-// CHECK:STDOUT:     %return: ref %.7 = var <return slot>
+// CHECK:STDOUT:     %.loc8_16: type = const_type %C [template = constants.%.8]
+// CHECK:STDOUT:     %return: ref %.8 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -443,13 +523,25 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %.loc6_19.2: type = const_type @F.%T.loc6_6.2 (%T) [symbolic = %.loc6_19.2 (constants.%.3)]
 // CHECK:STDOUT:   %.loc6_26.2: type = ptr_type @F.%.loc6_19.2 (%.3) [symbolic = %.loc6_26.2 (constants.%.4)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc6_6.1: type](%p: @F.%.loc6_26.2 (%.4)) -> @F.%T.loc6_6.2 (%T);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_43.2: <specific function> = specific_function constants.%F, @F(%T.loc6_6.2) [symbolic = %.loc6_43.2 (constants.%.6)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc6_6.1: type](%p: @F.%.loc6_26.2 (%.4)) -> @F.%T.loc6_6.2 (%T) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
+// CHECK:STDOUT:     %p.ref: @F.%.loc6_26.2 (%.4) = name_ref p, %p
+// CHECK:STDOUT:     %.loc6_43.1: <specific function> = specific_function %F.ref, @F(constants.%T) [symbolic = %.loc6_43.2 (constants.%.6)]
+// CHECK:STDOUT:     %F.call: init @F.%T.loc6_6.2 (%T) = call %.loc6_43.1(%p.ref)
+// CHECK:STDOUT:     %.loc6_47.1: @F.%T.loc6_6.2 (%T) = value_of_initializer %F.call
+// CHECK:STDOUT:     %.loc6_47.2: @F.%T.loc6_6.2 (%T) = converted %F.call, %.loc6_47.1
+// CHECK:STDOUT:     return %.loc6_47.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @G(%p: %.6) -> %return: %.7 {
+// CHECK:STDOUT: fn @G(%p: %.7) -> %return: %.8 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
-// CHECK:STDOUT:   %p.ref: %.6 = name_ref p, %p
+// CHECK:STDOUT:   %p.ref: %.7 = name_ref p, %p
 // CHECK:STDOUT:   return <error> to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -457,5 +549,14 @@ fn G(p: C*) -> const C {
 // CHECK:STDOUT:   %T.loc6_6.2 => constants.%T
 // CHECK:STDOUT:   %.loc6_19.2 => constants.%.3
 // CHECK:STDOUT:   %.loc6_26.2 => constants.%.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc6_43.2 => constants.%.6
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(@F.%T.loc6_6.2) {
+// CHECK:STDOUT:   %T.loc6_6.2 => constants.%T
+// CHECK:STDOUT:   %.loc6_19.2 => constants.%.3
+// CHECK:STDOUT:   %.loc6_26.2 => constants.%.4
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 141 - 62
toolchain/check/testdata/function/generic/deduce.carbon

@@ -12,7 +12,7 @@
 
 library "[[@TEST_NAME]]";
 
-fn ExplicitGenericParam(T:! type) -> T*;
+fn ExplicitGenericParam(T:! type) -> T* { return ExplicitGenericParam(T); }
 
 fn CallExplicitGenericParam() -> i32* {
   return ExplicitGenericParam(i32);
@@ -46,7 +46,7 @@ fn CallExplicitAndAlsoDeduced(n: i32) -> i32* {
 
 library "[[@TEST_NAME]]";
 
-fn ImplicitGenericParam[T:! type](x: T) -> T*;
+fn ImplicitGenericParam[T:! type](x: T) -> T* { return ImplicitGenericParam(x); }
 
 fn CallImplicitGenericParam(n: i32) -> i32* {
   return ImplicitGenericParam(n);
@@ -56,7 +56,7 @@ fn CallImplicitGenericParam(n: i32) -> i32* {
 
 library "[[@TEST_NAME]]";
 
-fn TupleParam[T:! type](x: (T, i32));
+fn TupleParam[T:! type](x: (T, i32)) {}
 
 fn CallTupleParam() {
   TupleParam((1, 2));
@@ -66,15 +66,15 @@ fn CallTupleParam() {
 
 library "[[@TEST_NAME]]";
 
-fn StructParam[T:! type](x: {.a: T, .b: i32});
+fn StructParam[T:! type](x: {.a: T, .b: i32}) {}
 
 fn CallStructParam() {
   // CHECK:STDERR: fail_todo_deduce_nested_struct.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `T`
   // CHECK:STDERR:   StructParam({.a = 1, .b = 2});
   // CHECK:STDERR:   ^~~~~~~~~~~~
   // CHECK:STDERR: fail_todo_deduce_nested_struct.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here
-  // CHECK:STDERR: fn StructParam[T:! type](x: {.a: T, .b: i32});
-  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fn StructParam[T:! type](x: {.a: T, .b: i32}) {}
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   StructParam({.a = 1, .b = 2});
 }
@@ -122,15 +122,18 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %ExplicitGenericParam.type: type = fn_type @ExplicitGenericParam [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %ExplicitGenericParam: %ExplicitGenericParam.type = struct_value () [template]
+// CHECK:STDOUT:   %.3: <specific function> = specific_function %ExplicitGenericParam, @ExplicitGenericParam(%T) [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:   %.4: type = ptr_type i32 [template]
 // CHECK:STDOUT:   %CallExplicitGenericParam.type: type = fn_type @CallExplicitGenericParam [template]
 // CHECK:STDOUT:   %CallExplicitGenericParam: %CallExplicitGenericParam.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: type = struct_type {.a: %T} [symbolic]
-// CHECK:STDOUT:   %.5: type = ptr_type %.4 [symbolic]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %ExplicitGenericParam, @ExplicitGenericParam(i32) [template]
+// CHECK:STDOUT:   %.6: type = struct_type {.a: %T} [symbolic]
+// CHECK:STDOUT:   %.7: type = ptr_type %.6 [symbolic]
 // CHECK:STDOUT:   %CallExplicitGenericParamWithGenericArg.type: type = fn_type @CallExplicitGenericParamWithGenericArg [template]
 // CHECK:STDOUT:   %CallExplicitGenericParamWithGenericArg: %CallExplicitGenericParamWithGenericArg.type = struct_value () [template]
+// CHECK:STDOUT:   %.8: <specific function> = specific_function %ExplicitGenericParam, @ExplicitGenericParam(%.6) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -161,7 +164,7 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %T.param: type = param T, runtime_param<invalid>
 // CHECK:STDOUT:     %T.loc4_25.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc4_25.2 (constants.%T)]
-// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc4_25.1 [symbolic = %T.loc4_25.2 (constants.%T)]
+// CHECK:STDOUT:     %T.ref.loc4_38: type = name_ref T, %T.loc4_25.1 [symbolic = %T.loc4_25.2 (constants.%T)]
 // CHECK:STDOUT:     %.loc4_39.1: type = ptr_type %T [symbolic = %.loc4_39.2 (constants.%.1)]
 // CHECK:STDOUT:     %return: ref @ExplicitGenericParam.%.loc4_39.2 (%.1) = var <return slot>
 // CHECK:STDOUT:   }
@@ -169,8 +172,8 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:     %int.make_type_32.loc6: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:     %.loc6_37.1: type = value_of_initializer %int.make_type_32.loc6 [template = i32]
 // CHECK:STDOUT:     %.loc6_37.2: type = converted %int.make_type_32.loc6, %.loc6_37.1 [template = i32]
-// CHECK:STDOUT:     %.loc6_37.3: type = ptr_type i32 [template = constants.%.3]
-// CHECK:STDOUT:     %return: ref %.3 = var <return slot>
+// CHECK:STDOUT:     %.loc6_37.3: type = ptr_type i32 [template = constants.%.4]
+// CHECK:STDOUT:     %return: ref %.4 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %CallExplicitGenericParamWithGenericArg.decl: %CallExplicitGenericParamWithGenericArg.type = fn_decl @CallExplicitGenericParamWithGenericArg [template = constants.%CallExplicitGenericParamWithGenericArg] {
 // CHECK:STDOUT:     %T.patt: type = symbolic_binding_pattern T, 0
@@ -178,9 +181,9 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:     %T.param: type = param T, runtime_param<invalid>
 // CHECK:STDOUT:     %T.loc10_43.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc10_43.2 (constants.%T)]
 // CHECK:STDOUT:     %T.ref.loc10: type = name_ref T, %T.loc10_43.1 [symbolic = %T.loc10_43.2 (constants.%T)]
-// CHECK:STDOUT:     %.loc10_62.1: type = struct_type {.a: %T} [symbolic = %.loc10_62.2 (constants.%.4)]
-// CHECK:STDOUT:     %.loc10_63.1: type = ptr_type %.4 [symbolic = %.loc10_63.2 (constants.%.5)]
-// CHECK:STDOUT:     %return: ref @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.5) = var <return slot>
+// CHECK:STDOUT:     %.loc10_62.1: type = struct_type {.a: %T} [symbolic = %.loc10_62.2 (constants.%.6)]
+// CHECK:STDOUT:     %.loc10_63.1: type = ptr_type %.6 [symbolic = %.loc10_63.2 (constants.%.7)]
+// CHECK:STDOUT:     %return: ref @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.7) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -188,38 +191,53 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %T.loc4_25.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_25.2 (constants.%T)]
 // CHECK:STDOUT:   %.loc4_39.2: type = ptr_type @ExplicitGenericParam.%T.loc4_25.2 (%T) [symbolic = %.loc4_39.2 (constants.%.1)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%T.loc4_25.1: type) -> @ExplicitGenericParam.%.loc4_39.2 (%.1);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc4_50.2: <specific function> = specific_function constants.%ExplicitGenericParam, @ExplicitGenericParam(%T.loc4_25.2) [symbolic = %.loc4_50.2 (constants.%.3)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%T.loc4_25.1: type) -> @ExplicitGenericParam.%.loc4_39.2 (%.1) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [template = constants.%ExplicitGenericParam]
+// CHECK:STDOUT:     %T.ref.loc4_71: type = name_ref T, %T.loc4_25.1 [symbolic = %T.loc4_25.2 (constants.%T)]
+// CHECK:STDOUT:     %.loc4_50.1: <specific function> = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(constants.%T) [symbolic = %.loc4_50.2 (constants.%.3)]
+// CHECK:STDOUT:     %ExplicitGenericParam.call: init @ExplicitGenericParam.%.loc4_39.2 (%.1) = call %.loc4_50.1()
+// CHECK:STDOUT:     %.loc4_73.1: @ExplicitGenericParam.%.loc4_39.2 (%.1) = value_of_initializer %ExplicitGenericParam.call
+// CHECK:STDOUT:     %.loc4_73.2: @ExplicitGenericParam.%.loc4_39.2 (%.1) = converted %ExplicitGenericParam.call, %.loc4_73.1
+// CHECK:STDOUT:     return %.loc4_73.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @CallExplicitGenericParam() -> %.3 {
+// CHECK:STDOUT: fn @CallExplicitGenericParam() -> %.4 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [template = constants.%ExplicitGenericParam]
 // CHECK:STDOUT:   %int.make_type_32.loc7: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc7_30.1: type = value_of_initializer %int.make_type_32.loc7 [template = i32]
 // CHECK:STDOUT:   %.loc7_30.2: type = converted %int.make_type_32.loc7, %.loc7_30.1 [template = i32]
-// CHECK:STDOUT:   %ExplicitGenericParam.call: init %.3 = call %ExplicitGenericParam.ref()
-// CHECK:STDOUT:   %.loc7_35.1: %.3 = value_of_initializer %ExplicitGenericParam.call
-// CHECK:STDOUT:   %.loc7_35.2: %.3 = converted %ExplicitGenericParam.call, %.loc7_35.1
+// CHECK:STDOUT:   %.loc7_10: <specific function> = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(i32) [template = constants.%.5]
+// CHECK:STDOUT:   %ExplicitGenericParam.call: init %.4 = call %.loc7_10()
+// CHECK:STDOUT:   %.loc7_35.1: %.4 = value_of_initializer %ExplicitGenericParam.call
+// CHECK:STDOUT:   %.loc7_35.2: %.4 = converted %ExplicitGenericParam.call, %.loc7_35.1
 // CHECK:STDOUT:   return %.loc7_35.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @CallExplicitGenericParamWithGenericArg(%T.loc10_43.1: type) {
 // CHECK:STDOUT:   %T.loc10_43.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc10_43.2 (constants.%T)]
-// CHECK:STDOUT:   %.loc10_62.2: type = struct_type {.a: @CallExplicitGenericParamWithGenericArg.%T.loc10_43.2 (%T)} [symbolic = %.loc10_62.2 (constants.%.4)]
-// CHECK:STDOUT:   %.loc10_63.2: type = ptr_type @CallExplicitGenericParamWithGenericArg.%.loc10_62.2 (%.4) [symbolic = %.loc10_63.2 (constants.%.5)]
+// CHECK:STDOUT:   %.loc10_62.2: type = struct_type {.a: @CallExplicitGenericParamWithGenericArg.%T.loc10_43.2 (%T)} [symbolic = %.loc10_62.2 (constants.%.6)]
+// CHECK:STDOUT:   %.loc10_63.2: type = ptr_type @CallExplicitGenericParamWithGenericArg.%.loc10_62.2 (%.6) [symbolic = %.loc10_63.2 (constants.%.7)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc11_10.2: <specific function> = specific_function constants.%ExplicitGenericParam, @ExplicitGenericParam(%.loc10_62.2) [symbolic = %.loc11_10.2 (constants.%.8)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%T.loc10_43.1: type) -> @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.5) {
+// CHECK:STDOUT:   fn(%T.loc10_43.1: type) -> @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.7) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [template = constants.%ExplicitGenericParam]
 // CHECK:STDOUT:     %T.ref.loc11: type = name_ref T, %T.loc10_43.1 [symbolic = %T.loc10_43.2 (constants.%T)]
-// CHECK:STDOUT:     %.loc11_37: type = struct_type {.a: %T} [symbolic = %.loc10_62.2 (constants.%.4)]
-// CHECK:STDOUT:     %ExplicitGenericParam.call: init @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.5) = call %ExplicitGenericParam.ref()
-// CHECK:STDOUT:     %.loc11_39.1: @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.5) = value_of_initializer %ExplicitGenericParam.call
-// CHECK:STDOUT:     %.loc11_39.2: @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.5) = converted %ExplicitGenericParam.call, %.loc11_39.1
+// CHECK:STDOUT:     %.loc11_37: type = struct_type {.a: %T} [symbolic = %.loc10_62.2 (constants.%.6)]
+// CHECK:STDOUT:     %.loc11_10.1: <specific function> = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(constants.%.6) [symbolic = %.loc11_10.2 (constants.%.8)]
+// CHECK:STDOUT:     %ExplicitGenericParam.call: init @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.7) = call %.loc11_10.1()
+// CHECK:STDOUT:     %.loc11_39.1: @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.7) = value_of_initializer %ExplicitGenericParam.call
+// CHECK:STDOUT:     %.loc11_39.2: @CallExplicitGenericParamWithGenericArg.%.loc10_63.2 (%.7) = converted %ExplicitGenericParam.call, %.loc11_39.1
 // CHECK:STDOUT:     return %.loc11_39.2
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
@@ -227,22 +245,41 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT: specific @ExplicitGenericParam(constants.%T) {
 // CHECK:STDOUT:   %T.loc4_25.2 => constants.%T
 // CHECK:STDOUT:   %.loc4_39.2 => constants.%.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc4_50.2 => constants.%.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ExplicitGenericParam(@ExplicitGenericParam.%T.loc4_25.2) {
+// CHECK:STDOUT:   %T.loc4_25.2 => constants.%T
+// CHECK:STDOUT:   %.loc4_39.2 => constants.%.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @ExplicitGenericParam(i32) {
 // CHECK:STDOUT:   %T.loc4_25.2 => i32
-// CHECK:STDOUT:   %.loc4_39.2 => constants.%.3
+// CHECK:STDOUT:   %.loc4_39.2 => constants.%.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc4_50.2 => constants.%.5
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @CallExplicitGenericParamWithGenericArg(constants.%T) {
 // CHECK:STDOUT:   %T.loc10_43.2 => constants.%T
-// CHECK:STDOUT:   %.loc10_62.2 => constants.%.4
-// CHECK:STDOUT:   %.loc10_63.2 => constants.%.5
+// CHECK:STDOUT:   %.loc10_62.2 => constants.%.6
+// CHECK:STDOUT:   %.loc10_63.2 => constants.%.7
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @ExplicitGenericParam(constants.%.4) {
-// CHECK:STDOUT:   %T.loc4_25.2 => constants.%.4
-// CHECK:STDOUT:   %.loc4_39.2 => constants.%.5
+// CHECK:STDOUT: specific @ExplicitGenericParam(constants.%.6) {
+// CHECK:STDOUT:   %T.loc4_25.2 => constants.%.6
+// CHECK:STDOUT:   %.loc4_39.2 => constants.%.7
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc4_50.2 => constants.%.8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ExplicitGenericParam(@CallExplicitGenericParamWithGenericArg.%.loc10_62.2) {
+// CHECK:STDOUT:   %T.loc4_25.2 => constants.%.6
+// CHECK:STDOUT:   %.loc4_39.2 => constants.%.7
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_todo_explicit_vs_deduced.carbon
@@ -353,11 +390,13 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %ImplicitGenericParam.type: type = fn_type @ImplicitGenericParam [template]
 // CHECK:STDOUT:   %.2: type = tuple_type () [template]
 // CHECK:STDOUT:   %ImplicitGenericParam: %ImplicitGenericParam.type = struct_value () [template]
+// CHECK:STDOUT:   %.3: <specific function> = specific_function %ImplicitGenericParam, @ImplicitGenericParam(%T) [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:   %.4: type = ptr_type i32 [template]
 // CHECK:STDOUT:   %CallImplicitGenericParam.type: type = fn_type @CallImplicitGenericParam [template]
 // CHECK:STDOUT:   %CallImplicitGenericParam: %CallImplicitGenericParam.type = struct_value () [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %ImplicitGenericParam, @ImplicitGenericParam(i32) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -406,8 +445,8 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:     %int.make_type_32.loc6_40: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:     %.loc6_43.1: type = value_of_initializer %int.make_type_32.loc6_40 [template = i32]
 // CHECK:STDOUT:     %.loc6_43.2: type = converted %int.make_type_32.loc6_40, %.loc6_43.1 [template = i32]
-// CHECK:STDOUT:     %.loc6_43.3: type = ptr_type i32 [template = constants.%.3]
-// CHECK:STDOUT:     %return: ref %.3 = var <return slot>
+// CHECK:STDOUT:     %.loc6_43.3: type = ptr_type i32 [template = constants.%.4]
+// CHECK:STDOUT:     %return: ref %.4 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -415,29 +454,53 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %T.loc4_25.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_25.2 (constants.%T)]
 // CHECK:STDOUT:   %.loc4_45.2: type = ptr_type @ImplicitGenericParam.%T.loc4_25.2 (%T) [symbolic = %.loc4_45.2 (constants.%.1)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc4_25.1: type](%x: @ImplicitGenericParam.%T.loc4_25.2 (%T)) -> @ImplicitGenericParam.%.loc4_45.2 (%.1);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc4_56.2: <specific function> = specific_function constants.%ImplicitGenericParam, @ImplicitGenericParam(%T.loc4_25.2) [symbolic = %.loc4_56.2 (constants.%.3)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc4_25.1: type](%x: @ImplicitGenericParam.%T.loc4_25.2 (%T)) -> @ImplicitGenericParam.%.loc4_45.2 (%.1) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %ImplicitGenericParam.ref: %ImplicitGenericParam.type = name_ref ImplicitGenericParam, file.%ImplicitGenericParam.decl [template = constants.%ImplicitGenericParam]
+// CHECK:STDOUT:     %x.ref: @ImplicitGenericParam.%T.loc4_25.2 (%T) = name_ref x, %x
+// CHECK:STDOUT:     %.loc4_56.1: <specific function> = specific_function %ImplicitGenericParam.ref, @ImplicitGenericParam(constants.%T) [symbolic = %.loc4_56.2 (constants.%.3)]
+// CHECK:STDOUT:     %ImplicitGenericParam.call: init @ImplicitGenericParam.%.loc4_45.2 (%.1) = call %.loc4_56.1(%x.ref)
+// CHECK:STDOUT:     %.loc4_79.1: @ImplicitGenericParam.%.loc4_45.2 (%.1) = value_of_initializer %ImplicitGenericParam.call
+// CHECK:STDOUT:     %.loc4_79.2: @ImplicitGenericParam.%.loc4_45.2 (%.1) = converted %ImplicitGenericParam.call, %.loc4_79.1
+// CHECK:STDOUT:     return %.loc4_79.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @CallImplicitGenericParam(%n: i32) -> %.3 {
+// CHECK:STDOUT: fn @CallImplicitGenericParam(%n: i32) -> %.4 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %ImplicitGenericParam.ref: %ImplicitGenericParam.type = name_ref ImplicitGenericParam, file.%ImplicitGenericParam.decl [template = constants.%ImplicitGenericParam]
 // CHECK:STDOUT:   %n.ref: i32 = name_ref n, %n
-// CHECK:STDOUT:   %ImplicitGenericParam.call: init %.3 = call %ImplicitGenericParam.ref(%n.ref)
-// CHECK:STDOUT:   %.loc7_33.1: %.3 = value_of_initializer %ImplicitGenericParam.call
-// CHECK:STDOUT:   %.loc7_33.2: %.3 = converted %ImplicitGenericParam.call, %.loc7_33.1
+// CHECK:STDOUT:   %.loc7_10: <specific function> = specific_function %ImplicitGenericParam.ref, @ImplicitGenericParam(i32) [template = constants.%.5]
+// CHECK:STDOUT:   %ImplicitGenericParam.call: init %.4 = call %.loc7_10(%n.ref)
+// CHECK:STDOUT:   %.loc7_33.1: %.4 = value_of_initializer %ImplicitGenericParam.call
+// CHECK:STDOUT:   %.loc7_33.2: %.4 = converted %ImplicitGenericParam.call, %.loc7_33.1
 // CHECK:STDOUT:   return %.loc7_33.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @ImplicitGenericParam(constants.%T) {
 // CHECK:STDOUT:   %T.loc4_25.2 => constants.%T
 // CHECK:STDOUT:   %.loc4_45.2 => constants.%.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc4_56.2 => constants.%.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ImplicitGenericParam(@ImplicitGenericParam.%T.loc4_25.2) {
+// CHECK:STDOUT:   %T.loc4_25.2 => constants.%T
+// CHECK:STDOUT:   %.loc4_45.2 => constants.%.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @ImplicitGenericParam(i32) {
 // CHECK:STDOUT:   %T.loc4_25.2 => i32
-// CHECK:STDOUT:   %.loc4_45.2 => constants.%.3
+// CHECK:STDOUT:   %.loc4_45.2 => constants.%.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc4_56.2 => constants.%.5
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- deduce_nested_tuple.carbon
@@ -451,13 +514,15 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %.3: type = tuple_type (%T, i32) [symbolic]
 // CHECK:STDOUT:   %TupleParam.type: type = fn_type @TupleParam [template]
 // CHECK:STDOUT:   %TupleParam: %TupleParam.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: type = ptr_type %.3 [symbolic]
 // CHECK:STDOUT:   %CallTupleParam.type: type = fn_type @CallTupleParam [template]
 // CHECK:STDOUT:   %CallTupleParam: %CallTupleParam.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: i32 = int_literal 1 [template]
-// CHECK:STDOUT:   %.5: i32 = int_literal 2 [template]
-// CHECK:STDOUT:   %.6: type = tuple_type (i32, i32) [template]
-// CHECK:STDOUT:   %.7: type = ptr_type %.6 [template]
-// CHECK:STDOUT:   %tuple: %.6 = tuple_value (%.4, %.5) [template]
+// CHECK:STDOUT:   %.5: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.6: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.7: type = tuple_type (i32, i32) [template]
+// CHECK:STDOUT:   %.8: <specific function> = specific_function %TupleParam, @TupleParam(i32) [template]
+// CHECK:STDOUT:   %.9: type = ptr_type %.7 [template]
+// CHECK:STDOUT:   %tuple: %.7 = tuple_value (%.5, %.6) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -506,18 +571,24 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %T.loc4_15.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_15.2 (constants.%T)]
 // CHECK:STDOUT:   %.loc4_35.5: type = tuple_type (@TupleParam.%T.loc4_15.2 (%T), i32) [symbolic = %.loc4_35.5 (constants.%.3)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc4_15.1: type](%x: @TupleParam.%.loc4_35.5 (%.3));
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc4_15.1: type](%x: @TupleParam.%.loc4_35.5 (%.3)) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @CallTupleParam() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %TupleParam.ref: %TupleParam.type = name_ref TupleParam, file.%TupleParam.decl [template = constants.%TupleParam]
-// CHECK:STDOUT:   %.loc7_15: i32 = int_literal 1 [template = constants.%.4]
-// CHECK:STDOUT:   %.loc7_18: i32 = int_literal 2 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc7_19: %.6 = tuple_literal (%.loc7_15, %.loc7_18)
-// CHECK:STDOUT:   %tuple: %.6 = tuple_value (%.loc7_15, %.loc7_18) [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc7_13: %.6 = converted %.loc7_19, %tuple [template = constants.%tuple]
-// CHECK:STDOUT:   %TupleParam.call: init %.1 = call %TupleParam.ref(%.loc7_13)
+// CHECK:STDOUT:   %.loc7_15: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc7_18: i32 = int_literal 2 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc7_19: %.7 = tuple_literal (%.loc7_15, %.loc7_18)
+// CHECK:STDOUT:   %.loc7_3: <specific function> = specific_function %TupleParam.ref, @TupleParam(i32) [template = constants.%.8]
+// CHECK:STDOUT:   %tuple: %.7 = tuple_value (%.loc7_15, %.loc7_18) [template = constants.%tuple]
+// CHECK:STDOUT:   %.loc7_13: %.7 = converted %.loc7_19, %tuple [template = constants.%tuple]
+// CHECK:STDOUT:   %TupleParam.call: init %.1 = call %.loc7_3(%.loc7_13)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -528,7 +599,9 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @TupleParam(i32) {
 // CHECK:STDOUT:   %T.loc4_15.2 => i32
-// CHECK:STDOUT:   %.loc4_35.5 => constants.%.6
+// CHECK:STDOUT:   %.loc4_35.5 => constants.%.7
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_todo_deduce_nested_struct.carbon
@@ -541,11 +614,12 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %.2: type = struct_type {.a: %T, .b: i32} [symbolic]
 // CHECK:STDOUT:   %StructParam.type: type = fn_type @StructParam [template]
 // CHECK:STDOUT:   %StructParam: %StructParam.type = struct_value () [template]
+// CHECK:STDOUT:   %.3: type = ptr_type %.2 [symbolic]
 // CHECK:STDOUT:   %CallStructParam.type: type = fn_type @CallStructParam [template]
 // CHECK:STDOUT:   %CallStructParam: %CallStructParam.type = struct_value () [template]
-// CHECK:STDOUT:   %.3: i32 = int_literal 1 [template]
-// CHECK:STDOUT:   %.4: i32 = int_literal 2 [template]
-// CHECK:STDOUT:   %.5: type = struct_type {.a: i32, .b: i32} [template]
+// CHECK:STDOUT:   %.4: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.5: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.6: type = struct_type {.a: i32, .b: i32} [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -593,15 +667,20 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %T.loc4_16.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_16.2 (constants.%T)]
 // CHECK:STDOUT:   %.loc4_44.2: type = struct_type {.a: @StructParam.%T.loc4_16.2 (%T), .b: i32} [symbolic = %.loc4_44.2 (constants.%.2)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.loc4_16.1: type](%x: @StructParam.%.loc4_44.2 (%.2));
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc4_16.1: type](%x: @StructParam.%.loc4_44.2 (%.2)) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @CallStructParam() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %StructParam.ref: %StructParam.type = name_ref StructParam, file.%StructParam.decl [template = constants.%StructParam]
-// CHECK:STDOUT:   %.loc14_21: i32 = int_literal 1 [template = constants.%.3]
-// CHECK:STDOUT:   %.loc14_29: i32 = int_literal 2 [template = constants.%.4]
-// CHECK:STDOUT:   %.loc14_30: %.5 = struct_literal (%.loc14_21, %.loc14_29)
+// CHECK:STDOUT:   %.loc14_21: i32 = int_literal 1 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc14_29: i32 = int_literal 2 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc14_30: %.6 = struct_literal (%.loc14_21, %.loc14_29)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 89 - 45
toolchain/check/testdata/function/generic/no_prelude/call.carbon

@@ -61,15 +61,18 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:   %Function: %Function.type = struct_value () [template]
 // CHECK:STDOUT:   %CallGeneric.type: type = fn_type @CallGeneric [template]
 // CHECK:STDOUT:   %CallGeneric: %CallGeneric.type = struct_value () [template]
-// CHECK:STDOUT:   %.2: type = ptr_type %T [symbolic]
+// CHECK:STDOUT:   %.2: <specific function> = specific_function %Function, @Function(%T) [symbolic]
+// CHECK:STDOUT:   %.3: type = ptr_type %T [symbolic]
 // CHECK:STDOUT:   %CallGenericPtr.type: type = fn_type @CallGenericPtr [template]
 // CHECK:STDOUT:   %CallGenericPtr: %CallGenericPtr.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: <specific function> = specific_function %Function, @Function(%.3) [symbolic]
 // CHECK:STDOUT:   %C: type = class_type @C [template]
-// CHECK:STDOUT:   %.3: type = struct_type {} [template]
-// CHECK:STDOUT:   %.4: <witness> = complete_type_witness %.3 [template]
+// CHECK:STDOUT:   %.5: type = struct_type {} [template]
+// CHECK:STDOUT:   %.6: <witness> = complete_type_witness %.5 [template]
 // CHECK:STDOUT:   %CallSpecific.type: type = fn_type @CallSpecific [template]
 // CHECK:STDOUT:   %CallSpecific: %CallSpecific.type = struct_value () [template]
-// CHECK:STDOUT:   %.5: type = ptr_type %.3 [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.5 [template]
+// CHECK:STDOUT:   %.8: <specific function> = specific_function %Function, @Function(%C) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -106,17 +109,17 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %CallGenericPtr.decl: %CallGenericPtr.type = fn_decl @CallGenericPtr [template = constants.%CallGenericPtr] {
 // CHECK:STDOUT:     %T.patt: type = symbolic_binding_pattern T, 0
-// CHECK:STDOUT:     %x.patt: @CallGenericPtr.%.loc12_33.2 (%.2) = binding_pattern x
+// CHECK:STDOUT:     %x.patt: @CallGenericPtr.%.loc12_33.2 (%.3) = binding_pattern x
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %T.param: type = param T, runtime_param<invalid>
 // CHECK:STDOUT:     %T.loc12_19.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc12_19.2 (constants.%T)]
 // CHECK:STDOUT:     %T.ref.loc12_32: type = name_ref T, %T.loc12_19.1 [symbolic = %T.loc12_19.2 (constants.%T)]
-// CHECK:STDOUT:     %.loc12_33.1: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.2)]
-// CHECK:STDOUT:     %x.param: @CallGenericPtr.%.loc12_33.2 (%.2) = param x, runtime_param0
-// CHECK:STDOUT:     %x: @CallGenericPtr.%.loc12_33.2 (%.2) = bind_name x, %x.param
+// CHECK:STDOUT:     %.loc12_33.1: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.3)]
+// CHECK:STDOUT:     %x.param: @CallGenericPtr.%.loc12_33.2 (%.3) = param x, runtime_param0
+// CHECK:STDOUT:     %x: @CallGenericPtr.%.loc12_33.2 (%.3) = bind_name x, %x.param
 // CHECK:STDOUT:     %T.ref.loc12_39: type = name_ref T, %T.loc12_19.1 [symbolic = %T.loc12_19.2 (constants.%T)]
-// CHECK:STDOUT:     %.loc12_40: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.2)]
-// CHECK:STDOUT:     %return: ref @CallGenericPtr.%.loc12_33.2 (%.2) = var <return slot>
+// CHECK:STDOUT:     %.loc12_40: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.3)]
+// CHECK:STDOUT:     %return: ref @CallGenericPtr.%.loc12_33.2 (%.3) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {} {}
 // CHECK:STDOUT:   %CallSpecific.decl: %CallSpecific.type = fn_decl @CallSpecific [template = constants.%CallSpecific] {
@@ -131,7 +134,7 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
-// CHECK:STDOUT:   %.loc16: <witness> = complete_type_witness %.3 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc16: <witness> = complete_type_witness %.5 [template = constants.%.6]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%C
@@ -153,13 +156,15 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:   %T.loc8_16.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc8_16.2 (constants.%T)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc9_10.2: <specific function> = specific_function constants.%Function, @Function(%T.loc8_16.2) [symbolic = %.loc9_10.2 (constants.%.2)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%T.loc8_16.1: type, %x: @CallGeneric.%T.loc8_16.2 (%T)) -> @CallGeneric.%T.loc8_16.2 (%T) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %Function.ref: %Function.type = name_ref Function, file.%Function.decl [template = constants.%Function]
 // CHECK:STDOUT:     %T.ref.loc9: type = name_ref T, %T.loc8_16.1 [symbolic = %T.loc8_16.2 (constants.%T)]
 // CHECK:STDOUT:     %x.ref: @CallGeneric.%T.loc8_16.2 (%T) = name_ref x, %x
-// CHECK:STDOUT:     %Function.call: init @CallGeneric.%T.loc8_16.2 (%T) = call %Function.ref(%x.ref)
+// CHECK:STDOUT:     %.loc9_10.1: <specific function> = specific_function %Function.ref, @Function(constants.%T) [symbolic = %.loc9_10.2 (constants.%.2)]
+// CHECK:STDOUT:     %Function.call: init @CallGeneric.%T.loc8_16.2 (%T) = call %.loc9_10.1(%x.ref)
 // CHECK:STDOUT:     %.loc9_24.1: @CallGeneric.%T.loc8_16.2 (%T) = value_of_initializer %Function.call
 // CHECK:STDOUT:     %.loc9_24.2: @CallGeneric.%T.loc8_16.2 (%T) = converted %Function.call, %.loc9_24.1
 // CHECK:STDOUT:     return %.loc9_24.2
@@ -168,19 +173,21 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @CallGenericPtr(%T.loc12_19.1: type) {
 // CHECK:STDOUT:   %T.loc12_19.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc12_19.2 (constants.%T)]
-// CHECK:STDOUT:   %.loc12_33.2: type = ptr_type @CallGenericPtr.%T.loc12_19.2 (%T) [symbolic = %.loc12_33.2 (constants.%.2)]
+// CHECK:STDOUT:   %.loc12_33.2: type = ptr_type @CallGenericPtr.%T.loc12_19.2 (%T) [symbolic = %.loc12_33.2 (constants.%.3)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc13_10.2: <specific function> = specific_function constants.%Function, @Function(%.loc12_33.2) [symbolic = %.loc13_10.2 (constants.%.4)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%T.loc12_19.1: type, %x: @CallGenericPtr.%.loc12_33.2 (%.2)) -> @CallGenericPtr.%.loc12_33.2 (%.2) {
+// CHECK:STDOUT:   fn(%T.loc12_19.1: type, %x: @CallGenericPtr.%.loc12_33.2 (%.3)) -> @CallGenericPtr.%.loc12_33.2 (%.3) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %Function.ref: %Function.type = name_ref Function, file.%Function.decl [template = constants.%Function]
 // CHECK:STDOUT:     %T.ref.loc13: type = name_ref T, %T.loc12_19.1 [symbolic = %T.loc12_19.2 (constants.%T)]
-// CHECK:STDOUT:     %.loc13_20: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.2)]
-// CHECK:STDOUT:     %x.ref: @CallGenericPtr.%.loc12_33.2 (%.2) = name_ref x, %x
-// CHECK:STDOUT:     %Function.call: init @CallGenericPtr.%.loc12_33.2 (%.2) = call %Function.ref(%x.ref)
-// CHECK:STDOUT:     %.loc13_25.1: @CallGenericPtr.%.loc12_33.2 (%.2) = value_of_initializer %Function.call
-// CHECK:STDOUT:     %.loc13_25.2: @CallGenericPtr.%.loc12_33.2 (%.2) = converted %Function.call, %.loc13_25.1
+// CHECK:STDOUT:     %.loc13_20: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.3)]
+// CHECK:STDOUT:     %x.ref: @CallGenericPtr.%.loc12_33.2 (%.3) = name_ref x, %x
+// CHECK:STDOUT:     %.loc13_10.1: <specific function> = specific_function %Function.ref, @Function(constants.%.3) [symbolic = %.loc13_10.2 (constants.%.4)]
+// CHECK:STDOUT:     %Function.call: init @CallGenericPtr.%.loc12_33.2 (%.3) = call %.loc13_10.1(%x.ref)
+// CHECK:STDOUT:     %.loc13_25.1: @CallGenericPtr.%.loc12_33.2 (%.3) = value_of_initializer %Function.call
+// CHECK:STDOUT:     %.loc13_25.2: @CallGenericPtr.%.loc12_33.2 (%.3) = converted %Function.call, %.loc13_25.1
 // CHECK:STDOUT:     return %.loc13_25.2
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
@@ -190,30 +197,45 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:   %Function.ref: %Function.type = name_ref Function, file.%Function.decl [template = constants.%Function]
 // CHECK:STDOUT:   %C.ref.loc19: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:   %x.ref: %C = name_ref x, %x
+// CHECK:STDOUT:   %.loc19: <specific function> = specific_function %Function.ref, @Function(constants.%C) [template = constants.%.8]
 // CHECK:STDOUT:   %.loc18: ref %C = splice_block %return {}
-// CHECK:STDOUT:   %Function.call: init %C = call %Function.ref(%x.ref) to %.loc18
+// CHECK:STDOUT:   %Function.call: init %C = call %.loc19(%x.ref) to %.loc18
 // CHECK:STDOUT:   return %Function.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Function(constants.%T) {
 // CHECK:STDOUT:   %T.loc4_13.2 => constants.%T
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @CallGeneric(constants.%T) {
 // CHECK:STDOUT:   %T.loc8_16.2 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @Function(@CallGeneric.%T.loc8_16.2) {
+// CHECK:STDOUT:   %T.loc4_13.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @CallGenericPtr(constants.%T) {
 // CHECK:STDOUT:   %T.loc12_19.2 => constants.%T
-// CHECK:STDOUT:   %.loc12_33.2 => constants.%.2
+// CHECK:STDOUT:   %.loc12_33.2 => constants.%.3
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @Function(constants.%.2) {
-// CHECK:STDOUT:   %T.loc4_13.2 => constants.%.2
+// CHECK:STDOUT: specific @Function(constants.%.3) {
+// CHECK:STDOUT:   %T.loc4_13.2 => constants.%.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Function(@CallGenericPtr.%.loc12_33.2) {
+// CHECK:STDOUT:   %T.loc4_13.2 => constants.%.3
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Function(constants.%C) {
 // CHECK:STDOUT:   %T.loc4_13.2 => constants.%C
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- deduced.carbon
@@ -225,15 +247,18 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:   %Function: %Function.type = struct_value () [template]
 // CHECK:STDOUT:   %CallGeneric.type: type = fn_type @CallGeneric [template]
 // CHECK:STDOUT:   %CallGeneric: %CallGeneric.type = struct_value () [template]
-// CHECK:STDOUT:   %.2: type = ptr_type %T [symbolic]
+// CHECK:STDOUT:   %.2: <specific function> = specific_function %Function, @Function(%T) [symbolic]
+// CHECK:STDOUT:   %.3: type = ptr_type %T [symbolic]
 // CHECK:STDOUT:   %CallGenericPtr.type: type = fn_type @CallGenericPtr [template]
 // CHECK:STDOUT:   %CallGenericPtr: %CallGenericPtr.type = struct_value () [template]
+// CHECK:STDOUT:   %.4: <specific function> = specific_function %Function, @Function(%.3) [symbolic]
 // CHECK:STDOUT:   %C: type = class_type @C [template]
-// CHECK:STDOUT:   %.3: type = struct_type {} [template]
-// CHECK:STDOUT:   %.4: <witness> = complete_type_witness %.3 [template]
+// CHECK:STDOUT:   %.5: type = struct_type {} [template]
+// CHECK:STDOUT:   %.6: <witness> = complete_type_witness %.5 [template]
 // CHECK:STDOUT:   %CallSpecific.type: type = fn_type @CallSpecific [template]
 // CHECK:STDOUT:   %CallSpecific: %CallSpecific.type = struct_value () [template]
-// CHECK:STDOUT:   %.5: type = ptr_type %.3 [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.5 [template]
+// CHECK:STDOUT:   %.8: <specific function> = specific_function %Function, @Function(%C) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -270,17 +295,17 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %CallGenericPtr.decl: %CallGenericPtr.type = fn_decl @CallGenericPtr [template = constants.%CallGenericPtr] {
 // CHECK:STDOUT:     %T.patt: type = symbolic_binding_pattern T, 0
-// CHECK:STDOUT:     %x.patt: @CallGenericPtr.%.loc12_33.2 (%.2) = binding_pattern x
+// CHECK:STDOUT:     %x.patt: @CallGenericPtr.%.loc12_33.2 (%.3) = binding_pattern x
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %T.param: type = param T, runtime_param<invalid>
 // CHECK:STDOUT:     %T.loc12_19.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc12_19.2 (constants.%T)]
 // CHECK:STDOUT:     %T.ref.loc12_32: type = name_ref T, %T.loc12_19.1 [symbolic = %T.loc12_19.2 (constants.%T)]
-// CHECK:STDOUT:     %.loc12_33.1: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.2)]
-// CHECK:STDOUT:     %x.param: @CallGenericPtr.%.loc12_33.2 (%.2) = param x, runtime_param0
-// CHECK:STDOUT:     %x: @CallGenericPtr.%.loc12_33.2 (%.2) = bind_name x, %x.param
+// CHECK:STDOUT:     %.loc12_33.1: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.3)]
+// CHECK:STDOUT:     %x.param: @CallGenericPtr.%.loc12_33.2 (%.3) = param x, runtime_param0
+// CHECK:STDOUT:     %x: @CallGenericPtr.%.loc12_33.2 (%.3) = bind_name x, %x.param
 // CHECK:STDOUT:     %T.ref.loc12_39: type = name_ref T, %T.loc12_19.1 [symbolic = %T.loc12_19.2 (constants.%T)]
-// CHECK:STDOUT:     %.loc12_40: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.2)]
-// CHECK:STDOUT:     %return: ref @CallGenericPtr.%.loc12_33.2 (%.2) = var <return slot>
+// CHECK:STDOUT:     %.loc12_40: type = ptr_type %T [symbolic = %.loc12_33.2 (constants.%.3)]
+// CHECK:STDOUT:     %return: ref @CallGenericPtr.%.loc12_33.2 (%.3) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {} {}
 // CHECK:STDOUT:   %CallSpecific.decl: %CallSpecific.type = fn_decl @CallSpecific [template = constants.%CallSpecific] {
@@ -295,7 +320,7 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
-// CHECK:STDOUT:   %.loc16: <witness> = complete_type_witness %.3 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc16: <witness> = complete_type_witness %.5 [template = constants.%.6]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%C
@@ -317,12 +342,14 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:   %T.loc8_16.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc8_16.2 (constants.%T)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc9_10.2: <specific function> = specific_function constants.%Function, @Function(%T.loc8_16.2) [symbolic = %.loc9_10.2 (constants.%.2)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%T.loc8_16.1: type, %x: @CallGeneric.%T.loc8_16.2 (%T)) -> @CallGeneric.%T.loc8_16.2 (%T) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %Function.ref: %Function.type = name_ref Function, file.%Function.decl [template = constants.%Function]
 // CHECK:STDOUT:     %x.ref: @CallGeneric.%T.loc8_16.2 (%T) = name_ref x, %x
-// CHECK:STDOUT:     %Function.call: init @CallGeneric.%T.loc8_16.2 (%T) = call %Function.ref(%x.ref)
+// CHECK:STDOUT:     %.loc9_10.1: <specific function> = specific_function %Function.ref, @Function(constants.%T) [symbolic = %.loc9_10.2 (constants.%.2)]
+// CHECK:STDOUT:     %Function.call: init @CallGeneric.%T.loc8_16.2 (%T) = call %.loc9_10.1(%x.ref)
 // CHECK:STDOUT:     %.loc9_21.1: @CallGeneric.%T.loc8_16.2 (%T) = value_of_initializer %Function.call
 // CHECK:STDOUT:     %.loc9_21.2: @CallGeneric.%T.loc8_16.2 (%T) = converted %Function.call, %.loc9_21.1
 // CHECK:STDOUT:     return %.loc9_21.2
@@ -331,17 +358,19 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @CallGenericPtr(%T.loc12_19.1: type) {
 // CHECK:STDOUT:   %T.loc12_19.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc12_19.2 (constants.%T)]
-// CHECK:STDOUT:   %.loc12_33.2: type = ptr_type @CallGenericPtr.%T.loc12_19.2 (%T) [symbolic = %.loc12_33.2 (constants.%.2)]
+// CHECK:STDOUT:   %.loc12_33.2: type = ptr_type @CallGenericPtr.%T.loc12_19.2 (%T) [symbolic = %.loc12_33.2 (constants.%.3)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc13_10.2: <specific function> = specific_function constants.%Function, @Function(%.loc12_33.2) [symbolic = %.loc13_10.2 (constants.%.4)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%T.loc12_19.1: type, %x: @CallGenericPtr.%.loc12_33.2 (%.2)) -> @CallGenericPtr.%.loc12_33.2 (%.2) {
+// CHECK:STDOUT:   fn(%T.loc12_19.1: type, %x: @CallGenericPtr.%.loc12_33.2 (%.3)) -> @CallGenericPtr.%.loc12_33.2 (%.3) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %Function.ref: %Function.type = name_ref Function, file.%Function.decl [template = constants.%Function]
-// CHECK:STDOUT:     %x.ref: @CallGenericPtr.%.loc12_33.2 (%.2) = name_ref x, %x
-// CHECK:STDOUT:     %Function.call: init @CallGenericPtr.%.loc12_33.2 (%.2) = call %Function.ref(%x.ref)
-// CHECK:STDOUT:     %.loc13_21.1: @CallGenericPtr.%.loc12_33.2 (%.2) = value_of_initializer %Function.call
-// CHECK:STDOUT:     %.loc13_21.2: @CallGenericPtr.%.loc12_33.2 (%.2) = converted %Function.call, %.loc13_21.1
+// CHECK:STDOUT:     %x.ref: @CallGenericPtr.%.loc12_33.2 (%.3) = name_ref x, %x
+// CHECK:STDOUT:     %.loc13_10.1: <specific function> = specific_function %Function.ref, @Function(constants.%.3) [symbolic = %.loc13_10.2 (constants.%.4)]
+// CHECK:STDOUT:     %Function.call: init @CallGenericPtr.%.loc12_33.2 (%.3) = call %.loc13_10.1(%x.ref)
+// CHECK:STDOUT:     %.loc13_21.1: @CallGenericPtr.%.loc12_33.2 (%.3) = value_of_initializer %Function.call
+// CHECK:STDOUT:     %.loc13_21.2: @CallGenericPtr.%.loc12_33.2 (%.3) = converted %Function.call, %.loc13_21.1
 // CHECK:STDOUT:     return %.loc13_21.2
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
@@ -350,29 +379,44 @@ fn CallSpecific(x: C) -> C {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Function.ref: %Function.type = name_ref Function, file.%Function.decl [template = constants.%Function]
 // CHECK:STDOUT:   %x.ref: %C = name_ref x, %x
+// CHECK:STDOUT:   %.loc19: <specific function> = specific_function %Function.ref, @Function(constants.%C) [template = constants.%.8]
 // CHECK:STDOUT:   %.loc18: ref %C = splice_block %return {}
-// CHECK:STDOUT:   %Function.call: init %C = call %Function.ref(%x.ref) to %.loc18
+// CHECK:STDOUT:   %Function.call: init %C = call %.loc19(%x.ref) to %.loc18
 // CHECK:STDOUT:   return %Function.call to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Function(constants.%T) {
 // CHECK:STDOUT:   %T.loc4_13.2 => constants.%T
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @CallGeneric(constants.%T) {
 // CHECK:STDOUT:   %T.loc8_16.2 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @Function(@CallGeneric.%T.loc8_16.2) {
+// CHECK:STDOUT:   %T.loc4_13.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @CallGenericPtr(constants.%T) {
 // CHECK:STDOUT:   %T.loc12_19.2 => constants.%T
-// CHECK:STDOUT:   %.loc12_33.2 => constants.%.2
+// CHECK:STDOUT:   %.loc12_33.2 => constants.%.3
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @Function(constants.%.2) {
-// CHECK:STDOUT:   %T.loc4_13.2 => constants.%.2
+// CHECK:STDOUT: specific @Function(constants.%.3) {
+// CHECK:STDOUT:   %T.loc4_13.2 => constants.%.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Function(@CallGenericPtr.%.loc12_33.2) {
+// CHECK:STDOUT:   %T.loc4_13.2 => constants.%.3
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Function(constants.%C) {
 // CHECK:STDOUT:   %T.loc4_13.2 => constants.%C
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 12 - 1
toolchain/check/testdata/function/generic/redeclare.carbon

@@ -97,6 +97,7 @@ fn F(U:! type, T:! type) -> U* {
 // 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: <specific function> = specific_function %F, @F(%T) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -143,12 +144,14 @@ fn F(U:! type, T:! type) -> U* {
 // CHECK:STDOUT:   %.loc4_20.2: type = ptr_type @F.%T.loc4_6.2 (%T) [symbolic = %.loc4_20.2 (constants.%.1)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_10.2: <specific function> = specific_function constants.%F, @F(%T.loc4_6.2) [symbolic = %.loc7_10.2 (constants.%.3)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%T.loc6: type) -> %.1 {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl.loc4 [template = constants.%F]
 // CHECK:STDOUT:     %T.ref.loc7: type = name_ref T, %T.loc6 [symbolic = %T.loc4_6.2 (constants.%T)]
-// CHECK:STDOUT:     %F.call: init @F.%.loc4_20.2 (%.1) = call %F.ref()
+// CHECK:STDOUT:     %.loc7_10.1: <specific function> = specific_function %F.ref, @F(constants.%T) [symbolic = %.loc7_10.2 (constants.%.3)]
+// CHECK:STDOUT:     %F.call: init @F.%.loc4_20.2 (%.1) = call %.loc7_10.1()
 // CHECK:STDOUT:     %.loc7_14.1: @F.%.loc4_20.2 (%.1) = value_of_initializer %F.call
 // CHECK:STDOUT:     %.loc7_14.2: @F.%.loc4_20.2 (%.1) = converted %F.call, %.loc7_14.1
 // CHECK:STDOUT:     return %.loc7_14.2
@@ -158,6 +161,14 @@ fn F(U:! type, T:! type) -> U* {
 // CHECK:STDOUT: specific @F(constants.%T) {
 // CHECK:STDOUT:   %T.loc4_6.2 => constants.%T
 // CHECK:STDOUT:   %.loc4_20.2 => constants.%.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_10.2 => constants.%.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(@F.%T.loc4_6.2) {
+// CHECK:STDOUT:   %T.loc4_6.2 => constants.%T
+// CHECK:STDOUT:   %.loc4_20.2 => constants.%.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_different_return_type.carbon

+ 127 - 0
toolchain/check/testdata/function/generic/resolve_used.carbon

@@ -0,0 +1,127 @@
+// 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/function/generic/resolve_used.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/resolve_used.carbon
+
+// --- fail_call_monomorphization_error.carbon
+
+library "[[@TEST_NAME]]";
+
+fn ErrorIfNIsZero(N:! i32) {
+  // Check that we resolve the definition of a used specific function by
+  // ensuring we produce an error when doing so. Notionally this error is
+  // produced as a result of instantiating the `Core.Int` template, although
+  // that's not how we currently model `Core.Int`.
+  // CHECK:STDERR: fail_call_monomorphization_error.carbon:[[@LINE+3]]:10: error: integer type width of 0 is not positive
+  // CHECK:STDERR:   var v: Core.Int(N);
+  // CHECK:STDERR:          ^~~~~~~~~
+  var v: Core.Int(N);
+}
+
+fn CallNegative() {
+  ErrorIfNIsZero(0);
+}
+
+// CHECK:STDOUT: --- fail_call_monomorphization_error.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:   %N: i32 = bind_symbolic_name N, 0 [symbolic]
+// CHECK:STDOUT:   %ErrorIfNIsZero.type: type = fn_type @ErrorIfNIsZero [template]
+// CHECK:STDOUT:   %ErrorIfNIsZero: %ErrorIfNIsZero.type = struct_value () [template]
+// CHECK:STDOUT:   %Int.type: type = fn_type @Int [template]
+// CHECK:STDOUT:   %Int: %Int.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = int_type signed, %N [symbolic]
+// CHECK:STDOUT:   %CallNegative.type: type = fn_type @CallNegative [template]
+// CHECK:STDOUT:   %CallNegative: %CallNegative.type = struct_value () [template]
+// CHECK:STDOUT:   %.3: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.4: <specific function> = specific_function %ErrorIfNIsZero, @ErrorIfNIsZero(%.3) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .Int32 = %import_ref.1
+// CHECK:STDOUT:     .Int = %import_ref.2
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/operators
+// CHECK:STDOUT:     import Core//prelude/types
+// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
+// CHECK:STDOUT:     import Core//prelude/operators/bitwise
+// CHECK:STDOUT:     import Core//prelude/operators/comparison
+// CHECK:STDOUT:     import Core//prelude/types/bool
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT:   %import_ref.2: %Int.type = import_ref Core//prelude/types, inst+15, loaded [template = constants.%Int]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .ErrorIfNIsZero = %ErrorIfNIsZero.decl
+// CHECK:STDOUT:     .CallNegative = %CallNegative.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %ErrorIfNIsZero.decl: %ErrorIfNIsZero.type = fn_decl @ErrorIfNIsZero [template = constants.%ErrorIfNIsZero] {
+// CHECK:STDOUT:     %N.patt: i32 = symbolic_binding_pattern N, 0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc4_23.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc4_23.2: type = converted %int.make_type_32, %.loc4_23.1 [template = i32]
+// CHECK:STDOUT:     %N.param: i32 = param N, runtime_param<invalid>
+// CHECK:STDOUT:     %N.loc4_19.1: i32 = bind_symbolic_name N, 0, %N.param [symbolic = %N.loc4_19.2 (constants.%N)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %CallNegative.decl: %CallNegative.type = fn_decl @CallNegative [template = constants.%CallNegative] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @ErrorIfNIsZero(%N.loc4_19.1: i32) {
+// CHECK:STDOUT:   %N.loc4_19.2: i32 = bind_symbolic_name N, 0 [symbolic = %N.loc4_19.2 (constants.%N)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc12_18: type = int_type signed, %N.loc4_19.2 [symbolic = %.loc12_18 (constants.%.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%N.loc4_19.1: i32) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %Core.ref: <namespace> = name_ref Core, imports.%Core [template = imports.%Core]
+// CHECK:STDOUT:     %Int.ref: %Int.type = name_ref Int, imports.%import_ref.2 [template = constants.%Int]
+// CHECK:STDOUT:     %N.ref: i32 = name_ref N, %N.loc4_19.1 [symbolic = %N.loc4_19.2 (constants.%N)]
+// CHECK:STDOUT:     %int.make_type_signed: init type = call %Int.ref(%N.ref) [symbolic = %.loc12_18 (constants.%.2)]
+// CHECK:STDOUT:     %.loc12_20.1: type = value_of_initializer %int.make_type_signed [symbolic = %.loc12_18 (constants.%.2)]
+// CHECK:STDOUT:     %.loc12_20.2: type = converted %int.make_type_signed, %.loc12_20.1 [symbolic = %.loc12_18 (constants.%.2)]
+// CHECK:STDOUT:     %v.var: ref @ErrorIfNIsZero.%.loc12_18 (%.2) = var v
+// CHECK:STDOUT:     %v: ref @ErrorIfNIsZero.%.loc12_18 (%.2) = bind_name v, %v.var
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int(%size: i32) -> type = "int.make_type_signed";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @CallNegative() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %ErrorIfNIsZero.ref: %ErrorIfNIsZero.type = name_ref ErrorIfNIsZero, file.%ErrorIfNIsZero.decl [template = constants.%ErrorIfNIsZero]
+// CHECK:STDOUT:   %.loc16_18: i32 = int_literal 0 [template = constants.%.3]
+// CHECK:STDOUT:   %.loc16_3: <specific function> = specific_function %ErrorIfNIsZero.ref, @ErrorIfNIsZero(constants.%.3) [template = constants.%.4]
+// CHECK:STDOUT:   %ErrorIfNIsZero.call: init %.1 = call %.loc16_3()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ErrorIfNIsZero(constants.%N) {
+// CHECK:STDOUT:   %N.loc4_19.2 => constants.%N
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ErrorIfNIsZero(constants.%.3) {
+// CHECK:STDOUT:   %N.loc4_19.2 => constants.%.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc12_18 => <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 77 - 24
toolchain/check/testdata/function/generic/return_slot.carbon

@@ -9,7 +9,7 @@
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/return_slot.carbon
 
 class Wrap(T:! type) {
-  fn Make() -> T;
+  fn Make() -> T { return Make(); }
 }
 
 class C { var arr: [i32; 100]; }
@@ -32,29 +32,33 @@ fn G() {
 // CHECK:STDOUT:   %Make.1: %Make.type.1 = struct_value () [symbolic]
 // CHECK:STDOUT:   %.2: type = struct_type {} [template]
 // CHECK:STDOUT:   %.3: <witness> = complete_type_witness %.2 [template]
+// CHECK:STDOUT:   %.4: <specific function> = specific_function %Make.1, @Make(%T) [symbolic]
 // CHECK:STDOUT:   %C: type = class_type @C [template]
 // CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
 // CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
-// CHECK:STDOUT:   %.4: i32 = int_literal 100 [template]
-// CHECK:STDOUT:   %.5: type = array_type %.4, i32 [template]
-// CHECK:STDOUT:   %.6: type = ptr_type %.5 [template]
-// CHECK:STDOUT:   %.7: type = unbound_element_type %C, %.5 [template]
-// CHECK:STDOUT:   %.8: type = struct_type {.arr: %.5} [template]
-// CHECK:STDOUT:   %.9: <witness> = complete_type_witness %.8 [template]
+// CHECK:STDOUT:   %.5: i32 = int_literal 100 [template]
+// CHECK:STDOUT:   %.6: type = array_type %.5, i32 [template]
+// CHECK:STDOUT:   %.7: type = ptr_type %.6 [template]
+// CHECK:STDOUT:   %.8: type = unbound_element_type %C, %.6 [template]
+// CHECK:STDOUT:   %.9: type = struct_type {.arr: %.6} [template]
+// CHECK:STDOUT:   %.10: <witness> = complete_type_witness %.9 [template]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
 // CHECK:STDOUT:   %Wrap.3: type = class_type @Wrap, @Wrap(i32) [template]
 // CHECK:STDOUT:   %Make.type.2: type = fn_type @Make, @Wrap(i32) [template]
 // CHECK:STDOUT:   %Make.2: %Make.type.2 = struct_value () [template]
-// CHECK:STDOUT:   %.10: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %.11: type = ptr_type %.2 [template]
+// CHECK:STDOUT:   %.12: <specific function> = specific_function %Make.2, @Make(i32) [template]
 // CHECK:STDOUT:   %Wrap.4: type = class_type @Wrap, @Wrap(%.1) [template]
 // CHECK:STDOUT:   %Make.type.3: type = fn_type @Make, @Wrap(%.1) [template]
 // CHECK:STDOUT:   %Make.3: %Make.type.3 = struct_value () [template]
-// CHECK:STDOUT:   %.11: type = struct_type {.arr: %.6} [template]
-// CHECK:STDOUT:   %.12: type = ptr_type %.8 [template]
+// CHECK:STDOUT:   %.13: <specific function> = specific_function %Make.3, @Make(%.1) [template]
+// CHECK:STDOUT:   %.14: type = struct_type {.arr: %.7} [template]
+// CHECK:STDOUT:   %.15: type = ptr_type %.9 [template]
 // CHECK:STDOUT:   %Wrap.5: type = class_type @Wrap, @Wrap(%C) [template]
 // CHECK:STDOUT:   %Make.type.4: type = fn_type @Make, @Wrap(%C) [template]
 // CHECK:STDOUT:   %Make.4: %Make.type.4 = struct_value () [template]
+// CHECK:STDOUT:   %.16: <specific function> = specific_function %Make.4, @Make(%C) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -112,12 +116,12 @@ fn G() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc15_26: i32 = int_literal 100 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc15_26: i32 = int_literal 100 [template = constants.%.5]
 // CHECK:STDOUT:   %.loc15_21.1: type = value_of_initializer %int.make_type_32 [template = i32]
 // CHECK:STDOUT:   %.loc15_21.2: type = converted %int.make_type_32, %.loc15_21.1 [template = i32]
-// CHECK:STDOUT:   %.loc15_29: type = array_type %.loc15_26, i32 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc15_18: %.7 = field_decl arr, element0 [template]
-// CHECK:STDOUT:   %.loc15_32: <witness> = complete_type_witness %.8 [template = constants.%.9]
+// CHECK:STDOUT:   %.loc15_29: type = array_type %.loc15_26, i32 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc15_18: %.8 = field_decl arr, element0 [template]
+// CHECK:STDOUT:   %.loc15_32: <witness> = complete_type_witness %.9 [template = constants.%.10]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .Self = constants.%C
@@ -127,7 +131,21 @@ fn G() {
 // CHECK:STDOUT: generic fn @Make(@Wrap.%T.loc11_12.1: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn() -> @Make.%T (%T);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Make.type: type = fn_type @Make, @Wrap(%T) [symbolic = %Make.type (constants.%Make.type.1)]
+// CHECK:STDOUT:   %Make: @Make.%Make.type (%Make.type.1) = struct_value () [symbolic = %Make (constants.%Make.1)]
+// CHECK:STDOUT:   %.loc12_27.3: <specific function> = specific_function %Make, @Make(%T) [symbolic = %.loc12_27.3 (constants.%.4)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn() -> @Make.%T (%T) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %.loc12_27.1: @Make.%Make.type (%Make.type.1) = specific_constant @Wrap.%Make.decl, @Wrap(constants.%T) [symbolic = %Make (constants.%Make.1)]
+// CHECK:STDOUT:     %Make.ref: @Make.%Make.type (%Make.type.1) = name_ref Make, %.loc12_27.1 [symbolic = %Make (constants.%Make.1)]
+// CHECK:STDOUT:     %.loc12_27.2: <specific function> = specific_function %Make.ref, @Make(constants.%T) [symbolic = %.loc12_27.3 (constants.%.4)]
+// CHECK:STDOUT:     %Make.call: init @Make.%T (%T) = call %.loc12_27.2()
+// CHECK:STDOUT:     %.loc12_33.1: @Make.%T (%T) = value_of_initializer %Make.call
+// CHECK:STDOUT:     %.loc12_33.2: @Make.%T (%T) = converted %Make.call, %.loc12_33.1
+// CHECK:STDOUT:     return %.loc12_33.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
@@ -144,9 +162,10 @@ fn G() {
 // CHECK:STDOUT:   %.loc18_20.1: type = value_of_initializer %int.make_type_32.loc18_21 [template = i32]
 // CHECK:STDOUT:   %.loc18_20.2: type = converted %int.make_type_32.loc18_21, %.loc18_20.1 [template = i32]
 // CHECK:STDOUT:   %Wrap.loc18: type = class_type @Wrap, @Wrap(i32) [template = constants.%Wrap.3]
-// CHECK:STDOUT:   %.loc18_25: %Make.type.2 = specific_constant @Wrap.%Make.decl, @Wrap(i32) [template = constants.%Make.2]
-// CHECK:STDOUT:   %Make.ref.loc18: %Make.type.2 = name_ref Make, %.loc18_25 [template = constants.%Make.2]
-// CHECK:STDOUT:   %Make.call.loc18: init i32 = call %Make.ref.loc18()
+// CHECK:STDOUT:   %.loc18_25.1: %Make.type.2 = specific_constant @Wrap.%Make.decl, @Wrap(i32) [template = constants.%Make.2]
+// CHECK:STDOUT:   %Make.ref.loc18: %Make.type.2 = name_ref Make, %.loc18_25.1 [template = constants.%Make.2]
+// CHECK:STDOUT:   %.loc18_25.2: <specific function> = specific_function %Make.ref.loc18, @Make(i32) [template = constants.%.12]
+// CHECK:STDOUT:   %Make.call.loc18: init i32 = call %.loc18_25.2()
 // CHECK:STDOUT:   assign %a.var, %Make.call.loc18
 // CHECK:STDOUT:   %.loc19_11.1: %.1 = tuple_literal ()
 // CHECK:STDOUT:   %.loc19_11.2: type = converted %.loc19_11.1, constants.%.1 [template = constants.%.1]
@@ -156,9 +175,10 @@ fn G() {
 // CHECK:STDOUT:   %.loc19_21: %.1 = tuple_literal ()
 // CHECK:STDOUT:   %.loc19_19: type = converted %.loc19_21, constants.%.1 [template = constants.%.1]
 // CHECK:STDOUT:   %Wrap.loc19: type = class_type @Wrap, @Wrap(constants.%.1) [template = constants.%Wrap.4]
-// CHECK:STDOUT:   %.loc19_23: %Make.type.3 = specific_constant @Wrap.%Make.decl, @Wrap(constants.%.1) [template = constants.%Make.3]
-// CHECK:STDOUT:   %Make.ref.loc19: %Make.type.3 = name_ref Make, %.loc19_23 [template = constants.%Make.3]
-// CHECK:STDOUT:   %Make.call.loc19: init %.1 = call %Make.ref.loc19()
+// CHECK:STDOUT:   %.loc19_23.1: %Make.type.3 = specific_constant @Wrap.%Make.decl, @Wrap(constants.%.1) [template = constants.%Make.3]
+// CHECK:STDOUT:   %Make.ref.loc19: %Make.type.3 = name_ref Make, %.loc19_23.1 [template = constants.%Make.3]
+// CHECK:STDOUT:   %.loc19_23.2: <specific function> = specific_function %Make.ref.loc19, @Make(constants.%.1) [template = constants.%.13]
+// CHECK:STDOUT:   %Make.call.loc19: init %.1 = call %.loc19_23.2()
 // CHECK:STDOUT:   assign %b.var, %Make.call.loc19
 // CHECK:STDOUT:   %C.ref.loc20_10: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:   %c.var: ref %C = var c
@@ -166,26 +186,44 @@ fn G() {
 // CHECK:STDOUT:   %Wrap.ref.loc20: %Wrap.type = name_ref Wrap, file.%Wrap.decl [template = constants.%Wrap.1]
 // CHECK:STDOUT:   %C.ref.loc20_19: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:   %Wrap.loc20: type = class_type @Wrap, @Wrap(constants.%C) [template = constants.%Wrap.5]
-// CHECK:STDOUT:   %.loc20_21: %Make.type.4 = specific_constant @Wrap.%Make.decl, @Wrap(constants.%C) [template = constants.%Make.4]
-// CHECK:STDOUT:   %Make.ref.loc20: %Make.type.4 = name_ref Make, %.loc20_21 [template = constants.%Make.4]
+// CHECK:STDOUT:   %.loc20_21.1: %Make.type.4 = specific_constant @Wrap.%Make.decl, @Wrap(constants.%C) [template = constants.%Make.4]
+// CHECK:STDOUT:   %Make.ref.loc20: %Make.type.4 = name_ref Make, %.loc20_21.1 [template = constants.%Make.4]
+// CHECK:STDOUT:   %.loc20_21.2: <specific function> = specific_function %Make.ref.loc20, @Make(constants.%C) [template = constants.%.16]
 // CHECK:STDOUT:   %.loc20_7: ref %C = splice_block %c.var {}
-// CHECK:STDOUT:   %Make.call.loc20: init %C = call %Make.ref.loc20() to %.loc20_7
+// CHECK:STDOUT:   %Make.call.loc20: init %C = call %.loc20_21.2() to %.loc20_7
 // CHECK:STDOUT:   assign %c.var, %Make.call.loc20
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Wrap(constants.%T) {
 // CHECK:STDOUT:   %T.loc11_12.2 => constants.%T
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Make.type => constants.%Make.type.1
+// CHECK:STDOUT:   %Make => constants.%Make.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Make(constants.%T) {
 // CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Make.type => constants.%Make.type.1
+// CHECK:STDOUT:   %Make => constants.%Make.1
+// CHECK:STDOUT:   %.loc12_27.3 => constants.%.4
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Wrap(@Wrap.%T.loc11_12.2) {
 // CHECK:STDOUT:   %T.loc11_12.2 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @Wrap(@Make.%T) {
+// CHECK:STDOUT:   %T.loc11_12.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Make(@Make.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @Wrap(i32) {
 // CHECK:STDOUT:   %T.loc11_12.2 => i32
 // CHECK:STDOUT:
@@ -196,6 +234,11 @@ fn G() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Make(i32) {
 // CHECK:STDOUT:   %T => i32
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Make.type => constants.%Make.type.2
+// CHECK:STDOUT:   %Make => constants.%Make.2
+// CHECK:STDOUT:   %.loc12_27.3 => constants.%.12
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Wrap(constants.%.1) {
@@ -208,6 +251,11 @@ fn G() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Make(constants.%.1) {
 // CHECK:STDOUT:   %T => constants.%.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Make.type => constants.%Make.type.3
+// CHECK:STDOUT:   %Make => constants.%Make.3
+// CHECK:STDOUT:   %.loc12_27.3 => constants.%.13
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Wrap(constants.%C) {
@@ -220,5 +268,10 @@ fn G() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Make(constants.%C) {
 // CHECK:STDOUT:   %T => constants.%C
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Make.type => constants.%Make.type.4
+// CHECK:STDOUT:   %Make => constants.%Make.4
+// CHECK:STDOUT:   %.loc12_27.3 => constants.%.16
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 332 - 0
toolchain/check/testdata/function/generic/undefined.carbon

@@ -0,0 +1,332 @@
+// 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/function/generic/undefined.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/undefined.carbon
+
+// --- call_defined.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Defined[T:! type](x: T) -> T {
+  return x;
+}
+
+fn CallDefined() -> i32 {
+  return Defined(0);
+}
+
+// --- call_defined_late.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Defined[T:! type](x: T) -> T;
+
+fn CallDefined() -> i32 {
+  return Defined(0);
+}
+
+fn Defined[T:! type](x: T) -> T {
+  return x;
+}
+
+// --- fail_call_undefined.carbon
+
+library "[[@TEST_NAME]]";
+
+fn Undefined[T:! type](x: T) -> T;
+
+fn CallUndefined() -> i32 {
+  // CHECK:STDERR: fail_call_undefined.carbon:[[@LINE+6]]:10: error: use of undefined generic function
+  // CHECK:STDERR:   return Undefined(0);
+  // CHECK:STDERR:          ^~~~~~~~~
+  // CHECK:STDERR: fail_call_undefined.carbon:[[@LINE-6]]:1: note: generic function declared here
+  // CHECK:STDERR: fn Undefined[T:! type](x: T) -> T;
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  return Undefined(0);
+}
+
+// CHECK:STDOUT: --- call_defined.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %Defined.type: type = fn_type @Defined [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Defined: %Defined.type = struct_value () [template]
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %CallDefined.type: type = fn_type @CallDefined [template]
+// CHECK:STDOUT:   %CallDefined: %CallDefined.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.3: <specific function> = specific_function %Defined, @Defined(i32) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .Int32 = %import_ref
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/operators
+// CHECK:STDOUT:     import Core//prelude/types
+// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
+// CHECK:STDOUT:     import Core//prelude/operators/bitwise
+// CHECK:STDOUT:     import Core//prelude/operators/comparison
+// CHECK:STDOUT:     import Core//prelude/types/bool
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Defined = %Defined.decl
+// CHECK:STDOUT:     .CallDefined = %CallDefined.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Defined.decl: %Defined.type = fn_decl @Defined [template = constants.%Defined] {
+// CHECK:STDOUT:     %T.patt: type = symbolic_binding_pattern T, 0
+// CHECK:STDOUT:     %x.patt: @Defined.%T.loc4_12.2 (%T) = binding_pattern x
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %T.param: type = param T, runtime_param<invalid>
+// CHECK:STDOUT:     %T.loc4_12.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc4_12.2 (constants.%T)]
+// CHECK:STDOUT:     %T.ref.loc4_25: type = name_ref T, %T.loc4_12.1 [symbolic = %T.loc4_12.2 (constants.%T)]
+// CHECK:STDOUT:     %x.param: @Defined.%T.loc4_12.2 (%T) = param x, runtime_param0
+// CHECK:STDOUT:     %x: @Defined.%T.loc4_12.2 (%T) = bind_name x, %x.param
+// CHECK:STDOUT:     %T.ref.loc4_31: type = name_ref T, %T.loc4_12.1 [symbolic = %T.loc4_12.2 (constants.%T)]
+// CHECK:STDOUT:     %return: ref @Defined.%T.loc4_12.2 (%T) = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %CallDefined.decl: %CallDefined.type = fn_decl @CallDefined [template = constants.%CallDefined] {} {
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc8_21.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc8_21.2: type = converted %int.make_type_32, %.loc8_21.1 [template = i32]
+// CHECK:STDOUT:     %return: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Defined(%T.loc4_12.1: type) {
+// CHECK:STDOUT:   %T.loc4_12.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_12.2 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc4_12.1: type](%x: @Defined.%T.loc4_12.2 (%T)) -> @Defined.%T.loc4_12.2 (%T) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %x.ref: @Defined.%T.loc4_12.2 (%T) = name_ref x, %x
+// CHECK:STDOUT:     return %x.ref
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @CallDefined() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Defined.ref: %Defined.type = name_ref Defined, file.%Defined.decl [template = constants.%Defined]
+// CHECK:STDOUT:   %.loc9_18: i32 = int_literal 0 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc9_10: <specific function> = specific_function %Defined.ref, @Defined(i32) [template = constants.%.3]
+// CHECK:STDOUT:   %Defined.call: init i32 = call %.loc9_10(%.loc9_18)
+// CHECK:STDOUT:   %.loc9_20.1: i32 = value_of_initializer %Defined.call
+// CHECK:STDOUT:   %.loc9_20.2: i32 = converted %Defined.call, %.loc9_20.1
+// CHECK:STDOUT:   return %.loc9_20.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Defined(constants.%T) {
+// CHECK:STDOUT:   %T.loc4_12.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Defined(i32) {
+// CHECK:STDOUT:   %T.loc4_12.2 => i32
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- call_defined_late.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %Defined.type: type = fn_type @Defined [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Defined: %Defined.type = struct_value () [template]
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %CallDefined.type: type = fn_type @CallDefined [template]
+// CHECK:STDOUT:   %CallDefined: %CallDefined.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.3: <specific function> = specific_function %Defined, @Defined(i32) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .Int32 = %import_ref
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/operators
+// CHECK:STDOUT:     import Core//prelude/types
+// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
+// CHECK:STDOUT:     import Core//prelude/operators/bitwise
+// CHECK:STDOUT:     import Core//prelude/operators/comparison
+// CHECK:STDOUT:     import Core//prelude/types/bool
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Defined = %Defined.decl.loc4
+// CHECK:STDOUT:     .CallDefined = %CallDefined.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Defined.decl.loc4: %Defined.type = fn_decl @Defined [template = constants.%Defined] {
+// CHECK:STDOUT:     %T.patt: type = symbolic_binding_pattern T, 0
+// CHECK:STDOUT:     %x.patt: %T = binding_pattern x
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %T.param.loc4: type = param T, runtime_param<invalid>
+// CHECK:STDOUT:     %T.loc4_12.1: type = bind_symbolic_name T, 0, %T.param.loc4 [symbolic = %T.loc4_12.2 (constants.%T)]
+// CHECK:STDOUT:     %T.ref.loc4_25: type = name_ref T, %T.loc4_12.1 [symbolic = %T.loc4_12.2 (constants.%T)]
+// CHECK:STDOUT:     %x.param.loc4: @Defined.%T.loc4_12.2 (%T) = param x, runtime_param0
+// CHECK:STDOUT:     %x.loc4: @Defined.%T.loc4_12.2 (%T) = bind_name x, %x.param.loc4
+// CHECK:STDOUT:     %T.ref.loc4_31: type = name_ref T, %T.loc4_12.1 [symbolic = %T.loc4_12.2 (constants.%T)]
+// CHECK:STDOUT:     %return.var.loc4: ref @Defined.%T.loc4_12.2 (%T) = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %CallDefined.decl: %CallDefined.type = fn_decl @CallDefined [template = constants.%CallDefined] {} {
+// CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
+// CHECK:STDOUT:     %.loc6_21.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc6_21.2: type = converted %int.make_type_32, %.loc6_21.1 [template = i32]
+// CHECK:STDOUT:     %return: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Defined.decl.loc10: %Defined.type = fn_decl @Defined [template = constants.%Defined] {
+// CHECK:STDOUT:     %T.patt: type = symbolic_binding_pattern T, 0
+// CHECK:STDOUT:     %x.patt: %T = binding_pattern x
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %T.param.loc10: type = param T, runtime_param<invalid>
+// CHECK:STDOUT:     %T.loc10: type = bind_symbolic_name T, 0, %T.param.loc10 [symbolic = constants.%T]
+// CHECK:STDOUT:     %T.ref.loc10_25: type = name_ref T, %T.loc10 [symbolic = constants.%T]
+// CHECK:STDOUT:     %x.param.loc10: %T = param x, runtime_param0
+// CHECK:STDOUT:     %x.loc10: %T = bind_name x, %x.param.loc10
+// CHECK:STDOUT:     %T.ref.loc10_31: type = name_ref T, %T.loc10 [symbolic = constants.%T]
+// CHECK:STDOUT:     %return: ref %T = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Defined(%T.loc4_12.1: type) {
+// CHECK:STDOUT:   %T.loc4_12.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_12.2 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc10: type](%x.loc10: %T) -> %T {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %x.ref: @Defined.%T.loc4_12.2 (%T) = name_ref x, %x.loc10
+// CHECK:STDOUT:     return %x.ref
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @CallDefined() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Defined.ref: %Defined.type = name_ref Defined, file.%Defined.decl.loc4 [template = constants.%Defined]
+// CHECK:STDOUT:   %.loc7_18: i32 = int_literal 0 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc7_10: <specific function> = specific_function %Defined.ref, @Defined(i32) [template = constants.%.3]
+// CHECK:STDOUT:   %Defined.call: init i32 = call %.loc7_10(%.loc7_18)
+// CHECK:STDOUT:   %.loc7_20.1: i32 = value_of_initializer %Defined.call
+// CHECK:STDOUT:   %.loc7_20.2: i32 = converted %Defined.call, %.loc7_20.1
+// CHECK:STDOUT:   return %.loc7_20.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Defined(constants.%T) {
+// CHECK:STDOUT:   %T.loc4_12.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Defined(i32) {
+// CHECK:STDOUT:   %T.loc4_12.2 => i32
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_call_undefined.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %Undefined.type: type = fn_type @Undefined [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Undefined: %Undefined.type = struct_value () [template]
+// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
+// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
+// CHECK:STDOUT:   %CallUndefined.type: type = fn_type @CallUndefined [template]
+// CHECK:STDOUT:   %CallUndefined: %CallUndefined.type = struct_value () [template]
+// CHECK:STDOUT:   %.2: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.3: <specific function> = specific_function %Undefined, @Undefined(i32) [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .Int32 = %import_ref
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/operators
+// CHECK:STDOUT:     import Core//prelude/types
+// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
+// CHECK:STDOUT:     import Core//prelude/operators/bitwise
+// CHECK:STDOUT:     import Core//prelude/operators/comparison
+// CHECK:STDOUT:     import Core//prelude/types/bool
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Undefined = %Undefined.decl
+// CHECK:STDOUT:     .CallUndefined = %CallUndefined.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Undefined.decl: %Undefined.type = fn_decl @Undefined [template = constants.%Undefined] {
+// CHECK:STDOUT:     %T.patt: type = symbolic_binding_pattern T, 0
+// CHECK:STDOUT:     %x.patt: @Undefined.%T.loc4_14.2 (%T) = binding_pattern x
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %T.param: type = param T, runtime_param<invalid>
+// CHECK:STDOUT:     %T.loc4_14.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc4_14.2 (constants.%T)]
+// CHECK:STDOUT:     %T.ref.loc4_27: type = name_ref T, %T.loc4_14.1 [symbolic = %T.loc4_14.2 (constants.%T)]
+// CHECK:STDOUT:     %x.param: @Undefined.%T.loc4_14.2 (%T) = param x, runtime_param0
+// CHECK:STDOUT:     %x: @Undefined.%T.loc4_14.2 (%T) = bind_name x, %x.param
+// CHECK:STDOUT:     %T.ref.loc4_33: type = name_ref T, %T.loc4_14.1 [symbolic = %T.loc4_14.2 (constants.%T)]
+// CHECK:STDOUT:     %return: ref @Undefined.%T.loc4_14.2 (%T) = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %CallUndefined.decl: %CallUndefined.type = fn_decl @CallUndefined [template = constants.%CallUndefined] {} {
+// 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:     %return: ref i32 = var <return slot>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @Undefined(%T.loc4_14.1: type) {
+// CHECK:STDOUT:   %T.loc4_14.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_14.2 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%T.loc4_14.1: type](%x: @Undefined.%T.loc4_14.2 (%T)) -> @Undefined.%T.loc4_14.2 (%T);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @CallUndefined() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Undefined.ref: %Undefined.type = name_ref Undefined, file.%Undefined.decl [template = constants.%Undefined]
+// CHECK:STDOUT:   %.loc13_20: i32 = int_literal 0 [template = constants.%.2]
+// CHECK:STDOUT:   %.loc13_10: <specific function> = specific_function %Undefined.ref, @Undefined(i32) [template = constants.%.3]
+// CHECK:STDOUT:   %Undefined.call: init i32 = call %.loc13_10(%.loc13_20)
+// CHECK:STDOUT:   %.loc13_22.1: i32 = value_of_initializer %Undefined.call
+// CHECK:STDOUT:   %.loc13_22.2: i32 = converted %Undefined.call, %.loc13_22.1
+// CHECK:STDOUT:   return %.loc13_22.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Undefined(constants.%T) {
+// CHECK:STDOUT:   %T.loc4_14.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Undefined(i32) {
+// CHECK:STDOUT:   %T.loc4_14.2 => i32
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 25 - 10
toolchain/check/testdata/impl/lookup/generic.carbon

@@ -141,6 +141,7 @@ fn G(x: A) {
 // CHECK:STDOUT:   %F.type.3: type = fn_type @F.2, @impl(%.5) [template]
 // CHECK:STDOUT:   %F.3: %F.type.3 = struct_value () [template]
 // CHECK:STDOUT:   %.6: <witness> = interface_witness (%F.3) [template]
+// CHECK:STDOUT:   %.7: <specific function> = specific_function %F.3, @F.2(%.5) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -230,8 +231,9 @@ fn G(x: A) {
 // CHECK:STDOUT:   %x.ref: %.5 = name_ref x, %x
 // CHECK:STDOUT:   %HasF.ref: type = name_ref HasF, file.%HasF.decl [template = constants.%HasF.type]
 // CHECK:STDOUT:   %F.ref: %.2 = name_ref F, @HasF.%.loc5 [template = constants.%.3]
-// CHECK:STDOUT:   %.loc13: %F.type.1 = interface_witness_access constants.%.6, element0 [template = constants.%F.3]
-// CHECK:STDOUT:   %F.call: init %.1 = call %.loc13()
+// CHECK:STDOUT:   %.loc13_4.1: %F.type.1 = interface_witness_access constants.%.6, element0 [template = constants.%F.3]
+// CHECK:STDOUT:   %.loc13_4.2: <specific function> = specific_function %.loc13_4.1, @F.2(constants.%.5) [template = constants.%.7]
+// CHECK:STDOUT:   %F.call: init %.1 = call %.loc13_4.2()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -263,7 +265,9 @@ fn G(x: A) {
 // CHECK:STDOUT:   %.loc8_34.2 => constants.%.6
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @F.2(constants.%.5) {}
+// CHECK:STDOUT: specific @F.2(constants.%.5) {
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- deduced_type_subst.carbon
 // CHECK:STDOUT:
@@ -404,7 +408,8 @@ fn G(x: A) {
 // CHECK:STDOUT:   %F.ref: %.2 = name_ref F, @HasF.%.loc5 [template = constants.%.3]
 // CHECK:STDOUT:   %.loc13_11.1: %F.type.1 = interface_witness_access constants.%.6, element0 [template = constants.%F.3]
 // CHECK:STDOUT:   %.loc13_11.2: <bound method> = bound_method %x.ref, %.loc13_11.1
-// CHECK:STDOUT:   %F.call: init %.5 = call %.loc13_11.2(%x.ref)
+// CHECK:STDOUT:   %.loc13_11.3: <specific function> = specific_function %.loc13_11.2, @F.2(constants.%.5)
+// CHECK:STDOUT:   %F.call: init %.5 = call %.loc13_11.3(%x.ref)
 // CHECK:STDOUT:   %.loc13_20.1: ref %.5 = temporary_storage
 // CHECK:STDOUT:   %.loc13_20.2: ref %.5 = temporary %.loc13_20.1, %F.call
 // CHECK:STDOUT:   %struct: %.5 = struct_value () [template = constants.%struct]
@@ -448,6 +453,8 @@ fn G(x: A) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F.2(constants.%.5) {
 // CHECK:STDOUT:   %T => constants.%.5
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- deduced_type_argument.carbon
@@ -476,6 +483,7 @@ fn G(x: A) {
 // CHECK:STDOUT:   %F.type.3: type = fn_type @F.2, @impl(%.4) [template]
 // CHECK:STDOUT:   %F.3: %F.type.3 = struct_value () [template]
 // CHECK:STDOUT:   %.8: <witness> = interface_witness (%F.3) [template]
+// CHECK:STDOUT:   %.9: <specific function> = specific_function %F.3, @F.2(%.4) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -590,8 +598,9 @@ fn G(x: A) {
 // CHECK:STDOUT:   %x.ref: %C.3 = name_ref x, %x
 // CHECK:STDOUT:   %HasF.ref: type = name_ref HasF, file.%HasF.decl [template = constants.%HasF.type]
 // CHECK:STDOUT:   %F.ref: %.2 = name_ref F, @HasF.%.loc5 [template = constants.%.3]
-// CHECK:STDOUT:   %.loc15: %F.type.1 = interface_witness_access constants.%.8, element0 [template = constants.%F.3]
-// CHECK:STDOUT:   %F.call: init %.1 = call %.loc15()
+// CHECK:STDOUT:   %.loc15_4.1: %F.type.1 = interface_witness_access constants.%.8, element0 [template = constants.%F.3]
+// CHECK:STDOUT:   %.loc15_4.2: <specific function> = specific_function %.loc15_4.1, @F.2(constants.%.4) [template = constants.%.9]
+// CHECK:STDOUT:   %F.call: init %.1 = call %.loc15_4.2()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -640,7 +649,9 @@ fn G(x: A) {
 // CHECK:STDOUT:   %.loc10_37.2 => constants.%.8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @F.2(constants.%.4) {}
+// CHECK:STDOUT: specific @F.2(constants.%.4) {
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- deduced_interface_argument.carbon
 // CHECK:STDOUT:
@@ -669,6 +680,7 @@ fn G(x: A) {
 // CHECK:STDOUT:   %F.type.4: type = fn_type @F.2, @impl(%.4) [template]
 // CHECK:STDOUT:   %F.4: %F.type.4 = struct_value () [template]
 // CHECK:STDOUT:   %.8: <witness> = interface_witness (%F.4) [template]
+// CHECK:STDOUT:   %.9: <specific function> = specific_function %F.4, @F.2(%.4) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -783,8 +795,9 @@ fn G(x: A) {
 // CHECK:STDOUT:   %HasF.type: type = interface_type @HasF, @HasF(constants.%.4) [template = constants.%HasF.type.3]
 // CHECK:STDOUT:   %.loc13_14: %.6 = specific_constant @HasF.%.loc5_9.1, @HasF(constants.%.4) [template = constants.%.7]
 // CHECK:STDOUT:   %F.ref: %.6 = name_ref F, %.loc13_14 [template = constants.%.7]
-// CHECK:STDOUT:   %.loc13_4: %F.type.3 = interface_witness_access constants.%.8, element0 [template = constants.%F.4]
-// CHECK:STDOUT:   %F.call: init %.1 = call %.loc13_4()
+// CHECK:STDOUT:   %.loc13_4.1: %F.type.3 = interface_witness_access constants.%.8, element0 [template = constants.%F.4]
+// CHECK:STDOUT:   %.loc13_4.2: <specific function> = specific_function %.loc13_4.1, @F.2(constants.%.4) [template = constants.%.9]
+// CHECK:STDOUT:   %F.call: init %.1 = call %.loc13_4.2()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -851,7 +864,9 @@ fn G(x: A) {
 // CHECK:STDOUT:   %.loc8_38.2 => constants.%.8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @F.2(constants.%.4) {}
+// CHECK:STDOUT: specific @F.2(constants.%.4) {
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_incomplete_deduction.carbon
 // CHECK:STDOUT:

+ 10 - 2
toolchain/check/testdata/impl/lookup/no_prelude/impl_forall.carbon

@@ -291,7 +291,8 @@ fn TestSpecific(a: A({})) -> {} {
 // CHECK:STDOUT:     %F.ref: @TestGeneric.%.loc17_17.2 (%.18) = name_ref F, %.loc17_17.1 [symbolic = %.loc17_17.3 (constants.%.19)]
 // CHECK:STDOUT:     %.loc17_11.1: @TestGeneric.%F.type.loc17_17 (%F.type.4) = interface_witness_access constants.%.20, element0 [symbolic = %F (constants.%F.5)]
 // CHECK:STDOUT:     %.loc17_11.2: <bound method> = bound_method %a.ref, %.loc17_11.1
-// CHECK:STDOUT:     %F.call: init @TestGeneric.%W.loc16_16.2 (%W) = call %.loc17_11.2(%a.ref)
+// CHECK:STDOUT:     %.loc17_11.3: <specific function> = specific_function %.loc17_11.2, @F.2(constants.%W)
+// CHECK:STDOUT:     %F.call: init @TestGeneric.%W.loc16_16.2 (%W) = call %.loc17_11.3(%a.ref)
 // CHECK:STDOUT:     %.loc17_22.1: @TestGeneric.%W.loc16_16.2 (%W) = value_of_initializer %F.call
 // CHECK:STDOUT:     %.loc17_22.2: @TestGeneric.%W.loc16_16.2 (%W) = converted %F.call, %.loc17_22.1
 // CHECK:STDOUT:     return %.loc17_22.2
@@ -309,7 +310,8 @@ fn TestSpecific(a: A({})) -> {} {
 // CHECK:STDOUT:   %F.ref: %.27 = name_ref F, %.loc21_18 [template = constants.%.28]
 // CHECK:STDOUT:   %.loc21_11.1: %F.type.6 = interface_witness_access constants.%.29, element0 [template = constants.%F.7]
 // CHECK:STDOUT:   %.loc21_11.2: <bound method> = bound_method %a.ref, %.loc21_11.1
-// CHECK:STDOUT:   %F.call: init %.21 = call %.loc21_11.2(%a.ref)
+// CHECK:STDOUT:   %.loc21_11.3: <specific function> = specific_function %.loc21_11.2, @F.2(constants.%.21)
+// CHECK:STDOUT:   %F.call: init %.21 = call %.loc21_11.3(%a.ref)
 // CHECK:STDOUT:   %.loc21_21.1: ref %.21 = temporary_storage
 // CHECK:STDOUT:   %.loc21_21.2: ref %.21 = temporary %.loc21_21.1, %F.call
 // CHECK:STDOUT:   %struct: %.21 = struct_value () [template = constants.%struct]
@@ -450,6 +452,9 @@ fn TestSpecific(a: A({})) -> {} {
 // CHECK:STDOUT: specific @F.2(constants.%W) {
 // CHECK:STDOUT:   %V => constants.%W
 // CHECK:STDOUT:   %A => constants.%A.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc12_16.3 => constants.%.14
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @I(@TestGeneric.%W.loc16_16.2) {
@@ -498,5 +503,8 @@ fn TestSpecific(a: A({})) -> {} {
 // CHECK:STDOUT: specific @F.2(constants.%.21) {
 // CHECK:STDOUT:   %V => constants.%.21
 // CHECK:STDOUT:   %A => constants.%A.5
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc12_16.3 => constants.%.22
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 27 - 11
toolchain/check/testdata/interface/no_prelude/assoc_const_in_generic.carbon

@@ -33,15 +33,20 @@ fn H() {
 // CHECK:STDOUT:   %I.type.2: type = interface_type @I, @I(%T) [symbolic]
 // CHECK:STDOUT:   %Self: %I.type.2 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %U: type = bind_symbolic_name U, 2 [symbolic]
-// CHECK:STDOUT:   %F.type: type = fn_type @F, @I(%T) [symbolic]
-// CHECK:STDOUT:   %F: %F.type = struct_value () [symbolic]
-// CHECK:STDOUT:   %.2: type = assoc_entity_type %I.type.2, %F.type [symbolic]
+// CHECK:STDOUT:   %F.type.1: type = fn_type @F, @I(%T) [symbolic]
+// CHECK:STDOUT:   %F: %F.type.1 = struct_value () [symbolic]
+// CHECK:STDOUT:   %.2: type = assoc_entity_type %I.type.2, %F.type.1 [symbolic]
 // CHECK:STDOUT:   %.3: %.2 = assoc_entity element0, @I.%F.decl [symbolic]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [template]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [template]
 // CHECK:STDOUT:   %H.type: type = fn_type @H [template]
 // CHECK:STDOUT:   %H: %H.type = struct_value () [template]
 // CHECK:STDOUT:   %.4: type = struct_type {} [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %G, @G(%.4) [template]
+// CHECK:STDOUT:   %I.type.3: type = interface_type @I, @I(%.4) [template]
+// CHECK:STDOUT:   %F.type.2: type = fn_type @F, @I(%.4) [template]
+// CHECK:STDOUT:   %.6: type = assoc_entity_type %I.type.3, %F.type.2 [template]
+// CHECK:STDOUT:   %.7: %.6 = assoc_entity element0, @I.%F.decl [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -71,14 +76,14 @@ fn H() {
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %I.type: type = interface_type @I, @I(%T.loc11_13.2) [symbolic = %I.type (constants.%I.type.2)]
 // CHECK:STDOUT:   %Self.2: %I.type.2 = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self)]
-// CHECK:STDOUT:   %F.type: type = fn_type @F, @I(%T.loc11_13.2) [symbolic = %F.type (constants.%F.type)]
-// CHECK:STDOUT:   %F: @I.%F.type (%F.type) = struct_value () [symbolic = %F (constants.%F)]
-// CHECK:STDOUT:   %.loc12_22.2: type = assoc_entity_type @I.%I.type (%I.type.2), @I.%F.type (%F.type) [symbolic = %.loc12_22.2 (constants.%.2)]
+// CHECK:STDOUT:   %F.type: type = fn_type @F, @I(%T.loc11_13.2) [symbolic = %F.type (constants.%F.type.1)]
+// CHECK:STDOUT:   %F: @I.%F.type (%F.type.1) = struct_value () [symbolic = %F (constants.%F)]
+// CHECK:STDOUT:   %.loc12_22.2: type = assoc_entity_type @I.%I.type (%I.type.2), @I.%F.type (%F.type.1) [symbolic = %.loc12_22.2 (constants.%.2)]
 // CHECK:STDOUT:   %.loc12_22.3: @I.%.loc12_22.2 (%.2) = assoc_entity element0, %F.decl [symbolic = %.loc12_22.3 (constants.%.3)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   interface {
 // CHECK:STDOUT:     %Self.1: @I.%I.type (%I.type.2) = bind_symbolic_name Self, 1 [symbolic = %Self.2 (constants.%Self)]
-// CHECK:STDOUT:     %F.decl: @I.%F.type (%F.type) = fn_decl @F [symbolic = @I.%F (constants.%F)] {
+// CHECK:STDOUT:     %F.decl: @I.%F.type (%F.type.1) = fn_decl @F [symbolic = @I.%F (constants.%F)] {
 // CHECK:STDOUT:       %U.patt: type = symbolic_binding_pattern U, 2
 // CHECK:STDOUT:     } {
 // CHECK:STDOUT:       %U.param: type = param U, runtime_param<invalid>
@@ -106,8 +111,8 @@ fn H() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %I.type.loc19_4.2: type = interface_type @I, @I(%T.loc15_6.2) [symbolic = %I.type.loc19_4.2 (constants.%I.type.2)]
-// CHECK:STDOUT:   %F.type: type = fn_type @F, @I(%T.loc15_6.2) [symbolic = %F.type (constants.%F.type)]
-// CHECK:STDOUT:   %.loc19_7.2: type = assoc_entity_type @G.%I.type.loc19_4.2 (%I.type.2), @G.%F.type (%F.type) [symbolic = %.loc19_7.2 (constants.%.2)]
+// CHECK:STDOUT:   %F.type: type = fn_type @F, @I(%T.loc15_6.2) [symbolic = %F.type (constants.%F.type.1)]
+// CHECK:STDOUT:   %.loc19_7.2: type = assoc_entity_type @G.%I.type.loc19_4.2 (%I.type.2), @G.%F.type (%F.type.1) [symbolic = %.loc19_7.2 (constants.%.2)]
 // CHECK:STDOUT:   %.loc19_7.3: @G.%.loc19_7.2 (%.2) = assoc_entity element0, @I.%F.decl [symbolic = %.loc19_7.3 (constants.%.3)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%T.loc15_6.1: type) {
@@ -126,7 +131,8 @@ fn H() {
 // CHECK:STDOUT:   %G.ref: %G.type = name_ref G, file.%G.decl [template = constants.%G]
 // CHECK:STDOUT:   %.loc23_6: %.4 = struct_literal ()
 // CHECK:STDOUT:   %.loc23_4: type = converted %.loc23_6, constants.%.4 [template = constants.%.4]
-// CHECK:STDOUT:   %G.call: init %.1 = call %G.ref()
+// CHECK:STDOUT:   %.loc23_3: <specific function> = specific_function %G.ref, @G(constants.%.4) [template = constants.%.5]
+// CHECK:STDOUT:   %G.call: init %.1 = call %.loc23_3()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -136,7 +142,7 @@ fn H() {
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %I.type => constants.%I.type.2
 // CHECK:STDOUT:   %Self.2 => constants.%Self
-// CHECK:STDOUT:   %F.type => constants.%F.type
+// CHECK:STDOUT:   %F.type => constants.%F.type.1
 // CHECK:STDOUT:   %F => constants.%F
 // CHECK:STDOUT:   %.loc12_22.2 => constants.%.2
 // CHECK:STDOUT:   %.loc12_22.3 => constants.%.3
@@ -160,5 +166,15 @@ fn H() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @G(constants.%.4) {
 // CHECK:STDOUT:   %T.loc15_6.2 => constants.%.4
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %I.type.loc19_4.2 => constants.%I.type.3
+// CHECK:STDOUT:   %F.type => constants.%F.type.2
+// CHECK:STDOUT:   %.loc19_7.2 => constants.%.6
+// CHECK:STDOUT:   %.loc19_7.3 => constants.%.7
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @I(constants.%.4) {
+// CHECK:STDOUT:   %T.loc11_13.2 => constants.%.4
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 17 - 3
toolchain/check/testdata/interface/no_prelude/generic.carbon

@@ -32,7 +32,7 @@ class C {
 
 interface WithImplicitArgs[T:! type](N:! T);
 
-fn Receive(T:! Simple(C));
+fn Receive(T:! Simple(C)) {}
 fn Pass(T:! Simple(C)) {
   Receive(T);
 }
@@ -99,6 +99,7 @@ fn G(T:! Generic(B)) {
 // 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:   %.11: <specific function> = specific_function %Receive, @Receive(%T.2) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -265,19 +266,26 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT: generic fn @Receive(%T.loc24_12.1: %Simple.type.3) {
 // CHECK:STDOUT:   %T.loc24_12.2: %Simple.type.3 = bind_symbolic_name T, 0 [symbolic = %T.loc24_12.2 (constants.%T.2)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%T.loc24_12.1: %Simple.type.3);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%T.loc24_12.1: %Simple.type.3) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @Pass(%T.loc25_9.1: %Simple.type.3) {
 // CHECK:STDOUT:   %T.loc25_9.2: %Simple.type.3 = bind_symbolic_name T, 0 [symbolic = %T.loc25_9.2 (constants.%T.2)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc26_3.2: <specific function> = specific_function constants.%Receive, @Receive(%T.loc25_9.2) [symbolic = %.loc26_3.2 (constants.%.11)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%T.loc25_9.1: %Simple.type.3) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %Receive.ref: %Receive.type = name_ref Receive, file.%Receive.decl [template = constants.%Receive]
 // CHECK:STDOUT:     %T.ref: %Simple.type.3 = name_ref T, %T.loc25_9.1 [symbolic = %T.loc25_9.2 (constants.%T.2)]
-// CHECK:STDOUT:     %Receive.call: init %.1 = call %Receive.ref()
+// CHECK:STDOUT:     %.loc26_3.1: <specific function> = specific_function %Receive.ref, @Receive(constants.%T.2) [symbolic = %.loc26_3.2 (constants.%.11)]
+// CHECK:STDOUT:     %Receive.call: init %.1 = call %.loc26_3.1()
 // CHECK:STDOUT:     return
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
@@ -329,12 +337,18 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Receive(constants.%T.2) {
 // CHECK:STDOUT:   %T.loc24_12.2 => constants.%T.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Pass(constants.%T.2) {
 // CHECK:STDOUT:   %T.loc25_9.2 => constants.%T.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @Receive(@Pass.%T.loc25_9.2) {
+// CHECK:STDOUT:   %T.loc24_12.2 => constants.%T.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_mismatched_args.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {

+ 37 - 7
toolchain/check/testdata/operators/overloaded/implicit_as.carbon

@@ -24,7 +24,7 @@ impl X as Core.ImplicitAs(i32) {
 // ... once we stop deducing `T` from the type of the second argument in this case.
 fn Sink_i32(n: i32);
 fn Sink_X(x: X);
-fn Source(T:! type) -> T;
+fn Source(T:! type) -> T { return Source(T); }
 
 fn Test() {
   Sink_i32(Source(X));
@@ -75,9 +75,12 @@ fn Test() {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
 // CHECK:STDOUT:   %Source.type: type = fn_type @Source [template]
 // CHECK:STDOUT:   %Source: %Source.type = struct_value () [template]
+// CHECK:STDOUT:   %.14: <specific function> = specific_function %Source, @Source(%T) [symbolic]
 // CHECK:STDOUT:   %Test.type: type = fn_type @Test [template]
 // CHECK:STDOUT:   %Test: %Test.type = struct_value () [template]
-// CHECK:STDOUT:   %.14: %.5 = assoc_entity element0, imports.%import_ref.7 [symbolic]
+// CHECK:STDOUT:   %.15: <specific function> = specific_function %Source, @Source(%X) [template]
+// CHECK:STDOUT:   %.16: %.5 = assoc_entity element0, imports.%import_ref.7 [symbolic]
+// CHECK:STDOUT:   %.17: <specific function> = specific_function %Source, @Source(i32) [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -96,7 +99,7 @@ fn Test() {
 // CHECK:STDOUT:   %import_ref.1: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
 // CHECK:STDOUT:   %import_ref.2: %ImplicitAs.type.1 = import_ref Core//prelude/operators/as, inst+40, loaded [template = constants.%ImplicitAs]
 // CHECK:STDOUT:   %import_ref.3 = import_ref Core//prelude/operators/as, inst+45, unloaded
-// CHECK:STDOUT:   %import_ref.4: @ImplicitAs.%.1 (%.5) = import_ref Core//prelude/operators/as, inst+63, loaded [symbolic = @ImplicitAs.%.2 (constants.%.14)]
+// CHECK:STDOUT:   %import_ref.4: @ImplicitAs.%.1 (%.5) = import_ref Core//prelude/operators/as, inst+63, loaded [symbolic = @ImplicitAs.%.2 (constants.%.16)]
 // CHECK:STDOUT:   %import_ref.5: @ImplicitAs.%Convert.type (%Convert.type.1) = import_ref Core//prelude/operators/as, inst+56, loaded [symbolic = @ImplicitAs.%Convert (constants.%Convert.1)]
 // CHECK:STDOUT:   %import_ref.6 = import_ref Core//prelude/operators/as, inst+56, unloaded
 // CHECK:STDOUT:   %import_ref.7 = import_ref Core//prelude/operators/as, inst+56, unloaded
@@ -152,7 +155,7 @@ fn Test() {
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %T.param: type = param T, runtime_param<invalid>
 // CHECK:STDOUT:     %T.loc27_11.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc27_11.2 (constants.%T)]
-// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc27_11.1 [symbolic = %T.loc27_11.2 (constants.%T)]
+// CHECK:STDOUT:     %T.ref.loc27_24: type = name_ref T, %T.loc27_11.1 [symbolic = %T.loc27_11.2 (constants.%T)]
 // CHECK:STDOUT:     %return: ref @Source.%T.loc27_11.2 (%T) = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Test.decl: %Test.type = fn_decl @Test [template = constants.%Test] {} {}
@@ -264,7 +267,19 @@ fn Test() {
 // CHECK:STDOUT: generic fn @Source(%T.loc27_11.1: type) {
 // CHECK:STDOUT:   %T.loc27_11.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc27_11.2 (constants.%T)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%T.loc27_11.1: type) -> @Source.%T.loc27_11.2 (%T);
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc27_35.2: <specific function> = specific_function constants.%Source, @Source(%T.loc27_11.2) [symbolic = %.loc27_35.2 (constants.%.14)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%T.loc27_11.1: type) -> @Source.%T.loc27_11.2 (%T) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %Source.ref: %Source.type = name_ref Source, file.%Source.decl [template = constants.%Source]
+// CHECK:STDOUT:     %T.ref.loc27_42: type = name_ref T, %T.loc27_11.1 [symbolic = %T.loc27_11.2 (constants.%T)]
+// CHECK:STDOUT:     %.loc27_35.1: <specific function> = specific_function %Source.ref, @Source(constants.%T) [symbolic = %.loc27_35.2 (constants.%.14)]
+// CHECK:STDOUT:     %Source.call: init @Source.%T.loc27_11.2 (%T) = call %.loc27_35.1()
+// CHECK:STDOUT:     %.loc27_44.1: @Source.%T.loc27_11.2 (%T) = value_of_initializer %Source.call
+// CHECK:STDOUT:     %.loc27_44.2: @Source.%T.loc27_11.2 (%T) = converted %Source.call, %.loc27_44.1
+// CHECK:STDOUT:     return %.loc27_44.2
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Test() {
@@ -272,8 +287,9 @@ fn Test() {
 // CHECK:STDOUT:   %Sink_i32.ref: %Sink_i32.type = name_ref Sink_i32, file.%Sink_i32.decl [template = constants.%Sink_i32]
 // CHECK:STDOUT:   %Source.ref.loc30: %Source.type = name_ref Source, file.%Source.decl [template = constants.%Source]
 // CHECK:STDOUT:   %X.ref: type = name_ref X, file.%X.decl [template = constants.%X]
+// CHECK:STDOUT:   %.loc30_12: <specific function> = specific_function %Source.ref.loc30, @Source(constants.%X) [template = constants.%.15]
 // CHECK:STDOUT:   %.loc30_18.1: ref %X = temporary_storage
-// CHECK:STDOUT:   %Source.call.loc30: init %X = call %Source.ref.loc30() to %.loc30_18.1
+// CHECK:STDOUT:   %Source.call.loc30: init %X = call %.loc30_12() to %.loc30_18.1
 // CHECK:STDOUT:   %ImplicitAs.type.loc30: type = interface_type @ImplicitAs, @ImplicitAs(i32) [template = constants.%ImplicitAs.type.4]
 // CHECK:STDOUT:   %.loc30_11.1: %.11 = specific_constant imports.%import_ref.4, @ImplicitAs(i32) [template = constants.%.12]
 // CHECK:STDOUT:   %Convert.ref.loc30: %.11 = name_ref Convert, %.loc30_11.1 [template = constants.%.12]
@@ -292,7 +308,8 @@ fn Test() {
 // CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
 // CHECK:STDOUT:   %.loc31_16.1: type = value_of_initializer %int.make_type_32 [template = i32]
 // CHECK:STDOUT:   %.loc31_16.2: type = converted %int.make_type_32, %.loc31_16.1 [template = i32]
-// CHECK:STDOUT:   %Source.call.loc31: init i32 = call %Source.ref.loc31()
+// CHECK:STDOUT:   %.loc31_10: <specific function> = specific_function %Source.ref.loc31, @Source(i32) [template = constants.%.17]
+// CHECK:STDOUT:   %Source.call.loc31: init i32 = call %.loc31_10()
 // CHECK:STDOUT:   %ImplicitAs.type.loc31: type = interface_type @ImplicitAs, @ImplicitAs(constants.%X) [template = constants.%ImplicitAs.type.3]
 // CHECK:STDOUT:   %.loc31_9.1: %.7 = specific_constant imports.%import_ref.4, @ImplicitAs(constants.%X) [template = constants.%.8]
 // CHECK:STDOUT:   %Convert.ref.loc31: %.7 = name_ref Convert, %.loc31_9.1 [template = constants.%.8]
@@ -366,13 +383,26 @@ fn Test() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Source(constants.%T) {
 // CHECK:STDOUT:   %T.loc27_11.2 => constants.%T
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc27_35.2 => constants.%.14
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Source(@Source.%T.loc27_11.2) {
+// CHECK:STDOUT:   %T.loc27_11.2 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Source(constants.%X) {
 // CHECK:STDOUT:   %T.loc27_11.2 => constants.%X
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc27_35.2 => constants.%.15
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Source(i32) {
 // CHECK:STDOUT:   %T.loc27_11.2 => i32
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc27_35.2 => constants.%.17
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -159,6 +159,8 @@ CARBON_DIAGNOSTIC_KIND(RepeatedImport)
 CARBON_DIAGNOSTIC_KIND(FirstImported)
 CARBON_DIAGNOSTIC_KIND(ExportFromImpl)
 CARBON_DIAGNOSTIC_KIND(MissingDefinitionInImpl)
+CARBON_DIAGNOSTIC_KIND(MissingGenericFunctionDefinition)
+CARBON_DIAGNOSTIC_KIND(MissingGenericFunctionDefinitionHere)
 
 // Merge-related redeclaration checking.
 CARBON_DIAGNOSTIC_KIND(RedeclPrevDecl)

+ 5 - 0
toolchain/lower/constant.cpp

@@ -215,6 +215,11 @@ static auto EmitAsConstant(ConstantContext& context, SemIR::Namespace inst)
   return context.GetUnusedConstant(inst.type_id);
 }
 
+static auto EmitAsConstant(ConstantContext& /*context*/,
+                           SemIR::SpecificFunction inst) -> llvm::Constant* {
+  CARBON_FATAL("TODO: Add support: {0}", inst);
+}
+
 static auto EmitAsConstant(ConstantContext& /*context*/,
                            SemIR::StringLiteral inst) -> llvm::Constant* {
   CARBON_FATAL("TODO: Add support: {0}", inst);

+ 1 - 0
toolchain/lower/file_context.cpp

@@ -412,6 +412,7 @@ static auto BuildTypeForInst(FileContext& context, SemIR::BuiltinInst inst)
       // storage
       // (`i8`) versus for `bool` values (`i1`).
       return llvm::Type::getInt1Ty(context.llvm_context());
+    case SemIR::BuiltinInstKind::SpecificFunctionType:
     case SemIR::BuiltinInstKind::StringType:
       // TODO: Decide how we want to represent `StringType`.
       return llvm::PointerType::get(context.llvm_context(), 0);

+ 3 - 0
toolchain/sem_ir/builtin_inst_kind.def

@@ -67,6 +67,9 @@ CARBON_SEM_IR_BUILTIN_INST_KIND(StringType, "String")
 // The type of bound method values.
 CARBON_SEM_IR_BUILTIN_INST_KIND(BoundMethodType, "<bound method>")
 
+// The type of specific functions.
+CARBON_SEM_IR_BUILTIN_INST_KIND(SpecificFunctionType, "<specific function>")
+
 // The type of namespace and imported package names.
 CARBON_SEM_IR_BUILTIN_INST_KIND(NamespaceType, "<namespace>")
 

+ 1 - 0
toolchain/sem_ir/file.cpp

@@ -270,6 +270,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
       case IntType::Kind:
       case Param::Kind:
       case PointerType::Kind:
+      case SpecificFunction::Kind:
       case StringLiteral::Kind:
       case StructValue::Kind:
       case StructType::Kind:

+ 9 - 2
toolchain/sem_ir/function.cpp

@@ -12,10 +12,17 @@ namespace Carbon::SemIR {
 
 auto GetCalleeFunction(const File& sem_ir, InstId callee_id) -> CalleeFunction {
   CalleeFunction result = {.function_id = FunctionId::Invalid,
-                           .specific_id = SpecificId::Invalid,
+                           .enclosing_specific_id = SpecificId::Invalid,
+                           .resolved_specific_id = SpecificId::Invalid,
                            .self_id = InstId::Invalid,
                            .is_error = false};
 
+  if (auto specific_function =
+          sem_ir.insts().TryGetAs<SpecificFunction>(callee_id)) {
+    result.resolved_specific_id = specific_function->specific_id;
+    callee_id = specific_function->callee_id;
+  }
+
   if (auto bound_method = sem_ir.insts().TryGetAs<BoundMethod>(callee_id)) {
     result.self_id = bound_method->object_id;
     callee_id = bound_method->function_id;
@@ -38,7 +45,7 @@ auto GetCalleeFunction(const File& sem_ir, InstId callee_id) -> CalleeFunction {
   }
 
   result.function_id = fn_type->function_id;
-  result.specific_id = fn_type->specific_id;
+  result.enclosing_specific_id = fn_type->specific_id;
   return result;
 }
 

+ 3 - 1
toolchain/sem_ir/function.h

@@ -93,7 +93,9 @@ struct CalleeFunction {
   // The function. Invalid if not a function.
   SemIR::FunctionId function_id;
   // The specific that contains the function.
-  SemIR::SpecificId specific_id;
+  SemIR::SpecificId enclosing_specific_id;
+  // The specific for the callee itself, in a resolved call.
+  SemIR::SpecificId resolved_specific_id;
   // The bound `self` parameter. Invalid if not a method.
   SemIR::InstId self_id;
   // True if an error instruction was found.

+ 2 - 1
toolchain/sem_ir/inst_kind.def

@@ -64,7 +64,6 @@ CARBON_SEM_IR_INST_KIND(ImportDecl)
 CARBON_SEM_IR_INST_KIND(ImportRefUnloaded)
 CARBON_SEM_IR_INST_KIND(ImportRefLoaded)
 CARBON_SEM_IR_INST_KIND(InitializeFrom)
-CARBON_SEM_IR_INST_KIND(SpecificConstant)
 CARBON_SEM_IR_INST_KIND(InterfaceDecl)
 CARBON_SEM_IR_INST_KIND(InterfaceType)
 CARBON_SEM_IR_INST_KIND(InterfaceWitness)
@@ -80,6 +79,8 @@ CARBON_SEM_IR_INST_KIND(RequirementEquivalent)
 CARBON_SEM_IR_INST_KIND(RequirementImpls)
 CARBON_SEM_IR_INST_KIND(ReturnExpr)
 CARBON_SEM_IR_INST_KIND(Return)
+CARBON_SEM_IR_INST_KIND(SpecificConstant)
+CARBON_SEM_IR_INST_KIND(SpecificFunction)
 CARBON_SEM_IR_INST_KIND(SpliceBlock)
 CARBON_SEM_IR_INST_KIND(StringLiteral)
 CARBON_SEM_IR_INST_KIND(StructAccess)

+ 1 - 0
toolchain/sem_ir/stringify_type.cpp

@@ -305,6 +305,7 @@ auto StringifyTypeExpr(const SemIR::File& outer_sem_ir, InstId outer_inst_id)
       case RequirementRewrite::Kind:
       case Return::Kind:
       case ReturnExpr::Kind:
+      case SpecificFunction::Kind:
       case SpliceBlock::Kind:
       case StringLiteral::Kind:
       case StructAccess::Kind:

+ 21 - 0
toolchain/sem_ir/typed_insts.h

@@ -910,6 +910,27 @@ struct SpecificConstant {
   SpecificId specific_id;
 };
 
+// A specific instance of a generic function. This represents the callee in a
+// call instruction that is calling a generic function, where the specific
+// arguments of the function have been deduced.
+//
+// TODO: This value corresponds to the `(FunctionType as Call(...)).Op` function
+// in the overloaded calls design. Eventually we should represent it more
+// directly as a member of the `Call` interface.
+struct SpecificFunction {
+  static constexpr auto Kind = InstKind::SpecificFunction.Define<Parse::NodeId>(
+      {.ir_name = "specific_function",
+       .constant_kind = InstConstantKind::Always});
+
+  // Always the builtin SpecificFunctionType.
+  TypeId type_id;
+  // The expression denoting the callee.
+  InstId callee_id;
+  // The specific instance of the generic callee that will be called, including
+  // all the compile-time arguments.
+  SpecificId specific_id;
+};
+
 // Splices a block into the location where this appears. This may be an
 // expression, producing a result with a given type. For example, when
 // constructing from aggregates we may figure out which conversions are required