소스 검색

Superficial support for `template` modifier on symbolic bindings. (#4948)

Change parse tree from `template (T:! type)` to `(template T):! type`,
so that we have information about whether a binding is a template
binding available when forming the representation of the binding
pattern. This incidentally fixes a bug that we would accept `template
addr A:! B` instead of the intended `addr template A:! B`.

Track whether a symbolic binding is a template binding on the
`EntityName` object. I'm borrowing a bit from the `CompileTimeBindIndex`
for this in order to avoid making `EntityName`s larger. Longer-term, we
should think about using a different representation for symbolic
bindings, to avoid including these fields in all `EntityName`s, but
that's out of scope for this change.

So far, template bindings are treated as having the same phase as
checked bindings, but that will change in a future PR.
Richard Smith 1 년 전
부모
커밋
6dda094928
32개의 변경된 파일705개의 추가작업 그리고 165개의 파일을 삭제
  1. 2 2
      toolchain/check/deduce.cpp
  2. 4 2
      toolchain/check/eval.cpp
  3. 2 1
      toolchain/check/generic.cpp
  4. 1 2
      toolchain/check/handle_alias.cpp
  5. 29 33
      toolchain/check/handle_binding_pattern.cpp
  6. 4 4
      toolchain/check/handle_interface.cpp
  7. 1 3
      toolchain/check/handle_where.cpp
  8. 3 9
      toolchain/check/import.cpp
  9. 12 14
      toolchain/check/import_ref.cpp
  10. 1 1
      toolchain/check/node_stack.h
  11. 1 1
      toolchain/check/subst.cpp
  12. 145 5
      toolchain/check/testdata/array/init_dependent_bound.carbon
  13. 102 0
      toolchain/check/testdata/class/no_prelude/comp_time_field.carbon
  14. 84 0
      toolchain/check/testdata/function/generic/no_prelude/template_param.carbon
  15. 25 6
      toolchain/check/testdata/interface/no_prelude/fail_assoc_const_template.carbon
  16. 1 1
      toolchain/check/testdata/interface/no_prelude/fail_incomplete_type.carbon
  17. 86 6
      toolchain/check/testdata/return/fail_let_in_type.carbon
  18. 5 1
      toolchain/diagnostics/diagnostic_kind.def
  19. 1 1
      toolchain/driver/testdata/compile/multifile_raw_and_textual_ir.carbon
  20. 1 1
      toolchain/driver/testdata/compile/multifile_raw_ir.carbon
  21. 1 1
      toolchain/driver/testdata/compile/raw_and_textual_ir.carbon
  22. 2 2
      toolchain/driver/testdata/compile/raw_ir.carbon
  23. 18 21
      toolchain/parse/handle_binding_pattern.cpp
  24. 1 1
      toolchain/parse/node_kind.def
  25. 4 17
      toolchain/parse/state.def
  26. 88 0
      toolchain/parse/testdata/generics/generic_params/fail_template_no_param.carbon
  27. 3 3
      toolchain/parse/testdata/generics/generic_params/template.carbon
  28. 29 6
      toolchain/parse/testdata/generics/generic_params/template_addr.carbon
  29. 11 12
      toolchain/parse/typed_nodes.h
  30. 28 5
      toolchain/sem_ir/entity_name.h
  31. 5 2
      toolchain/sem_ir/formatter.cpp
  32. 5 2
      toolchain/sem_ir/inst_fingerprinter.cpp

+ 2 - 2
toolchain/check/deduce.cpp

@@ -357,7 +357,7 @@ auto DeductionContext::Deduce() -> bool {
       // 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;
+        auto index = entity_name.bind_index();
         if (!index.has_value()) {
           break;
         }
@@ -396,7 +396,7 @@ auto DeductionContext::Deduce() -> bool {
       // 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;
+        auto index = entity_name.bind_index();
         if (!index.has_value() || index < first_deduced_index_ ||
             non_deduced_indexes_[index.index - first_deduced_index_.index]) {
           break;

+ 4 - 2
toolchain/check/eval.cpp

@@ -1862,7 +1862,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
       // argument of that specific, its constant value is the corresponding
       // argument value.
       if (auto value =
-              eval_context.GetCompileTimeBindValue(bind_name.bind_index);
+              eval_context.GetCompileTimeBindValue(bind_name.bind_index());
           value.has_value()) {
         return value;
       }
@@ -1871,6 +1871,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
       // original, with no equivalent value.
       bind.entity_name_id =
           eval_context.entity_names().MakeCanonical(bind.entity_name_id);
+      // TODO: Propagate the `is_template` flag into the phase.
       return MakeConstantResult(eval_context.context(), bind, Phase::Symbolic);
     }
     case CARBON_KIND(SemIR::BindSymbolicName bind): {
@@ -1885,10 +1886,11 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
         // argument of that specific, its constant value is the corresponding
         // argument value.
         if (auto value =
-                eval_context.GetCompileTimeBindValue(bind_name.bind_index);
+                eval_context.GetCompileTimeBindValue(bind_name.bind_index());
             value.has_value()) {
           return value;
         }
+        // TODO: Propagate the `is_template` flag into the phase.
         phase = Phase::Symbolic;
       }
       // The constant form of a symbolic binding is an idealized form of the

+ 2 - 1
toolchain/check/generic.cpp

@@ -115,7 +115,8 @@ class RebuildGenericConstantInEvalBlockCallbacks final
             context_.insts().TryGetAs<SemIR::BindSymbolicName>(inst_id)) {
       if (context_.entity_names()
               .Get(binding->entity_name_id)
-              .bind_index.has_value()) {
+              .bind_index()
+              .has_value()) {
         inst_id = Rebuild(inst_id, *binding);
         return true;
       }

+ 1 - 2
toolchain/check/handle_alias.cpp

@@ -36,8 +36,7 @@ auto HandleParseNode(Context& context, Parse::AliasId /*node_id*/) -> bool {
 
   auto entity_name_id = context.entity_names().Add(
       {.name_id = name_context.name_id_for_new_inst(),
-       .parent_scope_id = name_context.parent_scope_id,
-       .bind_index = SemIR::CompileTimeBindIndex::None});
+       .parent_scope_id = name_context.parent_scope_id});
 
   auto alias_type_id = SemIR::TypeId::None;
   auto alias_value_id = SemIR::InstId::None;

+ 29 - 33
toolchain/check/handle_binding_pattern.cpp

@@ -28,22 +28,16 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
   SemIR::ExprRegionId type_expr_region_id =
       EndSubpatternAsExpr(context, cast_type_inst_id);
 
-  // Every other kind of pattern binding has a name.
-  auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
-
-  // Determine whether we're handling an associated constant. These share the
-  // syntax for a compile-time binding, but don't behave like other compile-time
-  // bindings.
-  // TODO: Consider using a different parse node kind to make this easier.
-  bool is_associated_constant = false;
+  // The name in a template binding may be wrapped in `template`.
   bool is_generic = node_kind == Parse::NodeKind::CompileTimeBindingPattern;
-  if (is_generic) {
-    auto inst_id = context.scope_stack().PeekInstId();
-    is_associated_constant = inst_id.has_value() &&
-                             context.insts().Is<SemIR::InterfaceDecl>(inst_id);
-  }
+  auto is_template =
+      context.node_stack()
+          .PopAndDiscardSoloNodeIdIf<Parse::NodeKind::TemplateBindingName>();
+  // A non-generic template binding is diagnosed by the parser.
+  is_template &= is_generic;
 
-  bool needs_compile_time_binding = is_generic && !is_associated_constant;
+  // Every other kind of pattern binding has a name.
+  auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
 
   const DeclIntroducerState& introducer =
       context.decl_introducer_state_stack().innermost();
@@ -53,16 +47,12 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
     auto binding_pattern_id = SemIR::InstId::None;
     // TODO: Eventually the name will need to support associations with other
     // scopes, but right now we don't support qualified names here.
-    auto entity_name_id = context.entity_names().Add(
-        {.name_id = name_id,
-         .parent_scope_id = context.scope_stack().PeekNameScopeId(),
-         // TODO: Don't allocate a compile-time binding index for an associated
-         // constant declaration.
-         .bind_index = needs_compile_time_binding
-                           ? context.scope_stack().AddCompileTimeBinding()
-                           : SemIR::CompileTimeBindIndex::None});
+    auto entity_name_id = context.entity_names().AddSymbolicBindingName(
+        name_id, context.scope_stack().PeekNameScopeId(),
+        is_generic ? context.scope_stack().AddCompileTimeBinding()
+                   : SemIR::CompileTimeBindIndex::None,
+        is_template);
     if (is_generic) {
-      // TODO: Create a `BindTemplateName` instead inside a `template` pattern.
       bind_id = context.AddInstInNoBlock(SemIR::LocIdAndInst(
           name_node, SemIR::BindSymbolicName{.type_id = cast_type_id,
                                              .entity_name_id = entity_name_id,
@@ -83,7 +73,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
 
     // Add name to lookup immediately, so it can be used in the rest of the
     // enclosing pattern.
-    if (needs_compile_time_binding) {
+    if (is_generic) {
       context.scope_stack().PushCompileTimeBinding(bind_id);
     }
     auto name_context =
@@ -114,7 +104,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
   // so we handle it separately.
   if (auto parent_class_decl =
           context.scope_stack().GetCurrentScopeAs<SemIR::ClassDecl>();
-      parent_class_decl.has_value() &&
+      parent_class_decl.has_value() && !is_generic &&
       node_kind == Parse::NodeKind::VarBindingPattern) {
     cast_type_id = AsConcreteType(
         context, cast_type_id, type_node,
@@ -131,9 +121,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
                                          cast_type_id);
         });
     auto binding_id =
-        is_generic
-            ? Parse::NodeId::None
-            : context.parse_tree().As<Parse::VarBindingPatternId>(node_id);
+        context.parse_tree().As<Parse::VarBindingPatternId>(node_id);
     auto& class_info = context.classes().Get(parent_class_decl->class_id);
     auto field_type_id =
         context.GetUnboundElementType(class_info.self_type_id, cast_type_id);
@@ -157,12 +145,18 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
           context.scope_stack().GetCurrentScopeAs<SemIR::InterfaceDecl>();
       parent_interface_decl.has_value() && is_generic) {
     cast_type_id = AsCompleteType(context, cast_type_id, type_node, [&] {
-      CARBON_DIAGNOSTIC(IncompleteTypeInAssociatedDecl, Error,
+      CARBON_DIAGNOSTIC(IncompleteTypeInAssociatedConstantDecl, Error,
                         "associated constant has incomplete type {0}",
                         SemIR::TypeId);
-      return context.emitter().Build(type_node, IncompleteTypeInAssociatedDecl,
-                                     cast_type_id);
+      return context.emitter().Build(
+          type_node, IncompleteTypeInAssociatedConstantDecl, cast_type_id);
     });
+    if (is_template) {
+      CARBON_DIAGNOSTIC(TemplateBindingInAssociatedConstantDecl, Error,
+                        "associated constant has `template` binding");
+      context.emitter().Emit(type_node,
+                             TemplateBindingInAssociatedConstantDecl);
+    }
 
     SemIR::AssociatedConstantDecl assoc_const_decl = {
         .type_id = cast_type_id,
@@ -357,8 +351,10 @@ auto HandleParseNode(Context& context, Parse::AddrId node_id) -> bool {
   return true;
 }
 
-auto HandleParseNode(Context& context, Parse::TemplateId node_id) -> bool {
-  return context.TODO(node_id, "HandleTemplate");
+auto HandleParseNode(Context& context, Parse::TemplateBindingNameId node_id)
+    -> bool {
+  context.node_stack().Push(node_id);
+  return true;
 }
 
 }  // namespace Carbon::Check

+ 4 - 4
toolchain/check/handle_interface.cpp

@@ -169,10 +169,10 @@ auto HandleParseNode(Context& context,
   // We model `Self` as a symbolic binding whose type is the interface.
   // Because there is no equivalent non-symbolic value, we use `None` as
   // the `value_id` on the `BindSymbolicName`.
-  auto entity_name_id = context.entity_names().Add(
-      {.name_id = SemIR::NameId::SelfType,
-       .parent_scope_id = interface_info.scope_id,
-       .bind_index = context.scope_stack().AddCompileTimeBinding()});
+  auto entity_name_id = context.entity_names().AddSymbolicBindingName(
+      SemIR::NameId::SelfType, interface_info.scope_id,
+      context.scope_stack().AddCompileTimeBinding(),
+      /*is_template=*/false);
   interface_info.self_param_id =
       context.AddInst(SemIR::LocIdAndInst::NoLoc<SemIR::BindSymbolicName>(
           {.type_id = self_type_id,

+ 1 - 3
toolchain/check/handle_where.cpp

@@ -31,9 +31,7 @@ auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool {
   // expression to the left of `where`, so `MyInterface` in the example above.
   auto entity_name_id = context.entity_names().Add(
       {.name_id = SemIR::NameId::PeriodSelf,
-       .parent_scope_id = context.scope_stack().PeekNameScopeId(),
-       // `None` because this is not the parameter of a generic.
-       .bind_index = SemIR::CompileTimeBindIndex::None});
+       .parent_scope_id = context.scope_stack().PeekNameScopeId()});
   auto inst_id =
       context.AddInst(SemIR::LocIdAndInst::NoLoc<SemIR::BindSymbolicName>(
           {.type_id = self_type_id,

+ 3 - 9
toolchain/check/import.cpp

@@ -179,9 +179,7 @@ static auto CopySingleNameScopeFromImportIR(
   // Produce the namespace for the entry.
   auto make_import_id = [&]() {
     auto entity_name_id = context.entity_names().Add(
-        {.name_id = name_id,
-         .parent_scope_id = parent_scope_id,
-         .bind_index = SemIR::CompileTimeBindIndex::None});
+        {.name_id = name_id, .parent_scope_id = parent_scope_id});
     auto import_ir_inst_id = context.import_ir_insts().Add(
         {.ir_id = ir_id, .inst_id = import_inst_id});
     auto inst_id = context.AddInstInNoBlock(
@@ -273,9 +271,7 @@ static auto AddImportRefOrMerge(Context& context, SemIR::ImportIRId ir_id,
   auto& entry = parent_scope.GetEntry(entry_id);
   if (inserted) {
     auto entity_name_id = context.entity_names().Add(
-        {.name_id = name_id,
-         .parent_scope_id = parent_scope_id,
-         .bind_index = SemIR::CompileTimeBindIndex::None});
+        {.name_id = name_id, .parent_scope_id = parent_scope_id});
     entry.result = SemIR::ScopeLookupResult::MakeFound(
         AddImportRef(context, {.ir_id = ir_id, .inst_id = import_inst_id},
                      entity_name_id),
@@ -313,9 +309,7 @@ static auto AddScopedImportRef(Context& context,
                                SemIR::AccessKind access_kind) -> SemIR::InstId {
   // Add an ImportRef for other instructions.
   auto impl_entity_name_id = context.entity_names().Add(
-      {.name_id = name_id,
-       .parent_scope_id = parent_scope_id,
-       .bind_index = SemIR::CompileTimeBindIndex::None});
+      {.name_id = name_id, .parent_scope_id = parent_scope_id});
   auto import_ref_id = AddImportRef(context, import_inst, impl_entity_name_id);
   parent_scope.AddRequired({.name_id = name_id,
                             .result = SemIR::ScopeLookupResult::MakeFound(

+ 12 - 14
toolchain/check/import_ref.cpp

@@ -1024,9 +1024,7 @@ static auto GetLocalParamPatternsId(ImportContext& context,
     switch (binding.kind) {
       case SemIR::BindingPattern::Kind: {
         auto entity_name_id = context.local_entity_names().Add(
-            {.name_id = name_id,
-             .parent_scope_id = SemIR::NameScopeId::None,
-             .bind_index = SemIR::CompileTimeBindIndex::None});
+            {.name_id = name_id, .parent_scope_id = SemIR::NameScopeId::None});
         new_param_id =
             context.local_context().AddInstInNoBlock<SemIR::BindingPattern>(
                 AddImportIRInst(context, binding_id),
@@ -1279,9 +1277,7 @@ static auto AddAssociatedEntities(ImportContext& context,
     }
     auto name_id = GetLocalNameId(context, import_name_id);
     auto entity_name_id = context.local_entity_names().Add(
-        {.name_id = name_id,
-         .parent_scope_id = local_name_scope_id,
-         .bind_index = SemIR::CompileTimeBindIndex::None});
+        {.name_id = name_id, .parent_scope_id = local_name_scope_id});
     new_associated_entities.push_back(
         AddImportRef(context, inst_id, entity_name_id));
   }
@@ -1531,10 +1527,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   const auto& import_entity_name =
       resolver.import_entity_names().Get(inst.entity_name_id);
   auto name_id = GetLocalNameId(resolver, import_entity_name.name_id);
-  auto entity_name_id = resolver.local_entity_names().Add(
-      {.name_id = name_id,
-       .parent_scope_id = SemIR::NameScopeId::None,
-       .bind_index = import_entity_name.bind_index});
+  // TODO: Use the same `EntityName` for the `SymbolicBindingPattern` and the
+  // `BindSymbolicName`.
+  auto entity_name_id = resolver.local_entity_names().AddSymbolicBindingName(
+      name_id, SemIR::NameScopeId::None, import_entity_name.bind_index(),
+      import_entity_name.is_template);
   return ResolveAs<SemIR::BindSymbolicName>(
       resolver,
       {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
@@ -1553,10 +1550,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   const auto& import_entity_name =
       resolver.import_entity_names().Get(inst.entity_name_id);
   auto name_id = GetLocalNameId(resolver, import_entity_name.name_id);
-  auto entity_name_id = resolver.local_entity_names().Add(
-      {.name_id = name_id,
-       .parent_scope_id = SemIR::NameScopeId::None,
-       .bind_index = import_entity_name.bind_index});
+  // TODO: Use the same `EntityName` for the `SymbolicBindingPattern` and the
+  // `BindSymbolicName`.
+  auto entity_name_id = resolver.local_entity_names().AddSymbolicBindingName(
+      name_id, SemIR::NameScopeId::None, import_entity_name.bind_index(),
+      import_entity_name.is_template);
   return ResolveAs<SemIR::SymbolicBindingPattern>(
       resolver,
       {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),

+ 1 - 1
toolchain/check/node_stack.h

@@ -444,6 +444,7 @@ class NodeStack {
       case Parse::NodeKind::StructLiteralStart:
       case Parse::NodeKind::StructTypeLiteralField:
       case Parse::NodeKind::StructTypeLiteralStart:
+      case Parse::NodeKind::TemplateBindingName:
       case Parse::NodeKind::TupleLiteralStart:
       case Parse::NodeKind::TuplePatternStart:
       case Parse::NodeKind::VariableInitializer:
@@ -508,7 +509,6 @@ class NodeStack {
       case Parse::NodeKind::StructLiteralComma:
       case Parse::NodeKind::StructFieldDesignator:
       case Parse::NodeKind::StructTypeLiteralComma:
-      case Parse::NodeKind::Template:
       case Parse::NodeKind::TupleLiteralComma:
         return Id::Kind::Invalid;
       default:

+ 1 - 1
toolchain/check/subst.cpp

@@ -342,7 +342,7 @@ class SubstConstantCallbacks final : public SubstInstCallbacks {
     // TODO: Consider building a hash map for substitutions. We might have a
     // lot of them.
     for (auto [bind_index, replacement_id] : substitutions_) {
-      if (context_.entity_names().Get(entity_name_id).bind_index ==
+      if (context_.entity_names().Get(entity_name_id).bind_index() ==
           bind_index) {
         // This is the binding we're replacing. Perform substitution.
         inst_id = context_.constant_values().GetInstId(replacement_id);

+ 145 - 5
toolchain/check/testdata/array/init_dependent_bound.carbon

@@ -25,11 +25,11 @@ fn F(N:! i32) {
 library "[[@TEST_NAME]]";
 
 // TODO: This should be valid.
-// CHECK:STDERR: fail_todo_init_template_dependent_bound.carbon:[[@LINE+4]]:6: error: semantics TODO: `HandleTemplate` [SemanticsTodo]
-// CHECK:STDERR: fn G(template N:! i32) {
-// CHECK:STDERR:      ^~~~~~~~~~~~~~~~
-// CHECK:STDERR:
 fn G(template N:! i32) {
+  // CHECK:STDERR: fail_todo_init_template_dependent_bound.carbon:[[@LINE+4]]:23: error: cannot initialize array with dependent bound from a list of initializers [ArrayInitDependentBound]
+  // CHECK:STDERR:   var arr: [i32; N] = (1, 2, 3);
+  // CHECK:STDERR:                       ^~~~~~~~~
+  // CHECK:STDERR:
   var arr: [i32; N] = (1, 2, 3);
 }
 
@@ -138,7 +138,147 @@ fn H() { G(3); }
 // CHECK:STDOUT: --- fail_todo_init_template_dependent_bound.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %N.51e: %i32 = bind_symbolic_name N, 0, template [symbolic]
+// CHECK:STDOUT:   %N.patt.8e2: %i32 = symbolic_binding_pattern N, 0, template [symbolic]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.2fd: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete]
+// CHECK:STDOUT:   %Convert.type.71e: type = fn_type @Convert.1, @ImplicitAs(Core.IntLiteral) [concrete]
+// CHECK:STDOUT:   %impl_witness.023: <witness> = impl_witness (imports.%Core.import_ref.85c), @impl.2(%int_32) [concrete]
+// CHECK:STDOUT:   %Convert.type.4ad: type = fn_type @Convert.3, @impl.2(%int_32) [concrete]
+// CHECK:STDOUT:   %Convert.960: %Convert.type.4ad = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet.e25: %ImplicitAs.type.2fd = facet_value %i32, %impl_witness.023 [concrete]
+// CHECK:STDOUT:   %.10e: type = fn_type_with_self_type %Convert.type.71e, %ImplicitAs.facet.e25 [concrete]
+// CHECK:STDOUT:   %Convert.bound.588: <bound method> = bound_method %N.51e, %Convert.960 [symbolic]
+// CHECK:STDOUT:   %Convert.specific_fn.18b: <specific function> = specific_function %Convert.bound.588, @Convert.3(%int_32) [symbolic]
+// CHECK:STDOUT:   %int.convert_checked: init Core.IntLiteral = call %Convert.specific_fn.18b(%N.51e) [symbolic]
+// CHECK:STDOUT:   %array_type.b04: type = array_type %int.convert_checked, %i32 [symbolic]
+// CHECK:STDOUT:   %require_complete.9dc: <witness> = require_complete_type %array_type.b04 [symbolic]
+// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %int_2: Core.IntLiteral = int_value 2 [concrete]
+// CHECK:STDOUT:   %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
+// CHECK:STDOUT:   %tuple.type: type = tuple_type (Core.IntLiteral, Core.IntLiteral, Core.IntLiteral) [concrete]
+// CHECK:STDOUT:   %H.type: type = fn_type @H [concrete]
+// CHECK:STDOUT:   %H: %H.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.205: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
+// CHECK:STDOUT:   %Convert.type.1b6: type = fn_type @Convert.1, @ImplicitAs(%i32) [concrete]
+// CHECK:STDOUT:   %impl_witness.d39: <witness> = impl_witness (imports.%Core.import_ref.a5b), @impl.1(%int_32) [concrete]
+// CHECK:STDOUT:   %Convert.type.035: type = fn_type @Convert.2, @impl.1(%int_32) [concrete]
+// CHECK:STDOUT:   %Convert.956: %Convert.type.035 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet.f7f: %ImplicitAs.type.205 = facet_value Core.IntLiteral, %impl_witness.d39 [concrete]
+// CHECK:STDOUT:   %.a0b: type = fn_type_with_self_type %Convert.type.1b6, %ImplicitAs.facet.f7f [concrete]
+// CHECK:STDOUT:   %Convert.bound.b30: <bound method> = bound_method %int_3.1ba, %Convert.956 [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn.b42: <specific function> = specific_function %Convert.bound.b30, @Convert.2(%int_32) [concrete]
+// CHECK:STDOUT:   %int_3.822: %i32 = int_value 3 [concrete]
+// CHECK:STDOUT:   %G.specific_fn: <specific function> = specific_function %G, @G(%int_3.822) [concrete]
+// CHECK:STDOUT:   %Convert.bound.2d6: <bound method> = bound_method %int_3.822, %Convert.960 [concrete]
+// CHECK:STDOUT:   %Convert.specific_fn.377: <specific function> = specific_function %Convert.bound.2d6, @Convert.3(%int_32) [concrete]
+// CHECK:STDOUT:   %array_type.3fb: type = array_type %int_3.1ba, %i32 [concrete]
+// CHECK:STDOUT:   %complete_type.cbf: <witness> = complete_type_witness %array_type.3fb [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .Int = %Core.Int
+// CHECK:STDOUT:     .ImplicitAs = %Core.ImplicitAs
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:     .H = %H.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {
+// CHECK:STDOUT:     %N.patt.loc5_15.1: %i32 = symbolic_binding_pattern N, 0, template [symbolic = %N.patt.loc5_15.2 (constants.%N.patt.8e2)]
+// CHECK:STDOUT:     %N.param_patt: %i32 = value_param_pattern %N.patt.loc5_15.1, runtime_param<none> [symbolic = %N.patt.loc5_15.2 (constants.%N.patt.8e2)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %N.param: %i32 = value_param runtime_param<none>
+// CHECK:STDOUT:     %.loc5: type = splice_block %i32.loc5 [concrete = constants.%i32] {
+// CHECK:STDOUT:       %int_32.loc5: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:       %i32.loc5: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %N.loc5_15.1: %i32 = bind_symbolic_name N, 0, template, %N.param [symbolic = %N.loc5_15.2 (constants.%N.51e)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @G(%N.loc5_15.1: %i32) {
+// CHECK:STDOUT:   %N.loc5_15.2: %i32 = bind_symbolic_name N, 0, template [symbolic = %N.loc5_15.2 (constants.%N.51e)]
+// CHECK:STDOUT:   %N.patt.loc5_15.2: %i32 = symbolic_binding_pattern N, 0, template [symbolic = %N.patt.loc5_15.2 (constants.%N.patt.8e2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Convert.bound: <bound method> = bound_method %N.loc5_15.2, constants.%Convert.960 [symbolic = %Convert.bound (constants.%Convert.bound.588)]
+// CHECK:STDOUT:   %Convert.specific_fn: <specific function> = specific_function %Convert.bound, @Convert.3(constants.%int_32) [symbolic = %Convert.specific_fn (constants.%Convert.specific_fn.18b)]
+// CHECK:STDOUT:   %int.convert_checked.loc10_18.2: init Core.IntLiteral = call %Convert.specific_fn(%N.loc5_15.2) [symbolic = %int.convert_checked.loc10_18.2 (constants.%int.convert_checked)]
+// CHECK:STDOUT:   %array_type.loc10_19.2: type = array_type %int.convert_checked.loc10_18.2, %i32 [symbolic = %array_type.loc10_19.2 (constants.%array_type.b04)]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @G.%array_type.loc10_19.2 (%array_type.b04) [symbolic = %require_complete (constants.%require_complete.9dc)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%N.param_patt: %i32) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     name_binding_decl {
+// CHECK:STDOUT:       %arr.patt: @G.%array_type.loc10_19.2 (%array_type.b04) = binding_pattern arr
+// CHECK:STDOUT:       %.loc10_3: @G.%array_type.loc10_19.2 (%array_type.b04) = var_pattern %arr.patt
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %arr.var: ref @G.%array_type.loc10_19.2 (%array_type.b04) = var arr
+// CHECK:STDOUT:     %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
+// CHECK:STDOUT:     %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2]
+// CHECK:STDOUT:     %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
+// CHECK:STDOUT:     %.loc10_31: %tuple.type = tuple_literal (%int_1, %int_2, %int_3)
+// CHECK:STDOUT:     assign %arr.var, <error>
+// CHECK:STDOUT:     %.loc10_19: type = splice_block %array_type.loc10_19.1 [symbolic = %array_type.loc10_19.2 (constants.%array_type.b04)] {
+// CHECK:STDOUT:       %int_32.loc10: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:       %i32.loc10: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:       %N.ref: %i32 = name_ref N, %N.loc5_15.1 [symbolic = %N.loc5_15.2 (constants.%N.51e)]
+// CHECK:STDOUT:       %impl.elem0: %.10e = impl_witness_access constants.%impl_witness.023, element0 [concrete = constants.%Convert.960]
+// CHECK:STDOUT:       %bound_method: <bound method> = bound_method %N.ref, %impl.elem0 [symbolic = %Convert.bound (constants.%Convert.bound.588)]
+// CHECK:STDOUT:       %specific_fn: <specific function> = specific_function %bound_method, @Convert.3(constants.%int_32) [symbolic = %Convert.specific_fn (constants.%Convert.specific_fn.18b)]
+// CHECK:STDOUT:       %int.convert_checked.loc10_18.1: init Core.IntLiteral = call %specific_fn(%N.ref) [symbolic = %int.convert_checked.loc10_18.2 (constants.%int.convert_checked)]
+// CHECK:STDOUT:       %.loc10_18.1: Core.IntLiteral = value_of_initializer %int.convert_checked.loc10_18.1 [symbolic = %int.convert_checked.loc10_18.2 (constants.%int.convert_checked)]
+// CHECK:STDOUT:       %.loc10_18.2: Core.IntLiteral = converted %N.ref, %.loc10_18.1 [symbolic = %int.convert_checked.loc10_18.2 (constants.%int.convert_checked)]
+// CHECK:STDOUT:       %array_type.loc10_19.1: type = array_type %.loc10_18.2, %i32 [symbolic = %array_type.loc10_19.2 (constants.%array_type.b04)]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %arr: ref @G.%array_type.loc10_19.2 (%array_type.b04) = bind_name arr, %arr.var
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: fn @H() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G]
+// CHECK:STDOUT:   %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
+// CHECK:STDOUT:   %impl.elem0: %.a0b = impl_witness_access constants.%impl_witness.d39, element0 [concrete = constants.%Convert.956]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_3, %impl.elem0 [concrete = constants.%Convert.bound.b30]
+// CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %bound_method, @Convert.2(constants.%int_32) [concrete = constants.%Convert.specific_fn.b42]
+// CHECK:STDOUT:   %int.convert_checked: init %i32 = call %specific_fn(%int_3) [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %.loc13_13.1: %i32 = value_of_initializer %int.convert_checked [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %.loc13_13.2: %i32 = converted %int_3, %.loc13_13.1 [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %G.specific_fn: <specific function> = specific_function %G.ref, @G(constants.%int_3.822) [concrete = constants.%G.specific_fn]
+// CHECK:STDOUT:   %G.call: init %empty_tuple.type = call %G.specific_fn()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @G(constants.%N.51e) {
+// CHECK:STDOUT:   %N.loc5_15.2 => constants.%N.51e
+// CHECK:STDOUT:   %N.patt.loc5_15.2 => constants.%N.51e
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @G(constants.%int_3.822) {
+// CHECK:STDOUT:   %N.loc5_15.2 => constants.%int_3.822
+// CHECK:STDOUT:   %N.patt.loc5_15.2 => constants.%int_3.822
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Convert.bound => constants.%Convert.bound.2d6
+// CHECK:STDOUT:   %Convert.specific_fn => constants.%Convert.specific_fn.377
+// CHECK:STDOUT:   %int.convert_checked.loc10_18.2 => constants.%int_3.1ba
+// CHECK:STDOUT:   %array_type.loc10_19.2 => constants.%array_type.3fb
+// CHECK:STDOUT:   %require_complete => constants.%complete_type.cbf
+// CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 102 - 0
toolchain/check/testdata/class/no_prelude/comp_time_field.carbon

@@ -0,0 +1,102 @@
+// 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/class/no_prelude/comp_time_field.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/comp_time_field.carbon
+
+// --- fail_let.carbon
+
+library "[[@TEST_NAME]]";
+
+class Class {
+  // CHECK:STDERR: fail_let.carbon:[[@LINE+4]]:7: error: semantics TODO: ``let` compile time binding outside function or interface` [SemanticsTodo]
+  // CHECK:STDERR:   let A:! type = Class;
+  // CHECK:STDERR:       ^~~~~~~~
+  // CHECK:STDERR:
+  let A:! type = Class;
+
+  // CHECK:STDERR: fail_let.carbon:[[@LINE+4]]:7: error: semantics TODO: ``let` compile time binding outside function or interface` [SemanticsTodo]
+  // CHECK:STDERR:   let template B:! type = Class;
+  // CHECK:STDERR:       ^~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  let template B:! type = Class;
+}
+
+// --- fail_var.carbon
+
+library "[[@TEST_NAME]]";
+
+class Class {
+  // CHECK:STDERR: fail_var.carbon:[[@LINE+8]]:7: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
+  // CHECK:STDERR:   var C:! type = Class;
+  // CHECK:STDERR:       ^~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:16: error: `var` declaration cannot declare a compile-time binding [CompileTimeBindingInVarDecl]
+  // CHECK:STDERR:   var C:! type = Class;
+  // CHECK:STDERR:                ^
+  // CHECK:STDERR:
+  var C:! type = Class;
+
+  // CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:25: error: `var` declaration cannot declare a compile-time binding [CompileTimeBindingInVarDecl]
+  // CHECK:STDERR:   var template D:! type = Class;
+  // CHECK:STDERR:                         ^
+  // CHECK:STDERR:
+  var template D:! type = Class;
+}
+
+var x: Class = {};
+
+// CHECK:STDOUT: --- fail_let.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Class: type = class_type @Class [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Class = %Class.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %A.patt: type = binding_pattern A
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Class.ref.loc9: type = name_ref Class, file.%Class.decl [concrete = constants.%Class]
+// CHECK:STDOUT:   %A: type = bind_name A, %Class.ref.loc9
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %B.patt: type = binding_pattern B
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Class.ref.loc15: type = name_ref Class, file.%Class.decl [concrete = constants.%Class]
+// CHECK:STDOUT:   %B: type = bind_name B, %Class.ref.loc15
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Class
+// CHECK:STDOUT:   .A = %A
+// CHECK:STDOUT:   .B = %B
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_var.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Class: type = class_type @Class [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   complete_type_witness = invalid
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Class
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 84 - 0
toolchain/check/testdata/function/generic/no_prelude/template_param.carbon

@@ -0,0 +1,84 @@
+// 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/no_prelude/template_param.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/no_prelude/template_param.carbon
+
+// --- fn.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F(template T:! type) {
+}
+
+fn G() {
+  F({});
+}
+
+// CHECK:STDOUT: --- fn.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0, template [symbolic]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0, template [symbolic]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %F.specific_fn: <specific function> = specific_function %F, @F(%empty_struct_type) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %T.patt.loc4_15.1: type = symbolic_binding_pattern T, 0, template [symbolic = %T.patt.loc4_15.2 (constants.%T.patt)]
+// CHECK:STDOUT:     %T.param_patt: type = value_param_pattern %T.patt.loc4_15.1, runtime_param<none> [symbolic = %T.patt.loc4_15.2 (constants.%T.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %T.param: type = value_param runtime_param<none>
+// CHECK:STDOUT:     %T.loc4_15.1: type = bind_symbolic_name T, 0, template, %T.param [symbolic = %T.loc4_15.2 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @F(%T.loc4_15.1: type) {
+// CHECK:STDOUT:   %T.loc4_15.2: type = bind_symbolic_name T, 0, template [symbolic = %T.loc4_15.2 (constants.%T)]
+// CHECK:STDOUT:   %T.patt.loc4_15.2: type = symbolic_binding_pattern T, 0, template [symbolic = %T.patt.loc4_15.2 (constants.%T.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%T.param_patt: type) {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     return
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
+// CHECK:STDOUT:   %.loc8_6: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc8_7: type = converted %.loc8_6, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   %F.specific_fn: <specific function> = specific_function %F.ref, @F(constants.%empty_struct_type) [concrete = constants.%F.specific_fn]
+// CHECK:STDOUT:   %F.call: init %empty_tuple.type = call %F.specific_fn()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(constants.%T) {
+// CHECK:STDOUT:   %T.loc4_15.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc4_15.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(constants.%empty_struct_type) {
+// CHECK:STDOUT:   %T.loc4_15.2 => constants.%empty_struct_type
+// CHECK:STDOUT:   %T.patt.loc4_15.2 => constants.%empty_struct_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 25 - 6
toolchain/check/testdata/interface/no_prelude/fail_assoc_const_template.carbon

@@ -9,9 +9,9 @@
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interface/no_prelude/fail_assoc_const_template.carbon
 
 interface I {
-  // CHECK:STDERR: fail_assoc_const_template.carbon:[[@LINE+4]]:7: error: semantics TODO: `HandleTemplate` [SemanticsTodo]
+  // CHECK:STDERR: fail_assoc_const_template.carbon:[[@LINE+4]]:20: error: associated constant has `template` binding [TemplateBindingInAssociatedConstantDecl]
   // CHECK:STDERR:   let template T:! type;
-  // CHECK:STDERR:       ^~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:                    ^~~~
   // CHECK:STDERR:
   let template T:! type;
 }
@@ -19,15 +19,34 @@ interface I {
 // CHECK:STDOUT: --- fail_assoc_const_template.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [concrete]
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %I.assoc_type: type = assoc_entity_type %I.type [concrete]
+// CHECK:STDOUT:   %assoc0: %I.assoc_type = assoc_entity element0, @I.%T [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {}
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @I {
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:   %T: type = assoc_const_decl @T [concrete] {
+// CHECK:STDOUT:     %assoc0: %I.assoc_type = assoc_entity element0, @I.%T [concrete = constants.%assoc0]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:
 // CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = <unexpected>.inst15
-// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .T = @T.%assoc0
+// CHECK:STDOUT:   witness = (%T)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic assoc_const @T(@I.%Self: %I.type) {
+// CHECK:STDOUT:   assoc_const T:! type;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: assoc_const @T T:! type;
+// CHECK:STDOUT: specific @T(constants.%Self) {}
 // CHECK:STDOUT:

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

@@ -11,7 +11,7 @@
 class C;
 
 interface I {
-  // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE+7]]:11: error: associated constant has incomplete type `C` [IncompleteTypeInAssociatedDecl]
+  // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE+7]]:11: error: associated constant has incomplete type `C` [IncompleteTypeInAssociatedConstantDecl]
   // CHECK:STDERR:   let T:! C;
   // CHECK:STDERR:           ^
   // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE-6]]:1: note: class was forward declared here [ClassForwardDeclaredHere]

+ 86 - 6
toolchain/check/testdata/return/fail_let_in_type.carbon

@@ -28,24 +28,87 @@ let y:! type = i32;
 fn HalfDozen() -> y { return 6; }
 
 // TODO: This should work.
-// CHECK:STDERR: fail_let_in_type.carbon:[[@LINE+8]]:14: error: semantics TODO: ``let` compile time binding outside function or interface` [SemanticsTodo]
-// CHECK:STDERR: let template z:! type = i32;
-// CHECK:STDERR:              ^~~~~~~~
-// CHECK:STDERR:
-// CHECK:STDERR: fail_let_in_type.carbon:[[@LINE+4]]:5: error: semantics TODO: `HandleTemplate` [SemanticsTodo]
+// CHECK:STDERR: fail_let_in_type.carbon:[[@LINE+4]]:5: error: semantics TODO: ``let` compile time binding outside function or interface` [SemanticsTodo]
 // CHECK:STDERR: let template z:! type = i32;
 // CHECK:STDERR:     ^~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 let template z:! type = i32;
+// CHECK:STDERR: fail_let_in_type.carbon:[[@LINE+4]]:28: error: cannot evaluate type expression [TypeExprEvaluationFailure]
+// CHECK:STDERR: fn FirstPerfectNumber() -> z { return 6; }
+// CHECK:STDERR:                            ^
+// CHECK:STDERR:
 fn FirstPerfectNumber() -> z { return 6; }
 
 // CHECK:STDOUT: --- fail_let_in_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %Six.type: type = fn_type @Six [concrete]
+// CHECK:STDOUT:   %Six: %Six.type = struct_value () [concrete]
 // CHECK:STDOUT:   %int_6: Core.IntLiteral = int_value 6 [concrete]
+// CHECK:STDOUT:   %HalfDozen.type: type = fn_type @HalfDozen [concrete]
+// CHECK:STDOUT:   %HalfDozen: %HalfDozen.type = struct_value () [concrete]
+// CHECK:STDOUT:   %FirstPerfectNumber.type: type = fn_type @FirstPerfectNumber [concrete]
+// CHECK:STDOUT:   %FirstPerfectNumber: %FirstPerfectNumber.type = struct_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .Int = %Core.Int
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .x = %x
+// CHECK:STDOUT:     .Six = %Six.decl
+// CHECK:STDOUT:     .y = %y
+// CHECK:STDOUT:     .HalfDozen = %HalfDozen.decl
+// CHECK:STDOUT:     .z = %z
+// CHECK:STDOUT:     .FirstPerfectNumber = %FirstPerfectNumber.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: type = binding_pattern x
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %x: type = bind_name x, @__global_init.%i32.loc11
+// CHECK:STDOUT:   %Six.decl: %Six.type = fn_decl @Six [concrete = constants.%Six] {
+// CHECK:STDOUT:     %return.patt: <error> = return_slot_pattern
+// CHECK:STDOUT:     %return.param_patt: <error> = out_param_pattern %return.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %x.ref: type = name_ref x, file.%x
+// CHECK:STDOUT:     %return.param: ref <error> = out_param runtime_param0
+// CHECK:STDOUT:     %return: ref <error> = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %y.patt: type = binding_pattern y
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %y: type = bind_name y, @__global_init.%i32.loc23
+// CHECK:STDOUT:   %HalfDozen.decl: %HalfDozen.type = fn_decl @HalfDozen [concrete = constants.%HalfDozen] {
+// CHECK:STDOUT:     %return.patt: <error> = return_slot_pattern
+// CHECK:STDOUT:     %return.param_patt: <error> = out_param_pattern %return.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %y.ref: type = name_ref y, file.%y
+// CHECK:STDOUT:     %return.param: ref <error> = out_param runtime_param0
+// CHECK:STDOUT:     %return: ref <error> = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %z.patt: type = binding_pattern z
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %z: type = bind_name z, @__global_init.%i32.loc35
+// CHECK:STDOUT:   %FirstPerfectNumber.decl: %FirstPerfectNumber.type = fn_decl @FirstPerfectNumber [concrete = constants.%FirstPerfectNumber] {
+// CHECK:STDOUT:     %return.patt: <error> = return_slot_pattern
+// CHECK:STDOUT:     %return.param_patt: <error> = out_param_pattern %return.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %z.ref: type = name_ref z, file.%z
+// CHECK:STDOUT:     %return.param: ref <error> = out_param runtime_param0
+// CHECK:STDOUT:     %return: ref <error> = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Six() -> <error> {
 // CHECK:STDOUT: !entry:
@@ -59,3 +122,20 @@ fn FirstPerfectNumber() -> z { return 6; }
 // CHECK:STDOUT:   return <error>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: fn @FirstPerfectNumber() -> <error> {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %int_6: Core.IntLiteral = int_value 6 [concrete = constants.%int_6]
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %int_32.loc11: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:   %i32.loc11: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:   %int_32.loc23: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:   %i32.loc23: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:   %int_32.loc35: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:   %i32.loc35: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 5 - 1
toolchain/diagnostics/diagnostic_kind.def

@@ -82,6 +82,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedExpr)
 CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterPeriodOrArrow)
 CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierOrSelfAfterPeriod)
 CARBON_DIAGNOSTIC_KIND(ExpectedBindingPattern)
+CARBON_DIAGNOSTIC_KIND(ExpectedGenericBindingPatternAfterTemplate)
 CARBON_DIAGNOSTIC_KIND(ExpectedParenAfter)
 CARBON_DIAGNOSTIC_KIND(ExpectedExprSemi)
 CARBON_DIAGNOSTIC_KIND(ExpectedStatementSemi)
@@ -298,6 +299,9 @@ CARBON_DIAGNOSTIC_KIND(MissingImplInMemberAccessNote)
 CARBON_DIAGNOSTIC_KIND(ExpectedInitializerAfterLet)
 CARBON_DIAGNOSTIC_KIND(ExpectedSymbolicBindingInAssociatedConstant)
 
+// Pattern checking.
+CARBON_DIAGNOSTIC_KIND(TemplateBindingInAssociatedConstantDecl)
+
 // Qualified declaration name checking.
 CARBON_DIAGNOSTIC_KIND(QualifiedDeclOutsidePackage)
 CARBON_DIAGNOSTIC_KIND(QualifiedDeclOutsidePackageSource)
@@ -356,7 +360,7 @@ CARBON_DIAGNOSTIC_KIND(NameUseBeforeDecl)
 CARBON_DIAGNOSTIC_KIND(NameUseBeforeDeclNote)
 CARBON_DIAGNOSTIC_KIND(RepeatedConst)
 CARBON_DIAGNOSTIC_KIND(IncompleteTypeInAdaptDecl)
-CARBON_DIAGNOSTIC_KIND(IncompleteTypeInAssociatedDecl)
+CARBON_DIAGNOSTIC_KIND(IncompleteTypeInAssociatedConstantDecl)
 CARBON_DIAGNOSTIC_KIND(IncompleteTypeInBaseDecl)
 CARBON_DIAGNOSTIC_KIND(IncompleteTypeInBindingDecl)
 CARBON_DIAGNOSTIC_KIND(IncompleteTypeInConversion)

+ 1 - 1
toolchain/driver/testdata/compile/multifile_raw_and_textual_ir.carbon

@@ -110,7 +110,7 @@ fn B() {
 // CHECK:STDOUT:     name_scope0:     {inst: inst12, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name1: inst14, name0: inst15}}
 // CHECK:STDOUT:     name_scope1:     {inst: inst14, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst20}}
 // CHECK:STDOUT:   entity_names:
-// CHECK:STDOUT:     entity_name0:    {name: name1, parent_scope: name_scope1, index: comp_time_bind<none>}
+// CHECK:STDOUT:     entity_name0:    {name: name1, parent_scope: name_scope1, index: -1, is_template: 0}
 // CHECK:STDOUT:   functions:
 // CHECK:STDOUT:     function0:       {name: name0, parent_scope: name_scope0, body: [inst_block5]}
 // CHECK:STDOUT:     function1:       {name: name1, parent_scope: name_scope1}

+ 1 - 1
toolchain/driver/testdata/compile/multifile_raw_ir.carbon

@@ -90,7 +90,7 @@ fn B() {
 // CHECK:STDOUT:     name_scope0:     {inst: inst12, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name1: inst14, name0: inst15}}
 // CHECK:STDOUT:     name_scope1:     {inst: inst14, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst20}}
 // CHECK:STDOUT:   entity_names:
-// CHECK:STDOUT:     entity_name0:    {name: name1, parent_scope: name_scope1, index: comp_time_bind<none>}
+// CHECK:STDOUT:     entity_name0:    {name: name1, parent_scope: name_scope1, index: -1, is_template: 0}
 // CHECK:STDOUT:   functions:
 // CHECK:STDOUT:     function0:       {name: name0, parent_scope: name_scope0, body: [inst_block5]}
 // CHECK:STDOUT:     function1:       {name: name1, parent_scope: name_scope1}

+ 1 - 1
toolchain/driver/testdata/compile/raw_and_textual_ir.carbon

@@ -25,7 +25,7 @@ fn Foo(n: ()) -> ((), ()) {
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst12, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst32}}
 // CHECK:STDOUT:   entity_names:
-// CHECK:STDOUT:     entity_name0:    {name: name1, parent_scope: name_scope<none>, index: comp_time_bind<none>}
+// CHECK:STDOUT:     entity_name0:    {name: name1, parent_scope: name_scope<none>, index: -1, is_template: 0}
 // CHECK:STDOUT:   functions:
 // CHECK:STDOUT:     function0:       {name: name0, parent_scope: name_scope0, return_slot_pattern: inst27, body: [inst_block10]}
 // CHECK:STDOUT:   classes:         {}

+ 2 - 2
toolchain/driver/testdata/compile/raw_ir.carbon

@@ -25,8 +25,8 @@ fn Foo[T:! type](n: T) -> (T, ()) {
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst12, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst36}}
 // CHECK:STDOUT:   entity_names:
-// CHECK:STDOUT:     entity_name0:    {name: name1, parent_scope: name_scope<none>, index: comp_time_bind0}
-// CHECK:STDOUT:     entity_name1:    {name: name2, parent_scope: name_scope<none>, index: comp_time_bind<none>}
+// CHECK:STDOUT:     entity_name0:    {name: name1, parent_scope: name_scope<none>, index: 0, is_template: 0}
+// CHECK:STDOUT:     entity_name1:    {name: name2, parent_scope: name_scope<none>, index: -1, is_template: 0}
 // CHECK:STDOUT:   functions:
 // CHECK:STDOUT:     function0:       {name: name0, parent_scope: name_scope0, return_slot_pattern: inst31, body: [inst_block16]}
 // CHECK:STDOUT:   classes:         {}

+ 18 - 21
toolchain/parse/handle_binding_pattern.cpp

@@ -11,15 +11,8 @@ namespace Carbon::Parse {
 auto HandleBindingPattern(Context& context) -> void {
   auto state = context.PopState();
 
-  // Parameters may have keywords prefixing the pattern. They become the parent
-  // for the full BindingPattern.
-  if (auto token = context.ConsumeIf(Lex::TokenKind::Template)) {
-    context.PushState({.state = State::BindingPatternTemplate,
-                       .in_var_pattern = state.in_var_pattern,
-                       .token = *token,
-                       .subtree_start = state.subtree_start});
-  }
-
+  // An `addr` pattern may wrap the binding, and becomes the parent of the
+  // `BindingPattern`.
   if (auto token = context.ConsumeIf(Lex::TokenKind::Addr)) {
     context.PushState({.state = State::BindingPatternAddr,
                        .in_var_pattern = state.in_var_pattern,
@@ -39,6 +32,9 @@ auto HandleBindingPattern(Context& context) -> void {
     }
   };
 
+  // A `template` keyword may precede the name.
+  auto template_token = context.ConsumeIf(Lex::TokenKind::Template);
+
   // The first item should be an identifier or `self`.
   bool has_name = false;
   if (auto identifier = context.ConsumeIf(Lex::TokenKind::Identifier)) {
@@ -57,9 +53,21 @@ auto HandleBindingPattern(Context& context) -> void {
                         *context.position(), /*has_error=*/true);
     on_error(/*expected_name=*/true);
   }
-
   if (auto kind = context.PositionKind();
       kind == Lex::TokenKind::Colon || kind == Lex::TokenKind::ColonExclaim) {
+    // Add the wrapper node for the `template` keyword if present.
+    if (template_token) {
+      if (kind != Lex::TokenKind::ColonExclaim && !state.has_error) {
+        CARBON_DIAGNOSTIC(ExpectedGenericBindingPatternAfterTemplate, Error,
+                          "expected `:!` binding after `template`");
+        context.emitter().Emit(*template_token,
+                               ExpectedGenericBindingPatternAfterTemplate);
+        state.has_error = true;
+      }
+      context.AddNode(NodeKind::TemplateBindingName, *template_token,
+                      state.has_error);
+    }
+
     state.state = kind == Lex::TokenKind::Colon
                       ? State::BindingPatternFinishAsRegular
                       : State::BindingPatternFinishAsGeneric;
@@ -125,15 +133,4 @@ auto HandleBindingPatternAddr(Context& context) -> void {
   }
 }
 
-auto HandleBindingPatternTemplate(Context& context) -> void {
-  auto state = context.PopState();
-
-  context.AddNode(NodeKind::Template, state.token, state.has_error);
-
-  // If an error was encountered, propagate it while adding a node.
-  if (state.has_error) {
-    context.ReturnErrorOnState();
-  }
-}
-
 }  // namespace Carbon::Parse

+ 1 - 1
toolchain/parse/node_kind.def

@@ -160,9 +160,9 @@ CARBON_PARSE_NODE_KIND(ArrayExpr)
 
 CARBON_PARSE_NODE_KIND(LetBindingPattern)
 CARBON_PARSE_NODE_KIND(VarBindingPattern)
+CARBON_PARSE_NODE_KIND(TemplateBindingName)
 CARBON_PARSE_NODE_KIND(CompileTimeBindingPattern)
 CARBON_PARSE_NODE_KIND(Addr)
-CARBON_PARSE_NODE_KIND(Template)
 
 CARBON_PARSE_NODE_KIND(LetIntroducer)
 CARBON_PARSE_NODE_KIND(LetInitializer)

+ 4 - 17
toolchain/parse/state.def

@@ -992,12 +992,6 @@ CARBON_PARSE_STATE(Pattern)
 // Handles the initial part of a binding pattern, enqueuing type expression
 // processing.
 //
-// template    (variant is not Variable)
-// ^~~~~~~~
-//   4. BindingPatternTemplate
-//
-// THEN
-//
 // addr        (variant is not Variable)
 // ^~~~
 //   3. BindingPatternAddr
@@ -1011,10 +1005,10 @@ CARBON_PARSE_STATE(Pattern)
 //   1. Expr
 //   2. BindingPatternFinishAsRegular
 //
-// name:! ...
-// ^~~~~~
-// self:! ...
-// ^~~~~~
+// [template] name:! ...
+// ^~~~~~~~~~~~~~~~~
+// [template] self:! ...
+// ^~~~~~~~~~~~~~~~~
 //   1. Expr
 //   2. BindingPatternFinishAsGeneric
 //
@@ -1030,13 +1024,6 @@ CARBON_PARSE_STATE(BindingPattern)
 //   (state done)
 CARBON_PARSE_STATE(BindingPatternAddr)
 
-// Handles `template` in a binding pattern.
-//
-// template name:! type
-//                     ^
-//   (state done)
-CARBON_PARSE_STATE(BindingPatternTemplate)
-
 // Finishes binding pattern processing.
 //
 // name: type

+ 88 - 0
toolchain/parse/testdata/generics/generic_params/fail_template_no_param.carbon

@@ -0,0 +1,88 @@
+// 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/parse/testdata/generics/generic_params/fail_template_no_param.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/generic_params/fail_template_no_param.carbon
+
+// CHECK:STDERR: fail_template_no_param.carbon:[[@LINE+4]]:15: error: expected name in binding pattern [ExpectedBindingPattern]
+// CHECK:STDERR: fn A(template i32);
+// CHECK:STDERR:               ^~~
+// CHECK:STDERR:
+fn A(template i32);
+
+// CHECK:STDERR: fail_template_no_param.carbon:[[@LINE+4]]:6: error: expected `:!` binding after `template` [ExpectedGenericBindingPatternAfterTemplate]
+// CHECK:STDERR: fn B(template a: i32);
+// CHECK:STDERR:      ^~~~~~~~
+// CHECK:STDERR:
+fn B(template a: i32);
+
+// CHECK:STDERR: fail_template_no_param.carbon:[[@LINE+4]]:14: error: expected name in binding pattern [ExpectedBindingPattern]
+// CHECK:STDERR: fn C(template);
+// CHECK:STDERR:              ^
+// CHECK:STDERR:
+fn C(template);
+
+// CHECK:STDERR: fail_template_no_param.carbon:[[@LINE+4]]:14: error: expected name in binding pattern [ExpectedBindingPattern]
+// CHECK:STDERR: fn D(template:! i32);
+// CHECK:STDERR:              ^~
+// CHECK:STDERR:
+fn D(template:! i32);
+
+// CHECK:STDERR: fail_template_no_param.carbon:[[@LINE+4]]:14: error: expected name in binding pattern [ExpectedBindingPattern]
+// CHECK:STDERR: fn E(template:! i32);
+// CHECK:STDERR:              ^~
+// CHECK:STDERR:
+fn E(template:! i32);
+
+// CHECK:STDOUT: - filename: fail_template_no_param.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'A'},
+// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'i32', has_error: yes},
+// CHECK:STDOUT:           {kind: 'InvalidParse', text: 'i32', has_error: yes},
+// CHECK:STDOUT:         {kind: 'LetBindingPattern', text: 'template', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
+// CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'B'},
+// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
+// CHECK:STDOUT:           {kind: 'TemplateBindingName', text: 'template', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 9},
+// CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'C'},
+// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: ')', has_error: yes},
+// CHECK:STDOUT:           {kind: 'InvalidParse', text: ')', has_error: yes},
+// CHECK:STDOUT:         {kind: 'LetBindingPattern', text: 'template', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
+// CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'D'},
+// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: ':!', has_error: yes},
+// CHECK:STDOUT:           {kind: 'TemplateBindingName', text: 'template', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 9},
+// CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'E'},
+// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: ':!', has_error: yes},
+// CHECK:STDOUT:           {kind: 'TemplateBindingName', text: 'template', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 3 - 3
toolchain/parse/testdata/generics/generic_params/template.carbon

@@ -17,9 +17,9 @@ fn foo(template a:! i32);
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
 // CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
-// CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'Template', text: 'template', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'TemplateBindingName', text: 'template', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 29 - 6
toolchain/parse/testdata/generics/generic_params/template_addr.carbon

@@ -8,19 +8,42 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/generic_params/template_addr.carbon
 
+// --- fail_wrong_order.carbon
+
+// CHECK:STDERR: fail_wrong_order.carbon:[[@LINE+4]]:17: error: expected name in binding pattern [ExpectedBindingPattern]
+// CHECK:STDERR: fn foo(template addr a:! i32);
+// CHECK:STDERR:                 ^~~~
+// CHECK:STDERR:
 fn foo(template addr a:! i32);
 
-// CHECK:STDOUT: - filename: template_addr.carbon
+// --- correct_order.carbon
+
+fn bar(addr template b:! i32);
+
+// CHECK:STDOUT: - filename: fail_wrong_order.carbon
 // CHECK:STDOUT:   parse_tree: [
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
 // CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
-// CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:             {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'Addr', text: 'addr', subtree_size: 4},
-// CHECK:STDOUT:         {kind: 'Template', text: 'template', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'addr', has_error: yes},
+// CHECK:STDOUT:           {kind: 'InvalidParse', text: 'addr', has_error: yes},
+// CHECK:STDOUT:         {kind: 'LetBindingPattern', text: 'template', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: correct_order.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'bar'},
+// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
+// CHECK:STDOUT:             {kind: 'TemplateBindingName', text: 'template', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
+// CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'Addr', text: 'addr', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 7},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 10},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 11 - 12
toolchain/parse/typed_nodes.h

@@ -322,12 +322,22 @@ struct VarBindingPattern {
   AnyExprId type;
 };
 
+// A template binding name: `template T`.
+struct TemplateBindingName {
+  static constexpr auto Kind =
+      NodeKind::TemplateBindingName.Define({.child_count = 1});
+
+  Lex::TemplateTokenIndex token;
+  NodeIdOneOf<IdentifierNameNotBeforeParams, SelfValueName> name;
+};
+
 // `name:! Type`
 struct CompileTimeBindingPattern {
   static constexpr auto Kind = NodeKind::CompileTimeBindingPattern.Define(
       {.category = NodeCategory::Pattern, .child_count = 2});
 
-  NodeIdOneOf<IdentifierNameNotBeforeParams, SelfValueName> name;
+  NodeIdOneOf<IdentifierNameNotBeforeParams, SelfValueName, TemplateBindingName>
+      name;
   Lex::ColonExclaimTokenIndex token;
   AnyExprId type;
 };
@@ -341,17 +351,6 @@ struct Addr {
   AnyPatternId inner;
 };
 
-// A template binding: `template T:! type`.
-struct Template {
-  static constexpr auto Kind = NodeKind::Template.Define(
-      {.category = NodeCategory::Pattern, .child_count = 1});
-
-  Lex::TemplateTokenIndex token;
-  // This is a CompileTimeBindingPatternId in any valid program.
-  // TODO: Should the parser enforce that?
-  AnyPatternId inner;
-};
-
 using TuplePatternStart =
     LeafNode<NodeKind::TuplePatternStart, Lex::OpenParenTokenIndex>;
 using PatternListComma =

+ 28 - 5
toolchain/sem_ir/entity_name.h

@@ -15,7 +15,8 @@ namespace Carbon::SemIR {
 struct EntityName : public Printable<EntityName> {
   auto Print(llvm::raw_ostream& out) const -> void {
     out << "{name: " << name_id << ", parent_scope: " << parent_scope_id
-        << ", index: " << bind_index << "}";
+        << ", index: " << bind_index_value << ", is_template: " << is_template
+        << "}";
   }
 
   friend auto CarbonHashtableEq(const EntityName& lhs, const EntityName& rhs)
@@ -23,14 +24,26 @@ struct EntityName : public Printable<EntityName> {
     return std::memcmp(&lhs, &rhs, sizeof(EntityName)) == 0;
   }
 
+  // The index of the binding, if this is the name of a symbolic binding, or
+  // `None` otherwise. This is also `None` for a `.Self` symbolic binding,
+  // because such a binding is not assigned an index.
+  auto bind_index() const -> CompileTimeBindIndex {
+    return CompileTimeBindIndex(bind_index_value);
+  }
+
   // The name.
   NameId name_id;
   // The parent scope.
   NameScopeId parent_scope_id;
-  // The index for a compile-time binding. Invalid for a runtime binding, or
-  // for a symbolic binding, like `.Self`, that does not correspond to a generic
-  // parameter (and therefore has no index).
-  CompileTimeBindIndex bind_index;
+
+  // TODO: The following two fields are only meaningful for a symbolic binding.
+  // Consider splitting them off into a separate type so that we don't store
+  // them for other kinds of `EntityName`.
+
+  // The bind_index() value, unwrapped so it can be stored in a bit-field.
+  int32_t bind_index_value : 31 = CompileTimeBindIndex::None.index;
+  // Whether this binding is a template parameter.
+  bool is_template : 1 = false;
 };
 
 // Hashing for EntityName. See common/hashing.h.
@@ -45,6 +58,16 @@ inline auto CarbonHashValue(const EntityName& value, uint64_t seed)
 // functionality, this can provide optional canonical IDs for EntityNames.
 struct EntityNameStore : public ValueStore<EntityNameId> {
  public:
+  // Adds an entity name for a symbolic binding.
+  auto AddSymbolicBindingName(NameId name_id, NameScopeId parent_scope_id,
+                              CompileTimeBindIndex bind_index, bool is_template)
+      -> EntityNameId {
+    return Add({.name_id = name_id,
+                .parent_scope_id = parent_scope_id,
+                .bind_index_value = bind_index.index,
+                .is_template = is_template});
+  }
+
   // Convert an ID to a canonical ID. All calls to this with equivalent
   // `EntityName`s will return the same `EntityNameId`.
   auto MakeCanonical(EntityNameId id) -> EntityNameId;

+ 5 - 2
toolchain/sem_ir/formatter.cpp

@@ -1188,8 +1188,11 @@ class FormatterImpl {
   auto FormatArg(EntityNameId id) -> void {
     const auto& info = sem_ir_->entity_names().Get(id);
     FormatName(info.name_id);
-    if (info.bind_index.has_value()) {
-      out_ << ", " << info.bind_index.index;
+    if (info.bind_index().has_value()) {
+      out_ << ", " << info.bind_index().index;
+    }
+    if (info.is_template) {
+      out_ << ", template";
     }
   }
 

+ 5 - 2
toolchain/sem_ir/inst_fingerprinter.cpp

@@ -73,11 +73,14 @@ struct Worklist {
       return;
     }
     const auto& entity_name = sem_ir->entity_names().Get(entity_name_id);
-    if (entity_name.bind_index.has_value()) {
-      Add(entity_name.bind_index);
+    if (entity_name.bind_index().has_value()) {
+      Add(entity_name.bind_index());
       // Don't include the name. While it is part of the canonical identity of a
       // compile-time binding, renaming it (and its uses) is a compatible change
       // that we would like to not affect the fingerprint.
+      //
+      // Also don't include the `is_template` flag. Changing that flag should
+      // also be a compatible change from the perspective of users of a generic.
     } else {
       Add(entity_name.name_id);
     }