Explorar o código

Don't deduce values for explicitly-specified generic bindings. (#4415)

Distinguish between deduction against a symbolic binding pattern and
deduction against a symbolic binding name. In the former case, the value
is being explicitly specified and must be constant. In the latter case
we encountered a use of the binding name as a subexpression, and should
deduce against it if it's not explicitly specified.
Richard Smith hai 1 ano
pai
achega
684cda3d53

+ 95 - 36
toolchain/check/deduce.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/check/deduce.h"
 
+#include "llvm/ADT/SmallBitVector.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
@@ -183,6 +184,8 @@ class DeductionContext {
   llvm::SmallVector<SemIR::InstId> result_arg_ids_;
   llvm::SmallVector<Substitution> substitutions_;
   SemIR::CompileTimeBindIndex first_deduced_index_;
+  // Non-deduced indexes, indexed by parameter index - first_deduced_index_.
+  llvm::SmallBitVector non_deduced_indexes_;
 };
 
 }  // namespace
@@ -231,12 +234,33 @@ DeductionContext::DeductionContext(Context& context, SemIR::LocId loc_id,
     }
     first_deduced_index_ = SemIR::CompileTimeBindIndex(args.size());
   }
+
+  non_deduced_indexes_.resize(result_arg_ids_.size() -
+                              first_deduced_index_.index);
 }
 
 auto DeductionContext::Deduce() -> bool {
   while (!worklist_.Done()) {
     auto [param_id, arg_id, needs_substitution] = worklist_.PopNext();
 
+    auto note_initializing_param = [&](auto& builder) {
+      if (auto param =
+              context().insts().TryGetAs<SemIR::SymbolicBindingPattern>(
+                  param_id)) {
+        CARBON_DIAGNOSTIC(InitializingGenericParam, Note,
+                          "initializing generic parameter `{0}` declared here",
+                          SemIR::NameId);
+        builder.Note(
+            param_id, InitializingGenericParam,
+            context().entity_names().Get(param->entity_name_id).name_id);
+      } else {
+        NoteGenericHere(context(), generic_id_, builder);
+      }
+    };
+
+    // TODO: Bail out if there's nothing to deduce: if we're not in a pattern
+    // and the parameter doesn't have a symbolic constant value.
+
     // If the parameter has a symbolic type, deduce against that.
     auto param_type_id = context().insts().Get(param_id).type_id();
     if (param_type_id.AsConstantId().is_symbolic()) {
@@ -246,54 +270,70 @@ auto DeductionContext::Deduce() -> bool {
     } else {
       // The argument needs to have the same type as the parameter.
       // TODO: Suppress diagnostics here if diagnose_ is false.
-      DiagnosticAnnotationScope annotate_diagnostics(
-          &context().emitter(), [&](auto& builder) {
-            if (auto param =
-                    context().insts().TryGetAs<SemIR::BindSymbolicName>(
-                        param_id)) {
-              CARBON_DIAGNOSTIC(
-                  InitializingGenericParam, Note,
-                  "initializing generic parameter `{0}` declared here",
-                  SemIR::NameId);
-              builder.Note(
-                  param_id, InitializingGenericParam,
-                  context().entity_names().Get(param->entity_name_id).name_id);
-            }
-          });
+      // TODO: Only do this when deducing against a symbolic pattern.
+      DiagnosticAnnotationScope annotate_diagnostics(&context().emitter(),
+                                                     note_initializing_param);
       arg_id = ConvertToValueOfType(context(), loc_id_, arg_id, param_type_id);
       if (arg_id == SemIR::InstId::BuiltinError) {
         return false;
       }
     }
 
-    // If the parameter is a symbolic constant, deduce against it. Otherwise, we
-    // assume there is nothing to deduce.
-    // TODO: This won't do the right thing in a template deduction.
-    auto param_const_id = context().constant_values().Get(param_id);
-    if (!param_const_id.is_valid() || !param_const_id.is_symbolic()) {
-      continue;
-    }
-
     // Attempt to match `param_inst` against `arg_id`. If the match succeeds,
     // this should `continue` the outer loop. On `break`, we will try to desugar
     // the parameter to continue looking for a match.
-    auto param_inst = context().insts().Get(
-        context().constant_values().GetInstId(param_const_id));
+    auto param_inst = context().insts().Get(param_id);
     CARBON_KIND_SWITCH(param_inst) {
-      // Deducing a symbolic binding from an argument with a constant value
-      // deduces the binding as having that constant value.
-      case SemIR::InstKind::SymbolicBindingPattern:
-      case SemIR::InstKind::BindSymbolicName: {
-        auto entity_name_id = SemIR::EntityNameId::Invalid;
-        if (auto bind = param_inst.TryAs<SemIR::SymbolicBindingPattern>()) {
-          entity_name_id = bind->entity_name_id;
-        } else {
-          entity_name_id =
-              param_inst.As<SemIR::BindSymbolicName>().entity_name_id;
+      // Deducing a symbolic binding pattern from an argument deduces the
+      // binding as having that constant value. For example, deducing
+      // `(T:! type)` against `(i32)` deduces `T` to be `i32`. This only arises
+      // when initializing a generic parameter from an explicitly specified
+      // argument, and in this case, the argument is required to be a
+      // compile-time constant.
+      case CARBON_KIND(SemIR::SymbolicBindingPattern bind): {
+        auto& entity_name = context().entity_names().Get(bind.entity_name_id);
+        auto index = entity_name.bind_index;
+        if (!index.is_valid()) {
+          break;
+        }
+        CARBON_CHECK(
+            index >= first_deduced_index_ &&
+                static_cast<size_t>(index.index) < result_arg_ids_.size(),
+            "Unexpected index {0} for symbolic binding pattern; "
+            "expected to be in range [{1}, {2})",
+            index.index, first_deduced_index_.index, result_arg_ids_.size());
+        CARBON_CHECK(!result_arg_ids_[index.index].is_valid(),
+                     "Deduced a value for parameter prior to its declaration");
+
+        auto arg_const_inst_id =
+            context().constant_values().GetConstantInstId(arg_id);
+        if (!arg_const_inst_id.is_valid()) {
+          if (diagnose_) {
+            CARBON_DIAGNOSTIC(CompTimeArgumentNotConstant, Error,
+                              "argument for generic parameter is not a "
+                              "compile-time constant");
+            auto diag =
+                context().emitter().Build(loc_id_, CompTimeArgumentNotConstant);
+            note_initializing_param(diag);
+            diag.Emit();
+          }
+          return false;
         }
-        auto& entity_name = context().entity_names().Get(entity_name_id);
+
+        result_arg_ids_[index.index] = arg_const_inst_id;
+        // This parameter index should not be deduced if it appears later.
+        non_deduced_indexes_[index.index - first_deduced_index_.index] = true;
+        continue;
+      }
+
+      // Deducing a symbolic binding appearing within an expression against a
+      // constant value deduces the binding as having that value. For example,
+      // deducing `[T:! type](x: T)` against `("foo")` deduces `T` as `String`.
+      case CARBON_KIND(SemIR::BindSymbolicName bind): {
+        auto& entity_name = context().entity_names().Get(bind.entity_name_id);
         auto index = entity_name.bind_index;
-        if (!index.is_valid() || index < first_deduced_index_) {
+        if (!index.is_valid() || index < first_deduced_index_ ||
+            non_deduced_indexes_[index.index - first_deduced_index_.index]) {
           break;
         }
 
@@ -324,6 +364,11 @@ auto DeductionContext::Deduce() -> bool {
         continue;
       }
 
+      case CARBON_KIND(SemIR::ParamPattern pattern): {
+        Add(pattern.subpattern_id, arg_id, needs_substitution);
+        continue;
+      }
+
       // Various kinds of parameter should match an argument of the same form,
       // if the operands all match.
       case SemIR::ArrayType::Kind:
@@ -358,6 +403,20 @@ auto DeductionContext::Deduce() -> bool {
         break;
     }
 
+    // We didn't manage to deduce against the syntactic form of the parameter.
+    // Convert it to a canonical constant value and try deducing against that.
+    auto param_const_id = context().constant_values().Get(param_id);
+    if (!param_const_id.is_valid() || !param_const_id.is_symbolic()) {
+      // It's not a symbolic constant. There's nothing here to deduce.
+      continue;
+    }
+    auto param_const_inst_id =
+        context().constant_values().GetInstId(param_const_id);
+    if (param_const_inst_id != param_id) {
+      Add(param_const_inst_id, arg_id, needs_substitution);
+      continue;
+    }
+
     // If we've not yet substituted into the parameter, do so now and try again.
     if (needs_substitution) {
       param_const_id = SubstConstant(context(), param_const_id, substitutions_);

+ 12 - 9
toolchain/check/testdata/class/generic/call.carbon

@@ -55,12 +55,15 @@ library "[[@TEST_NAME]]";
 
 class Class(T:! type, N:! i32) {}
 
-// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+6]]:8: error: cannot implicitly convert from `i32` to `type`
+// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+9]]:8: error: cannot implicitly convert from `i32` to `type`
 // CHECK:STDERR: var a: Class(5, i32*);
 // CHECK:STDERR:        ^~~~~~
-// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+3]]:8: note: type `i32` does not implement interface `ImplicitAs`
+// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+6]]:8: note: type `i32` does not implement interface `ImplicitAs`
 // CHECK:STDERR: var a: Class(5, i32*);
 // CHECK:STDERR:        ^~~~~~
+// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE-8]]:1: note: while deducing parameters of generic declared here
+// CHECK:STDERR: class Class(T:! type, N:! i32) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 var a: Class(5, i32*);
 
 // --- call_in_nested_return_type.carbon
@@ -463,15 +466,15 @@ class Outer(T:! type) {
 // CHECK:STDOUT:     %N.loc4_23.1: i32 = bind_symbolic_name N, 1, %param.loc4_23 [symbolic = %N.loc4_23.2 (constants.%N)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Class.ref: %Class.type = name_ref Class, %Class.decl [template = constants.%Class.1]
-// CHECK:STDOUT:   %.loc12_14: i32 = int_literal 5 [template = constants.%.4]
+// CHECK:STDOUT:   %.loc15_14: i32 = int_literal 5 [template = constants.%.4]
 // CHECK:STDOUT:   %int.make_type_32: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:   %.loc12_20.1: type = value_of_initializer %int.make_type_32 [template = i32]
-// CHECK:STDOUT:   %.loc12_20.2: type = converted %int.make_type_32, %.loc12_20.1 [template = i32]
-// CHECK:STDOUT:   %.loc12_20.3: type = ptr_type i32 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc15_20.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:   %.loc15_20.2: type = converted %int.make_type_32, %.loc15_20.1 [template = i32]
+// CHECK:STDOUT:   %.loc15_20.3: type = ptr_type i32 [template = constants.%.5]
 // CHECK:STDOUT:   %ImplicitAs.type: type = interface_type @ImplicitAs, @ImplicitAs(type) [template = constants.%ImplicitAs.type.3]
-// CHECK:STDOUT:   %.loc12_13.1: %.8 = specific_constant imports.%import_ref.4, @ImplicitAs(type) [template = constants.%.9]
-// CHECK:STDOUT:   %Convert.ref: %.8 = name_ref Convert, %.loc12_13.1 [template = constants.%.9]
-// CHECK:STDOUT:   %.loc12_13.2: type = converted %.loc12_14, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc15_13.1: %.8 = specific_constant imports.%import_ref.4, @ImplicitAs(type) [template = constants.%.9]
+// CHECK:STDOUT:   %Convert.ref: %.8 = name_ref Convert, %.loc15_13.1 [template = constants.%.9]
+// CHECK:STDOUT:   %.loc15_13.2: type = converted %.loc15_14, <error> [template = <error>]
 // CHECK:STDOUT:   %a.var: ref <error> = var a
 // CHECK:STDOUT:   %a: ref <error> = bind_name a, %a.var
 // CHECK:STDOUT: }

+ 216 - 38
toolchain/check/testdata/function/generic/deduce.carbon

@@ -22,23 +22,38 @@ fn CallExplicitGenericParamWithGenericArg(T:! type) -> {.a: T}* {
   return ExplicitGenericParam({.a: T});
 }
 
-// --- fail_todo_explicit_vs_deduced.carbon
+// --- fail_deduce_explicit_non_constant.carbon
 
 library "[[@TEST_NAME]]";
 
-class A {}
+fn ExplicitGenericParam(T:! type) -> T* { return ExplicitGenericParam(T); }
 
-fn ExplicitAndAlsoDeduced(T:! type, x: T) -> T*;
+fn CallExplicitGenericParamConst(T:! type) {
+  ExplicitGenericParam(T);
+}
 
-// TODO: This should presumably be accepted. We shouldn't deduce values for parameters with explicitly-specified values.
-fn CallExplicitAndAlsoDeduced(n: i32) -> i32* {
-  // CHECK:STDERR: fail_todo_explicit_vs_deduced.carbon:[[@LINE+7]]:10: error: inconsistent deductions for value of generic parameter `T`
-  // CHECK:STDERR:   return ExplicitAndAlsoDeduced(A, {});
-  // CHECK:STDERR:          ^~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_todo_explicit_vs_deduced.carbon:[[@LINE-7]]:1: note: while deducing parameters of generic declared here
-  // CHECK:STDERR: fn ExplicitAndAlsoDeduced(T:! type, x: T) -> T*;
-  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+fn CallExplicitGenericParamNonConst(T: type) {
+  // CHECK:STDERR: fail_deduce_explicit_non_constant.carbon:[[@LINE+7]]:3: error: argument for generic parameter is not a compile-time constant
+  // CHECK:STDERR:   ExplicitGenericParam(T);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_deduce_explicit_non_constant.carbon:[[@LINE-10]]:25: note: initializing generic parameter `T` declared here
+  // CHECK:STDERR: fn ExplicitGenericParam(T:! type) -> T* { return ExplicitGenericParam(T); }
+  // CHECK:STDERR:                         ^
   // CHECK:STDERR:
+  ExplicitGenericParam(T);
+}
+
+// --- explicit_vs_deduced.carbon
+
+library "[[@TEST_NAME]]";
+
+class A {}
+
+fn ExplicitAndAlsoDeduced(T:! type, x: T) -> T* {
+  return ExplicitAndAlsoDeduced(T, x);
+}
+
+fn CallExplicitAndAlsoDeduced() -> A* {
   return ExplicitAndAlsoDeduced(A, {});
 }
 
@@ -294,7 +309,142 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %.loc4_39.2 => constants.%.7
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_explicit_vs_deduced.carbon
+// CHECK:STDOUT: --- fail_deduce_explicit_non_constant.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %T.patt.1: type = symbolic_binding_pattern T, 0 [symbolic]
+// CHECK:STDOUT:   %.1: type = ptr_type %T [symbolic]
+// 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:   %T.patt.2: type = symbolic_binding_pattern T, 0 [symbolic]
+// CHECK:STDOUT:   %CallExplicitGenericParamConst.type: type = fn_type @CallExplicitGenericParamConst [template]
+// CHECK:STDOUT:   %CallExplicitGenericParamConst: %CallExplicitGenericParamConst.type = struct_value () [template]
+// CHECK:STDOUT:   %CallExplicitGenericParamNonConst.type: type = fn_type @CallExplicitGenericParamNonConst [template]
+// CHECK:STDOUT:   %CallExplicitGenericParamNonConst: %CallExplicitGenericParamNonConst.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// 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: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .ExplicitGenericParam = %ExplicitGenericParam.decl
+// CHECK:STDOUT:     .CallExplicitGenericParamConst = %CallExplicitGenericParamConst.decl
+// CHECK:STDOUT:     .CallExplicitGenericParamNonConst = %CallExplicitGenericParamNonConst.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %ExplicitGenericParam.decl: %ExplicitGenericParam.type = fn_decl @ExplicitGenericParam [template = constants.%ExplicitGenericParam] {
+// CHECK:STDOUT:     %T.patt.loc4_25.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_25.2 (constants.%T.patt.1)]
+// CHECK:STDOUT:     %T.param_patt: type = param_pattern %T.patt.loc4_25.1, runtime_param<invalid> [symbolic = %T.patt.loc4_25.2 (constants.%T.patt.1)]
+// CHECK:STDOUT:   } {
+// 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:     %param: type = param runtime_param<invalid>
+// CHECK:STDOUT:     %T.loc4_25.1: type = bind_symbolic_name T, 0, %param [symbolic = %T.loc4_25.2 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %CallExplicitGenericParamConst.decl: %CallExplicitGenericParamConst.type = fn_decl @CallExplicitGenericParamConst [template = constants.%CallExplicitGenericParamConst] {
+// CHECK:STDOUT:     %T.patt.loc6_34.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc6_34.2 (constants.%T.patt.2)]
+// CHECK:STDOUT:     %T.param_patt: type = param_pattern %T.patt.loc6_34.1, runtime_param<invalid> [symbolic = %T.patt.loc6_34.2 (constants.%T.patt.2)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %param: type = param runtime_param<invalid>
+// CHECK:STDOUT:     %T.loc6_34.1: type = bind_symbolic_name T, 0, %param [symbolic = %T.loc6_34.2 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %CallExplicitGenericParamNonConst.decl: %CallExplicitGenericParamNonConst.type = fn_decl @CallExplicitGenericParamNonConst [template = constants.%CallExplicitGenericParamNonConst] {
+// CHECK:STDOUT:     %T.patt: type = binding_pattern T
+// CHECK:STDOUT:     %T.param_patt: type = param_pattern %T.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %param: type = param runtime_param0
+// CHECK:STDOUT:     %T: type = bind_name T, %param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @ExplicitGenericParam(%T.loc4_25.1: type) {
+// CHECK:STDOUT:   %T.loc4_25.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_25.2 (constants.%T)]
+// CHECK:STDOUT:   %T.patt.loc4_25.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_25.2 (constants.%T.patt.1)]
+// CHECK:STDOUT:   %.loc4_39.2: type = ptr_type @ExplicitGenericParam.%T.loc4_25.2 (%T) [symbolic = %.loc4_39.2 (constants.%.1)]
+// CHECK:STDOUT:
+// 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.param_patt: 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: generic fn @CallExplicitGenericParamConst(%T.loc6_34.1: type) {
+// CHECK:STDOUT:   %T.loc6_34.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc6_34.2 (constants.%T)]
+// CHECK:STDOUT:   %T.patt.loc6_34.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc6_34.2 (constants.%T.patt.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_3.2: <specific function> = specific_function constants.%ExplicitGenericParam, @ExplicitGenericParam(%T.loc6_34.2) [symbolic = %.loc7_3.2 (constants.%.3)]
+// CHECK:STDOUT:   %.loc7_23: type = ptr_type @CallExplicitGenericParamConst.%T.loc6_34.2 (%T) [symbolic = %.loc7_23 (constants.%.1)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%T.param_patt: type) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [template = constants.%ExplicitGenericParam]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc6_34.1 [symbolic = %T.loc6_34.2 (constants.%T)]
+// CHECK:STDOUT:     %.loc7_3.1: <specific function> = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(constants.%T) [symbolic = %.loc7_3.2 (constants.%.3)]
+// CHECK:STDOUT:     %ExplicitGenericParam.call: init @CallExplicitGenericParamConst.%.loc7_23 (%.1) = call %.loc7_3.1()
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @CallExplicitGenericParamNonConst(%T.param_patt: type) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [template = constants.%ExplicitGenericParam]
+// CHECK:STDOUT:   %T.ref: type = name_ref T, %T
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ExplicitGenericParam(constants.%T) {
+// CHECK:STDOUT:   %T.loc4_25.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.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:   %T.patt.loc4_25.2 => constants.%T
+// CHECK:STDOUT:   %.loc4_39.2 => constants.%.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CallExplicitGenericParamConst(constants.%T) {
+// CHECK:STDOUT:   %T.loc6_34.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc6_34.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ExplicitGenericParam(@CallExplicitGenericParamConst.%T.loc6_34.2) {
+// CHECK:STDOUT:   %T.loc4_25.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc4_25.2 => constants.%T
+// CHECK:STDOUT:   %.loc4_39.2 => constants.%.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- explicit_vs_deduced.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %A: type = class_type @A [template]
@@ -306,16 +456,17 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %ExplicitAndAlsoDeduced.type: type = fn_type @ExplicitAndAlsoDeduced [template]
 // CHECK:STDOUT:   %.4: type = tuple_type () [template]
 // CHECK:STDOUT:   %ExplicitAndAlsoDeduced: %ExplicitAndAlsoDeduced.type = struct_value () [template]
-// CHECK:STDOUT:   %Int32.type: type = fn_type @Int32 [template]
-// CHECK:STDOUT:   %Int32: %Int32.type = struct_value () [template]
-// CHECK:STDOUT:   %.5: type = ptr_type i32 [template]
+// CHECK:STDOUT:   %.5: <specific function> = specific_function %ExplicitAndAlsoDeduced, @ExplicitAndAlsoDeduced(%T) [symbolic]
+// CHECK:STDOUT:   %.6: type = ptr_type %A [template]
 // CHECK:STDOUT:   %CallExplicitAndAlsoDeduced.type: type = fn_type @CallExplicitAndAlsoDeduced [template]
 // CHECK:STDOUT:   %CallExplicitAndAlsoDeduced: %CallExplicitAndAlsoDeduced.type = struct_value () [template]
+// CHECK:STDOUT:   %.7: <specific function> = specific_function %ExplicitAndAlsoDeduced, @ExplicitAndAlsoDeduced(%A) [template]
+// CHECK:STDOUT:   %.8: type = ptr_type %.1 [template]
+// CHECK:STDOUT:   %struct: %A = struct_value () [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
@@ -325,7 +476,6 @@ fn CallImplicitNotDeducible() {
 // 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+9, loaded [template = constants.%Int32]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -352,20 +502,10 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:     %param.loc6_37: @ExplicitAndAlsoDeduced.%T.loc6_27.2 (%T) = param runtime_param0
 // CHECK:STDOUT:     %x: @ExplicitAndAlsoDeduced.%T.loc6_27.2 (%T) = bind_name x, %param.loc6_37
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %CallExplicitAndAlsoDeduced.decl: %CallExplicitAndAlsoDeduced.type = fn_decl @CallExplicitAndAlsoDeduced [template = constants.%CallExplicitAndAlsoDeduced] {
-// CHECK:STDOUT:     %n.patt: i32 = binding_pattern n
-// CHECK:STDOUT:     %n.param_patt: i32 = param_pattern %n.patt, runtime_param0
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     %int.make_type_32.loc9_34: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc9_34.1: type = value_of_initializer %int.make_type_32.loc9_34 [template = i32]
-// CHECK:STDOUT:     %.loc9_34.2: type = converted %int.make_type_32.loc9_34, %.loc9_34.1 [template = i32]
-// CHECK:STDOUT:     %int.make_type_32.loc9_42: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc9_45.1: type = value_of_initializer %int.make_type_32.loc9_42 [template = i32]
-// CHECK:STDOUT:     %.loc9_45.2: type = converted %int.make_type_32.loc9_42, %.loc9_45.1 [template = i32]
-// CHECK:STDOUT:     %.loc9_45.3: type = ptr_type i32 [template = constants.%.5]
-// CHECK:STDOUT:     %return: ref %.5 = var <return slot>
-// CHECK:STDOUT:     %param: i32 = param runtime_param0
-// CHECK:STDOUT:     %n: i32 = bind_name n, %param
+// CHECK:STDOUT:   %CallExplicitAndAlsoDeduced.decl: %CallExplicitAndAlsoDeduced.type = fn_decl @CallExplicitAndAlsoDeduced [template = constants.%CallExplicitAndAlsoDeduced] {} {
+// CHECK:STDOUT:     %A.ref.loc10: type = name_ref A, file.%A.decl [template = constants.%A]
+// CHECK:STDOUT:     %.loc10: type = ptr_type %A [template = constants.%.6]
+// CHECK:STDOUT:     %return: ref %.6 = var <return slot>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -381,23 +521,61 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %T.patt.loc6_27.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc6_27.2 (constants.%T.patt)]
 // CHECK:STDOUT:   %.loc6_47.2: type = ptr_type @ExplicitAndAlsoDeduced.%T.loc6_27.2 (%T) [symbolic = %.loc6_47.2 (constants.%.3)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn(%T.param_patt: type, %x.param_patt: @ExplicitAndAlsoDeduced.%T.loc6_27.2 (%T)) -> @ExplicitAndAlsoDeduced.%.loc6_47.2 (%.3);
-// CHECK:STDOUT: }
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_10.2: <specific function> = specific_function constants.%ExplicitAndAlsoDeduced, @ExplicitAndAlsoDeduced(%T.loc6_27.2) [symbolic = %.loc7_10.2 (constants.%.5)]
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
+// CHECK:STDOUT:   fn(%T.param_patt: type, %x.param_patt: @ExplicitAndAlsoDeduced.%T.loc6_27.2 (%T)) -> @ExplicitAndAlsoDeduced.%.loc6_47.2 (%.3) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %ExplicitAndAlsoDeduced.ref: %ExplicitAndAlsoDeduced.type = name_ref ExplicitAndAlsoDeduced, file.%ExplicitAndAlsoDeduced.decl [template = constants.%ExplicitAndAlsoDeduced]
+// CHECK:STDOUT:     %T.ref.loc7: type = name_ref T, %T.loc6_27.1 [symbolic = %T.loc6_27.2 (constants.%T)]
+// CHECK:STDOUT:     %x.ref: @ExplicitAndAlsoDeduced.%T.loc6_27.2 (%T) = name_ref x, %x
+// CHECK:STDOUT:     %.loc7_10.1: <specific function> = specific_function %ExplicitAndAlsoDeduced.ref, @ExplicitAndAlsoDeduced(constants.%T) [symbolic = %.loc7_10.2 (constants.%.5)]
+// CHECK:STDOUT:     %ExplicitAndAlsoDeduced.call: init @ExplicitAndAlsoDeduced.%.loc6_47.2 (%.3) = call %.loc7_10.1(%x.ref)
+// CHECK:STDOUT:     %.loc7_38.1: @ExplicitAndAlsoDeduced.%.loc6_47.2 (%.3) = value_of_initializer %ExplicitAndAlsoDeduced.call
+// CHECK:STDOUT:     %.loc7_38.2: @ExplicitAndAlsoDeduced.%.loc6_47.2 (%.3) = converted %ExplicitAndAlsoDeduced.call, %.loc7_38.1
+// CHECK:STDOUT:     return %.loc7_38.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @CallExplicitAndAlsoDeduced(%n.param_patt: i32) -> %.5 {
+// CHECK:STDOUT: fn @CallExplicitAndAlsoDeduced() -> %.6 {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %ExplicitAndAlsoDeduced.ref: %ExplicitAndAlsoDeduced.type = name_ref ExplicitAndAlsoDeduced, file.%ExplicitAndAlsoDeduced.decl [template = constants.%ExplicitAndAlsoDeduced]
-// CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [template = constants.%A]
-// CHECK:STDOUT:   %.loc17: %.1 = struct_literal ()
-// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT:   %A.ref.loc11: type = name_ref A, file.%A.decl [template = constants.%A]
+// CHECK:STDOUT:   %.loc11_37.1: %.1 = struct_literal ()
+// CHECK:STDOUT:   %.loc11_10: <specific function> = specific_function %ExplicitAndAlsoDeduced.ref, @ExplicitAndAlsoDeduced(constants.%A) [template = constants.%.7]
+// CHECK:STDOUT:   %.loc11_37.2: ref %A = temporary_storage
+// CHECK:STDOUT:   %.loc11_37.3: init %A = class_init (), %.loc11_37.2 [template = constants.%struct]
+// CHECK:STDOUT:   %.loc11_37.4: ref %A = temporary %.loc11_37.2, %.loc11_37.3
+// CHECK:STDOUT:   %.loc11_37.5: ref %A = converted %.loc11_37.1, %.loc11_37.4
+// CHECK:STDOUT:   %.loc11_37.6: %A = bind_value %.loc11_37.5
+// CHECK:STDOUT:   %ExplicitAndAlsoDeduced.call: init %.6 = call %.loc11_10(%.loc11_37.6)
+// CHECK:STDOUT:   %.loc11_39.1: %.6 = value_of_initializer %ExplicitAndAlsoDeduced.call
+// CHECK:STDOUT:   %.loc11_39.2: %.6 = converted %ExplicitAndAlsoDeduced.call, %.loc11_39.1
+// CHECK:STDOUT:   return %.loc11_39.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @ExplicitAndAlsoDeduced(constants.%T) {
 // CHECK:STDOUT:   %T.loc6_27.2 => constants.%T
 // CHECK:STDOUT:   %T.patt.loc6_27.2 => constants.%T
 // CHECK:STDOUT:   %.loc6_47.2 => constants.%.3
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_10.2 => constants.%.5
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ExplicitAndAlsoDeduced(@ExplicitAndAlsoDeduced.%T.loc6_27.2) {
+// CHECK:STDOUT:   %T.loc6_27.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc6_27.2 => constants.%T
+// CHECK:STDOUT:   %.loc6_47.2 => constants.%.3
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @ExplicitAndAlsoDeduced(constants.%A) {
+// CHECK:STDOUT:   %T.loc6_27.2 => constants.%A
+// CHECK:STDOUT:   %T.patt.loc6_27.2 => constants.%A
+// CHECK:STDOUT:   %.loc6_47.2 => constants.%.6
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc7_10.2 => constants.%.7
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- deduce_implicit.carbon

+ 5 - 2
toolchain/check/testdata/interface/no_prelude/generic.carbon

@@ -49,9 +49,12 @@ class B {}
 fn F(T:! Generic(A));
 fn G(T:! Generic(B)) {
   // TODO: Include generic arguments in the type name.
-  // CHECK:STDERR: fail_mismatched_args.carbon:[[@LINE+3]]:3: error: package `Core` implicitly referenced here, but not found
+  // CHECK:STDERR: fail_mismatched_args.carbon:[[@LINE+6]]:3: error: package `Core` implicitly referenced here, but not found
   // CHECK:STDERR:   F(T);
   // CHECK:STDERR:   ^~
+  // CHECK:STDERR: fail_mismatched_args.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here
+  // CHECK:STDERR: fn F(T:! Generic(A));
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
   F(T);
 }
 
@@ -488,7 +491,7 @@ fn G(T:! Generic(B)) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     %F.ref: %F.type = name_ref F, file.%F.decl [template = constants.%F]
 // CHECK:STDOUT:     %T.ref: %Generic.type.4 = name_ref T, %T.loc10_6.1 [symbolic = %T.loc10_6.2 (constants.%T.3)]
-// CHECK:STDOUT:     %.loc15: %Generic.type.3 = converted %T.ref, <error> [template = <error>]
+// CHECK:STDOUT:     %.loc18: %Generic.type.3 = converted %T.ref, <error> [template = <error>]
 // CHECK:STDOUT:     return
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }

+ 10 - 6
toolchain/check/testdata/struct/import.carbon

@@ -30,9 +30,13 @@ var c: C({.a = 1, .b = 2}) = F();
 // --- fail_bad_type.impl.carbon
 
 impl package Implicit;
-// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+4]]:14: error: missing value for field `a` in struct initialization
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+8]]:14: error: missing value for field `a` in struct initialization
 // CHECK:STDERR: var c_bad: C({.c = 1, .d = 2}) = F();
 // CHECK:STDERR:              ^~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE-4]]:6: in import
+// CHECK:STDERR: implicit.carbon:8:1: note: while deducing parameters of generic declared here
+// CHECK:STDERR: class C(S:! {.a: i32, .b: i32}) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 var c_bad: C({.c = 1, .d = 2}) = F();
 
@@ -430,9 +434,9 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.ref: %C.type = name_ref C, imports.%import_ref.3 [template = constants.%C.1]
-// CHECK:STDOUT:   %.loc7_20: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc7_28: i32 = int_literal 2 [template = constants.%.6]
-// CHECK:STDOUT:   %.loc7_29: %.7 = struct_literal (%.loc7_20, %.loc7_28)
+// CHECK:STDOUT:   %.loc11_20: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc11_28: i32 = int_literal 2 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc11_29: %.7 = struct_literal (%.loc11_20, %.loc11_28)
 // CHECK:STDOUT:   %c_bad.var: ref <error> = var c_bad
 // CHECK:STDOUT:   %c_bad: ref <error> = bind_name c_bad, %c_bad.var
 // CHECK:STDOUT: }
@@ -454,8 +458,8 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, imports.%import_ref.4 [template = constants.%F]
-// CHECK:STDOUT:   %.loc7: ref %C.3 = temporary_storage
-// CHECK:STDOUT:   %F.call: init %C.3 = call %F.ref() to %.loc7
+// CHECK:STDOUT:   %.loc11: ref %C.3 = temporary_storage
+// CHECK:STDOUT:   %F.call: init %C.3 = call %F.ref() to %.loc11
 // CHECK:STDOUT:   assign file.%c_bad.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 11 - 7
toolchain/check/testdata/tuple/import.carbon

@@ -31,9 +31,13 @@ var c: C((1, 2)) = F();
 
 impl package Implicit;
 
-// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+4]]:14: error: cannot initialize tuple of 2 elements from tuple with 3 elements
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE+8]]:14: error: cannot initialize tuple of 2 elements from tuple with 3 elements
 // CHECK:STDERR: var c_bad: C((1, 2, 3)) = F();
 // CHECK:STDERR:              ^~~~~~~~~
+// CHECK:STDERR: fail_bad_type.impl.carbon:[[@LINE-5]]:6: in import
+// CHECK:STDERR: implicit.carbon:7:1: note: while deducing parameters of generic declared here
+// CHECK:STDERR: class C(X:! (i32, i32)) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 var c_bad: C((1, 2, 3)) = F();
 
@@ -470,10 +474,10 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT:   %default.import = import <invalid>
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %C.ref: %C.type = name_ref C, imports.%import_ref.3 [template = constants.%C.1]
-// CHECK:STDOUT:   %.loc8_15: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc8_18: i32 = int_literal 2 [template = constants.%.6]
-// CHECK:STDOUT:   %.loc8_21: i32 = int_literal 3 [template = constants.%.7]
-// CHECK:STDOUT:   %.loc8_22: %.8 = tuple_literal (%.loc8_15, %.loc8_18, %.loc8_21)
+// CHECK:STDOUT:   %.loc12_15: i32 = int_literal 1 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc12_18: i32 = int_literal 2 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc12_21: i32 = int_literal 3 [template = constants.%.7]
+// CHECK:STDOUT:   %.loc12_22: %.8 = tuple_literal (%.loc12_15, %.loc12_18, %.loc12_21)
 // CHECK:STDOUT:   %c_bad.var: ref <error> = var c_bad
 // CHECK:STDOUT:   %c_bad: ref <error> = bind_name c_bad, %c_bad.var
 // CHECK:STDOUT: }
@@ -495,8 +499,8 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT: fn @__global_init() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %F.ref: %F.type = name_ref F, imports.%import_ref.4 [template = constants.%F]
-// CHECK:STDOUT:   %.loc8: ref %C.3 = temporary_storage
-// CHECK:STDOUT:   %F.call: init %C.3 = call %F.ref() to %.loc8
+// CHECK:STDOUT:   %.loc12: ref %C.3 = temporary_storage
+// CHECK:STDOUT:   %F.call: init %C.3 = call %F.ref() to %.loc12
 // CHECK:STDOUT:   assign file.%c_bad.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 28 - 21
toolchain/check/testdata/where_expr/constraints.carbon

@@ -128,12 +128,16 @@ impl C as J {}
 
 // TODO: Should report that `C` does not meet the constraint.
 fn DoesNotImplI() {
-  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE+7]]:3: error: cannot implicitly convert from `type` to `J`
+  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE+11]]:3: error: cannot implicitly convert from `type` to `J`
   // CHECK:STDERR:   Impls(C);
   // CHECK:STDERR:   ^~~~~~
-  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE+4]]:3: note: type `type` does not implement interface `ImplicitAs`
+  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE+8]]:3: note: type `type` does not implement interface `ImplicitAs`
   // CHECK:STDERR:   Impls(C);
   // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE-14]]:1: in import
+  // CHECK:STDERR: state_constraints.carbon:13:1: note: while deducing parameters of generic declared here
+  // CHECK:STDERR: fn Impls(V:! J where .Self impls I);
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   Impls(C);
 }
@@ -142,12 +146,15 @@ fn EmptyStruct(Y:! J where .Self == {});
 
 // TODO: Should report that `C` does not meeet the constraint.
 fn NotEmptyStruct() {
-  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE+6]]:3: error: cannot implicitly convert from `type` to `J`
+  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE+9]]:3: error: cannot implicitly convert from `type` to `J`
   // CHECK:STDERR:   EmptyStruct(C);
   // CHECK:STDERR:   ^~~~~~~~~~~~
-  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE+3]]:3: note: type `type` does not implement interface `ImplicitAs`
+  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE+6]]:3: note: type `type` does not implement interface `ImplicitAs`
   // CHECK:STDERR:   EmptyStruct(C);
   // CHECK:STDERR:   ^~~~~~~~~~~~
+  // CHECK:STDERR: fail_todo_enforce_constraint.carbon:[[@LINE-10]]:1: note: while deducing parameters of generic declared here
+  // CHECK:STDERR: fn EmptyStruct(Y:! J where .Self == {});
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   EmptyStruct(C);
 }
 
@@ -1305,18 +1312,18 @@ fn NotEmptyStruct() {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %DoesNotImplI.decl: %DoesNotImplI.type = fn_decl @DoesNotImplI [template = constants.%DoesNotImplI] {} {}
 // CHECK:STDOUT:   %EmptyStruct.decl: %EmptyStruct.type = fn_decl @EmptyStruct [template = constants.%EmptyStruct] {
-// CHECK:STDOUT:     %Y.patt.loc22_16.1: %J.type = symbolic_binding_pattern Y, 0 [symbolic = %Y.patt.loc22_16.2 (constants.%Y.patt)]
-// CHECK:STDOUT:     %Y.param_patt: %J.type = param_pattern %Y.patt.loc22_16.1, runtime_param<invalid> [symbolic = %Y.patt.loc22_16.2 (constants.%Y.patt)]
+// CHECK:STDOUT:     %Y.patt.loc26_16.1: %J.type = symbolic_binding_pattern Y, 0 [symbolic = %Y.patt.loc26_16.2 (constants.%Y.patt)]
+// CHECK:STDOUT:     %Y.param_patt: %J.type = param_pattern %Y.patt.loc26_16.1, runtime_param<invalid> [symbolic = %Y.patt.loc26_16.2 (constants.%Y.patt)]
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %J.ref: type = name_ref J, imports.%import_ref.1 [template = constants.%J.type]
 // CHECK:STDOUT:     %.Self: %J.type = bind_symbolic_name .Self, 0 [symbolic = constants.%.Self]
 // CHECK:STDOUT:     %.Self.ref: %J.type = name_ref .Self, %.Self [symbolic = constants.%.Self]
-// CHECK:STDOUT:     %.loc22_38: %.1 = struct_literal ()
-// CHECK:STDOUT:     %.loc22_22: type = where_expr %.Self [template = constants.%J.type] {
-// CHECK:STDOUT:       requirement_equivalent %.Self.ref, %.loc22_38
+// CHECK:STDOUT:     %.loc26_38: %.1 = struct_literal ()
+// CHECK:STDOUT:     %.loc26_22: type = where_expr %.Self [template = constants.%J.type] {
+// CHECK:STDOUT:       requirement_equivalent %.Self.ref, %.loc26_38
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %param: %J.type = param runtime_param<invalid>
-// CHECK:STDOUT:     %Y.loc22_16.1: %J.type = bind_symbolic_name Y, 0, %param [symbolic = %Y.loc22_16.2 (constants.%Y)]
+// CHECK:STDOUT:     %Y.loc26_16.1: %J.type = bind_symbolic_name Y, 0, %param [symbolic = %Y.loc26_16.2 (constants.%Y)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %NotEmptyStruct.decl: %NotEmptyStruct.type = fn_decl @NotEmptyStruct [template = constants.%NotEmptyStruct] {} {}
 // CHECK:STDOUT: }
@@ -1366,9 +1373,9 @@ fn NotEmptyStruct() {
 // CHECK:STDOUT:   %Impls.ref: %Impls.type = name_ref Impls, imports.%import_ref.4 [template = constants.%Impls]
 // CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:   %ImplicitAs.type: type = interface_type @ImplicitAs, @ImplicitAs(constants.%J.type) [template = constants.%ImplicitAs.type.3]
-// CHECK:STDOUT:   %.loc19_8.1: %.7 = specific_constant imports.%import_ref.9, @ImplicitAs(constants.%J.type) [template = constants.%.8]
-// CHECK:STDOUT:   %Convert.ref: %.7 = name_ref Convert, %.loc19_8.1 [template = constants.%.8]
-// CHECK:STDOUT:   %.loc19_8.2: %J.type = converted %C.ref, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc23_8.1: %.7 = specific_constant imports.%import_ref.9, @ImplicitAs(constants.%J.type) [template = constants.%.8]
+// CHECK:STDOUT:   %Convert.ref: %.7 = name_ref Convert, %.loc23_8.1 [template = constants.%.8]
+// CHECK:STDOUT:   %.loc23_8.2: %J.type = converted %C.ref, <error> [template = <error>]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1387,9 +1394,9 @@ fn NotEmptyStruct() {
 // CHECK:STDOUT:   fn[%self.param_patt: @Convert.%Self (%Self.3)]() -> @Convert.%Dest (%Dest);
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic fn @EmptyStruct(%Y.loc22_16.1: %J.type) {
-// CHECK:STDOUT:   %Y.loc22_16.2: %J.type = bind_symbolic_name Y, 0 [symbolic = %Y.loc22_16.2 (constants.%Y)]
-// CHECK:STDOUT:   %Y.patt.loc22_16.2: %J.type = symbolic_binding_pattern Y, 0 [symbolic = %Y.patt.loc22_16.2 (constants.%Y.patt)]
+// CHECK:STDOUT: generic fn @EmptyStruct(%Y.loc26_16.1: %J.type) {
+// CHECK:STDOUT:   %Y.loc26_16.2: %J.type = bind_symbolic_name Y, 0 [symbolic = %Y.loc26_16.2 (constants.%Y)]
+// CHECK:STDOUT:   %Y.patt.loc26_16.2: %J.type = symbolic_binding_pattern Y, 0 [symbolic = %Y.patt.loc26_16.2 (constants.%Y.patt)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%Y.param_patt: %J.type);
 // CHECK:STDOUT: }
@@ -1399,9 +1406,9 @@ fn NotEmptyStruct() {
 // CHECK:STDOUT:   %EmptyStruct.ref: %EmptyStruct.type = name_ref EmptyStruct, file.%EmptyStruct.decl [template = constants.%EmptyStruct]
 // CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
 // CHECK:STDOUT:   %ImplicitAs.type: type = interface_type @ImplicitAs, @ImplicitAs(constants.%J.type) [template = constants.%ImplicitAs.type.3]
-// CHECK:STDOUT:   %.loc32_14.1: %.7 = specific_constant imports.%import_ref.9, @ImplicitAs(constants.%J.type) [template = constants.%.8]
-// CHECK:STDOUT:   %Convert.ref: %.7 = name_ref Convert, %.loc32_14.1 [template = constants.%.8]
-// CHECK:STDOUT:   %.loc32_14.2: %J.type = converted %C.ref, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc39_14.1: %.7 = specific_constant imports.%import_ref.9, @ImplicitAs(constants.%J.type) [template = constants.%.8]
+// CHECK:STDOUT:   %Convert.ref: %.7 = name_ref Convert, %.loc39_14.1 [template = constants.%.8]
+// CHECK:STDOUT:   %.loc39_14.2: %J.type = converted %C.ref, <error> [template = <error>]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1445,7 +1452,7 @@ fn NotEmptyStruct() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @EmptyStruct(constants.%Y) {
-// CHECK:STDOUT:   %Y.loc22_16.2 => constants.%Y
-// CHECK:STDOUT:   %Y.patt.loc22_16.2 => constants.%Y
+// CHECK:STDOUT:   %Y.loc26_16.2 => constants.%Y
+// CHECK:STDOUT:   %Y.patt.loc26_16.2 => constants.%Y
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 1 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -228,6 +228,7 @@ CARBON_DIAGNOSTIC_KIND(DeductionIncomplete)
 CARBON_DIAGNOSTIC_KIND(DeductionInconsistent)
 CARBON_DIAGNOSTIC_KIND(DeductionGenericHere)
 CARBON_DIAGNOSTIC_KIND(InitializingGenericParam)
+CARBON_DIAGNOSTIC_KIND(CompTimeArgumentNotConstant)
 
 // Export checking.
 CARBON_DIAGNOSTIC_KIND(ExportNotImportedEntity)