diff --git a/toolchain/check/deduce.cpp b/toolchain/check/deduce.cpp index 703f6bb9c95a..9bc1677e19c4 100644 --- a/toolchain/check/deduce.cpp +++ b/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; diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 79fddb53b0e3..124b07e4b97b 100644 --- a/toolchain/check/eval.cpp +++ b/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 diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index 153e0fe87f96..7c798d75f06c 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -115,7 +115,8 @@ class RebuildGenericConstantInEvalBlockCallbacks final context_.insts().TryGetAs(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; } diff --git a/toolchain/check/handle_alias.cpp b/toolchain/check/handle_alias.cpp index ad09d747b619..a611234157c2 100644 --- a/toolchain/check/handle_alias.cpp +++ b/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; diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index 2d9bd52d1b0e..6e21770d0f26 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -28,23 +28,17 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, SemIR::ExprRegionId type_expr_region_id = EndSubpatternAsExpr(context, cast_type_inst_id); + // The name in a template binding may be wrapped in `template`. + bool is_generic = node_kind == Parse::NodeKind::CompileTimeBindingPattern; + auto is_template = + context.node_stack() + .PopAndDiscardSoloNodeIdIf(); + // A non-generic template binding is diagnosed by the parser. + is_template &= is_generic; + // 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; - 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(inst_id); - } - - bool needs_compile_time_binding = is_generic && !is_associated_constant; - 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(); - 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(node_id); + context.parse_tree().As(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(); 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 diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index 413b98bf9131..941f26c6ed53 100644 --- a/toolchain/check/handle_interface.cpp +++ b/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( {.type_id = self_type_id, diff --git a/toolchain/check/handle_where.cpp b/toolchain/check/handle_where.cpp index b8035f49ab51..5280b4e0857a 100644 --- a/toolchain/check/handle_where.cpp +++ b/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( {.type_id = self_type_id, diff --git a/toolchain/check/import.cpp b/toolchain/check/import.cpp index bd865d0d09d1..ace91932a99f 100644 --- a/toolchain/check/import.cpp +++ b/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( diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 0b7ade6c757a..d5b3130417a8 100644 --- a/toolchain/check/import_ref.cpp +++ b/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( 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( 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( resolver, {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id), diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 6e8655fe254d..3b326dcab422 100644 --- a/toolchain/check/node_stack.h +++ b/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: diff --git a/toolchain/check/subst.cpp b/toolchain/check/subst.cpp index 0de31039548f..00fe65a3ac12 100644 --- a/toolchain/check/subst.cpp +++ b/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); diff --git a/toolchain/check/testdata/array/init_dependent_bound.carbon b/toolchain/check/testdata/array/init_dependent_bound.carbon index 969c6d052687..4b93085a1192 100644 --- a/toolchain/check/testdata/array/init_dependent_bound.carbon +++ b/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: = 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 %N.51e, %Convert.960 [symbolic] +// CHECK:STDOUT: %Convert.specific_fn.18b: = 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: = 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: = 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 %int_3.1ba, %Convert.956 [concrete] +// CHECK:STDOUT: %Convert.specific_fn.b42: = 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 %G, @G(%int_3.822) [concrete] +// CHECK:STDOUT: %Convert.bound.2d6: = bound_method %int_3.822, %Convert.960 [concrete] +// CHECK:STDOUT: %Convert.specific_fn.377: = 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: = complete_type_witness %array_type.3fb [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = 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 [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 [symbolic = %N.patt.loc5_15.2 (constants.%N.patt.8e2)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %N.param: %i32 = value_param runtime_param +// 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 %N.loc5_15.2, constants.%Convert.960 [symbolic = %Convert.bound (constants.%Convert.bound.588)] +// CHECK:STDOUT: %Convert.specific_fn: = 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: = 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, +// 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 %N.ref, %impl.elem0 [symbolic = %Convert.bound (constants.%Convert.bound.588)] +// CHECK:STDOUT: %specific_fn: = 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: 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 %int_3, %impl.elem0 [concrete = constants.%Convert.bound.b30] +// CHECK:STDOUT: %specific_fn: = 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 %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: diff --git a/toolchain/check/testdata/class/no_prelude/comp_time_field.carbon b/toolchain/check/testdata/class/no_prelude/comp_time_field.carbon new file mode 100644 index 000000000000..c48f44e2a2e2 --- /dev/null +++ b/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: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = 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: = 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: diff --git a/toolchain/check/testdata/function/generic/no_prelude/template_param.carbon b/toolchain/check/testdata/function/generic/no_prelude/template_param.carbon new file mode 100644 index 000000000000..28a75f44e33a --- /dev/null +++ b/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 %F, @F(%empty_struct_type) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = 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 [symbolic = %T.patt.loc4_15.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.param: type = value_param runtime_param +// 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 %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: diff --git a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_template.carbon b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_template.carbon index 989825fbfab0..1596e1643362 100644 --- a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_template.carbon +++ b/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 [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 = .inst15 -// CHECK:STDOUT: witness = invalid +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .T = @T.%assoc0 +// CHECK:STDOUT: witness = (%T) // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: assoc_const @T T:! type; +// CHECK:STDOUT: generic assoc_const @T(@I.%Self: %I.type) { +// CHECK:STDOUT: assoc_const T:! type; +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T(constants.%Self) {} // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/no_prelude/fail_incomplete_type.carbon b/toolchain/check/testdata/interface/no_prelude/fail_incomplete_type.carbon index aaccb8b1d275..8553dfc79e7b 100644 --- a/toolchain/check/testdata/interface/no_prelude/fail_incomplete_type.carbon +++ b/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] diff --git a/toolchain/check/testdata/return/fail_let_in_type.carbon b/toolchain/check/testdata/return/fail_let_in_type.carbon index 91639b353462..ad6d0409766a 100644 --- a/toolchain/check/testdata/return/fail_let_in_type.carbon +++ b/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 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 [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: = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.ref: type = name_ref x, file.%x +// CHECK:STDOUT: %return.param: ref = out_param runtime_param0 +// CHECK:STDOUT: %return: ref = 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: = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %y.ref: type = name_ref y, file.%y +// CHECK:STDOUT: %return.param: ref = out_param runtime_param0 +// CHECK:STDOUT: %return: ref = 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: = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %z.ref: type = name_ref z, file.%z +// CHECK:STDOUT: %return.param: ref = out_param runtime_param0 +// CHECK:STDOUT: %return: ref = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Six() -> { // CHECK:STDOUT: !entry: @@ -59,3 +122,20 @@ fn FirstPerfectNumber() -> z { return 6; } // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @FirstPerfectNumber() -> { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %int_6: Core.IntLiteral = int_value 6 [concrete = constants.%int_6] +// CHECK:STDOUT: return +// 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: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 24a229f67585..375e0581043c 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/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) diff --git a/toolchain/driver/testdata/compile/multifile_raw_and_textual_ir.carbon b/toolchain/driver/testdata/compile/multifile_raw_and_textual_ir.carbon index 701a0a7ce6e3..06a1e5aa5718 100644 --- a/toolchain/driver/testdata/compile/multifile_raw_and_textual_ir.carbon +++ b/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, 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} +// 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} diff --git a/toolchain/driver/testdata/compile/multifile_raw_ir.carbon b/toolchain/driver/testdata/compile/multifile_raw_ir.carbon index bfd5a0d5e02f..97a9ade3cdcd 100644 --- a/toolchain/driver/testdata/compile/multifile_raw_ir.carbon +++ b/toolchain/driver/testdata/compile/multifile_raw_ir.carbon @@ -90,7 +90,7 @@ fn B() { // CHECK:STDOUT: name_scope0: {inst: inst12, parent_scope: name_scope, 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} +// 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} diff --git a/toolchain/driver/testdata/compile/raw_and_textual_ir.carbon b/toolchain/driver/testdata/compile/raw_and_textual_ir.carbon index b8d7c92941ca..03174560eecf 100644 --- a/toolchain/driver/testdata/compile/raw_and_textual_ir.carbon +++ b/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, has_error: false, extended_scopes: [], names: {name0: inst32}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope, index: comp_time_bind} +// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope, 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: {} diff --git a/toolchain/driver/testdata/compile/raw_ir.carbon b/toolchain/driver/testdata/compile/raw_ir.carbon index d8a2b22aa605..741727344f91 100644 --- a/toolchain/driver/testdata/compile/raw_ir.carbon +++ b/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, has_error: false, extended_scopes: [], names: {name0: inst36}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope, index: comp_time_bind0} -// CHECK:STDOUT: entity_name1: {name: name2, parent_scope: name_scope, index: comp_time_bind} +// CHECK:STDOUT: entity_name0: {name: name1, parent_scope: name_scope, index: 0, is_template: 0} +// CHECK:STDOUT: entity_name1: {name: name2, parent_scope: name_scope, 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: {} diff --git a/toolchain/parse/handle_binding_pattern.cpp b/toolchain/parse/handle_binding_pattern.cpp index eff0d255875c..f70d18d8cf4e 100644 --- a/toolchain/parse/handle_binding_pattern.cpp +++ b/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 diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 31690f2a30f6..d5d63499473e 100644 --- a/toolchain/parse/node_kind.def +++ b/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) diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 47788e8de598..a05c9906c8f6 100644 --- a/toolchain/parse/state.def +++ b/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 diff --git a/toolchain/parse/testdata/generics/generic_params/fail_template_no_param.carbon b/toolchain/parse/testdata/generics/generic_params/fail_template_no_param.carbon new file mode 100644 index 000000000000..98d666d432e8 --- /dev/null +++ b/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: ] diff --git a/toolchain/parse/testdata/generics/generic_params/template.carbon b/toolchain/parse/testdata/generics/generic_params/template.carbon index 080f78a4aa3d..ac4432908d29 100644 --- a/toolchain/parse/testdata/generics/generic_params/template.carbon +++ b/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: ''}, diff --git a/toolchain/parse/testdata/generics/generic_params/template_addr.carbon b/toolchain/parse/testdata/generics/generic_params/template_addr.carbon index 9407d769f935..4784cc13c4b6 100644 --- a/toolchain/parse/testdata/generics/generic_params/template_addr.carbon +++ b/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: ''}, diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 89e03139c504..cf574b26193d 100644 --- a/toolchain/parse/typed_nodes.h +++ b/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 name; +}; + // `name:! Type` struct CompileTimeBindingPattern { static constexpr auto Kind = NodeKind::CompileTimeBindingPattern.Define( {.category = NodeCategory::Pattern, .child_count = 2}); - NodeIdOneOf name; + NodeIdOneOf + 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; using PatternListComma = diff --git a/toolchain/sem_ir/entity_name.h b/toolchain/sem_ir/entity_name.h index 1955ca282413..933f9a128673 100644 --- a/toolchain/sem_ir/entity_name.h +++ b/toolchain/sem_ir/entity_name.h @@ -15,7 +15,8 @@ namespace Carbon::SemIR { struct EntityName : public Printable { 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 { 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 { 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; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 006cb5ce78e1..c22d7b422c78 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/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"; } } diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index 1508afc4e7e8..eee6729eb542 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/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); }