From 7848bbc62e245fe295026302cd975451d78a80aa Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 24 May 2024 19:34:15 +0000 Subject: [PATCH 1/7] Parse parameters in name qualifiers. Parse the name of a declaration as a sequence of name qualifiers -- which have a name, possibly parameters, and a trailing period -- followed by a name and possibly parameters. Cases like namespaces that can't actually have parameters are now diagnosed in check instead of in parse. --- toolchain/check/decl_name_stack.cpp | 23 ++- toolchain/check/handle_function.cpp | 22 ++- toolchain/check/handle_name.cpp | 36 ++-- toolchain/check/node_stack.h | 1 - .../alias/no_prelude/fail_params.carbon | 33 ++++ .../class/fail_base_as_declared_name.carbon | 4 +- .../class/fail_todo_generic_method.carbon | 35 +--- .../fail_todo_member_out_of_line.carbon | 35 +--- .../no_prelude/fail_todo_no_params.carbon | 92 ++++++++++ .../testdata/namespace/fail_params.carbon | 71 ++++++++ .../no_prelude/fail_export_name_member.carbon | 80 +++++++++ .../no_prelude/fail_export_name_params.carbon | 86 +++++++++ toolchain/diagnostics/diagnostic_kind.def | 2 + toolchain/parse/handle_alias.cpp | 2 +- toolchain/parse/handle_choice.cpp | 2 +- .../parse/handle_decl_name_and_params.cpp | 142 ++++++--------- toolchain/parse/handle_function.cpp | 2 +- toolchain/parse/handle_import_and_package.cpp | 2 +- toolchain/parse/handle_namespace.cpp | 2 +- toolchain/parse/handle_period.cpp | 11 +- toolchain/parse/handle_type.cpp | 2 +- toolchain/parse/node_ids.h | 1 - toolchain/parse/node_kind.cpp | 3 +- toolchain/parse/node_kind.def | 43 ++--- toolchain/parse/node_kind.h | 5 +- toolchain/parse/state.def | 76 ++++---- toolchain/parse/testdata/alias/basic.carbon | 4 +- .../fail_identifier_instead_of_sig.carbon | 2 +- .../fail_missing_implicit_close.carbon | 2 +- .../declaration/fail_no_sig_or_semi.carbon | 2 +- .../function/declaration/no_params.carbon | 17 ++ .../impl/fail_out_of_line_member.carbon | 23 +-- .../generics/params/name_qualifier.carbon | 169 ++++++++++++++++++ .../{fail_args.carbon => args.carbon} | 10 +- .../testdata/namespace/fail_arrow.carbon | 19 +- .../namespace/fail_incomplete_name.carbon | 8 +- .../parse/testdata/namespace/nested.carbon | 14 +- .../parse/testdata/packages/export.carbon | 12 +- toolchain/parse/typed_nodes.h | 58 +++--- toolchain/sem_ir/bind_name.h | 2 +- 40 files changed, 793 insertions(+), 362 deletions(-) create mode 100644 toolchain/check/testdata/alias/no_prelude/fail_params.carbon create mode 100644 toolchain/check/testdata/function/declaration/no_prelude/fail_todo_no_params.carbon create mode 100644 toolchain/check/testdata/namespace/fail_params.carbon create mode 100644 toolchain/check/testdata/packages/no_prelude/fail_export_name_member.carbon create mode 100644 toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon create mode 100644 toolchain/parse/testdata/function/declaration/no_params.carbon create mode 100644 toolchain/parse/testdata/generics/params/name_qualifier.carbon rename toolchain/parse/testdata/namespace/{fail_args.carbon => args.carbon} (63%) diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index 7cb85b31c1a9..7412eb818e19 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -6,6 +6,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" +#include "toolchain/diagnostics/diagnostic.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { @@ -56,14 +57,20 @@ auto DeclNameStack::PushScopeAndStartName() -> void { auto DeclNameStack::FinishName() -> NameContext { CARBON_CHECK(decl_name_stack_.back().state != NameContext::State::Finished) << "Finished name twice"; - if (context_->node_stack() - .PopAndDiscardSoloNodeIdIf()) { - // Any parts from a QualifiedName will already have been processed - // into the name. - } else { - // The name had no qualifiers, so we need to process the node now. - auto [loc_id, name_id] = context_->node_stack().PopNameWithNodeId(); - ApplyNameQualifier(loc_id, name_id); + auto params_id = + context_->node_stack().PopIf(); + auto implicit_params_id = + context_->node_stack().PopIf(); + auto [loc_id, name_id] = context_->node_stack().PopNameWithNodeId(); + + ApplyNameQualifier(loc_id, name_id); + + if (params_id || implicit_params_id) { + // TODO: Say which kind of declaraation we're parsing. + CARBON_DIAGNOSTIC(UnexpectedDeclNameParams, Error, + "Declaration cannot have parameters."); + context_->emitter().Emit(decl_name_stack_.back().loc_id, + UnexpectedDeclNameParams); } NameContext result = decl_name_stack_.back(); diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 9b1bdca47486..60bfaa370269 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -211,8 +211,12 @@ static auto BuildFunctionDecl(Context& context, return_slot = SemIR::Function::ReturnSlot::Absent; } - SemIR::InstBlockId param_refs_id = - context.node_stack().Pop(); + auto param_refs_id = + context.node_stack().PopIf(); + if (!param_refs_id) { + context.TODO(node_id, "function with positional parameters"); + param_refs_id = SemIR::InstBlockId::Empty; + } // TODO: Use Invalid rather than Empty if there was no implicit parameter // list. SemIR::InstBlockId implicit_param_refs_id = @@ -253,7 +257,7 @@ static auto BuildFunctionDecl(Context& context, .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(), .decl_id = context.AddPlaceholderInst({node_id, function_decl}), .implicit_param_refs_id = implicit_param_refs_id, - .param_refs_id = param_refs_id, + .param_refs_id = *param_refs_id, .return_type_id = return_type_id, .return_storage_id = return_storage_id, .is_extern = is_extern, @@ -297,12 +301,14 @@ static auto BuildFunctionDecl(Context& context, if (SemIR::IsEntryPoint(context.sem_ir(), function_decl.function_id)) { // TODO: Update this once valid signatures for the entry point are decided. - if (!context.inst_blocks().Get(implicit_param_refs_id).empty() || - !context.inst_blocks().Get(param_refs_id).empty() || - (return_type_id.is_valid() && - return_type_id != + if (!context.inst_blocks() + .Get(function_info.implicit_param_refs_id) + .empty() || + !context.inst_blocks().Get(function_info.param_refs_id).empty() || + (function_info.return_type_id.is_valid() && + function_info.return_type_id != context.GetBuiltinType(SemIR::BuiltinKind::IntType) && - return_type_id != context.GetTupleType({}))) { + function_info.return_type_id != context.GetTupleType({}))) { CARBON_DIAGNOSTIC(InvalidMainRunSignature, Error, "Invalid signature for `Main.Run` function. Expected " "`fn ()` or `fn () -> i32`."); diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index f9668272d187..c092506c6611 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -125,35 +125,19 @@ auto HandleSelfValueNameExpr(Context& context, return HandleNameAsExpr(context, node_id, SemIR::NameId::SelfValue); } -auto HandleQualifiedName(Context& context, Parse::QualifiedNameId node_id) +auto HandleNameQualifier(Context& context, Parse::NameQualifierId node_id) -> bool { - auto [node_id2, name_id2] = context.node_stack().PopNameWithNodeId(); - - Parse::NodeId node_id1 = context.node_stack().PeekNodeId(); - switch (context.parse_tree().node_kind(node_id1)) { - case Parse::NodeKind::QualifiedName: - // This is the second or subsequent QualifiedName in a chain. - // Nothing to do: the first QualifiedName remains as a - // bracketing node for later QualifiedNames. - break; - - case Parse::NodeKind::IdentifierName: { - // This is the first QualifiedName in a chain, and starts with an - // identifier name. - auto name_id = - context.node_stack().Pop(); - context.decl_name_stack().ApplyNameQualifier(node_id1, name_id); - // Add the QualifiedName so that it can be used for bracketing. - context.node_stack().Push(node_id); - break; - } - - default: - CARBON_FATAL() << "Unexpected node kind on left side of qualified " - "declaration name"; + auto params_id = context.node_stack().PopIf(); + auto implicit_params_id = + context.node_stack().PopIf(); + if (params_id || implicit_params_id) { + // TODO: Pass the parameters into decl_name_stack. + context.TODO(node_id, "name qualifier with parameters"); + return false; } - context.decl_name_stack().ApplyNameQualifier(node_id2, name_id2); + auto [name_node_id, name_id] = context.node_stack().PopNameWithNodeId(); + context.decl_name_stack().ApplyNameQualifier(name_node_id, name_id); return true; } diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 02fa21b82020..bfc0082a5549 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -445,7 +445,6 @@ class NodeStack { case Parse::NodeKind::InterfaceIntroducer: case Parse::NodeKind::LetInitializer: case Parse::NodeKind::LetIntroducer: - case Parse::NodeKind::QualifiedName: case Parse::NodeKind::ReturnedModifier: case Parse::NodeKind::ReturnStatementStart: case Parse::NodeKind::ReturnVarModifier: diff --git a/toolchain/check/testdata/alias/no_prelude/fail_params.carbon b/toolchain/check/testdata/alias/no_prelude/fail_params.carbon new file mode 100644 index 000000000000..56105e094ae9 --- /dev/null +++ b/toolchain/check/testdata/alias/no_prelude/fail_params.carbon @@ -0,0 +1,33 @@ +// 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 + +// CHECK:STDERR: fail_params.carbon:[[@LINE+7]]:7: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: alias A(T:! type) = T*; +// CHECK:STDERR: ^ +// CHECK:STDERR: +// CHECK:STDERR: fail_params.carbon:[[@LINE+3]]:21: ERROR: Alias initializer must be a name reference. +// CHECK:STDERR: alias A(T:! type) = T*; +// CHECK:STDERR: ^~ +alias A(T:! type) = T*; + +// CHECK:STDOUT: --- fail_params.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic] +// CHECK:STDOUT: %.1: type = ptr_type T [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A +// CHECK:STDOUT: } +// CHECK:STDOUT: %T.loc14_9.1: type = param T +// CHECK:STDOUT: %T.loc14_9.2: type = bind_symbolic_name T 0, %T.loc14_9.1 [symbolic = constants.%T] +// CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc14_9.2 [symbolic = constants.%T] +// CHECK:STDOUT: %.loc14: type = ptr_type T [symbolic = constants.%.1] +// CHECK:STDOUT: %A: = bind_alias A, [template = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_base_as_declared_name.carbon b/toolchain/check/testdata/class/fail_base_as_declared_name.carbon index 0164ff99359e..4cfa65f8b7e1 100644 --- a/toolchain/check/testdata/class/fail_base_as_declared_name.carbon +++ b/toolchain/check/testdata/class/fail_base_as_declared_name.carbon @@ -6,11 +6,11 @@ namespace N; -// CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+7]]:6: ERROR: Expected identifier after `.`. +// CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+7]]:6: ERROR: `.` should be followed by a name. // CHECK:STDERR: fn N.base() {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+3]]:6: ERROR: Semantics TODO: `Error recovery from keyword name.`. +// CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+3]]:6: ERROR: Semantics TODO: `HandleInvalidParse`. // CHECK:STDERR: fn N.base() {} // CHECK:STDERR: ^~~~ fn N.base() {} diff --git a/toolchain/check/testdata/class/fail_todo_generic_method.carbon b/toolchain/check/testdata/class/fail_todo_generic_method.carbon index f715469947b2..0a4f9e628f71 100644 --- a/toolchain/check/testdata/class/fail_todo_generic_method.carbon +++ b/toolchain/check/testdata/class/fail_todo_generic_method.carbon @@ -9,16 +9,9 @@ class Class(T:! type) { fn F[self: Self](n: T); } -// CHECK:STDERR: fail_todo_generic_method.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: fail_todo_generic_method.carbon:[[@LINE+3]]:4: ERROR: Semantics TODO: `name qualifier with parameters`. // CHECK:STDERR: fn Class(T:! type).F[self: Self](n: T) {} -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_generic_method.carbon:[[@LINE-8]]:1: Name is previously declared here. -// CHECK:STDERR: class Class(T:! type) { -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_generic_method.carbon:[[@LINE+3]]:19: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition. -// CHECK:STDERR: fn Class(T:! type).F[self: Self](n: T) {} -// CHECK:STDERR: ^ +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ fn Class(T:! type).F[self: Self](n: T) {} // CHECK:STDOUT: --- fail_todo_generic_method.carbon @@ -33,34 +26,18 @@ fn Class(T:! type).F[self: Self](n: T) {} // CHECK:STDOUT: %F: type = fn_type @F [template] // CHECK:STDOUT: %struct.2: F = struct_value () [template] // CHECK:STDOUT: %.3: type = struct_type {.a: T} [symbolic] -// CHECK:STDOUT: %.4: type = fn_type @.1 [template] -// CHECK:STDOUT: %struct.3: = struct_value () [template] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .Class = %Class.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core: = namespace [template] {} -// CHECK:STDOUT: %Class.decl: Class = class_decl @Class [template = constants.%struct.1] { -// CHECK:STDOUT: %T.loc7_13.1: type = param T -// CHECK:STDOUT: %T.loc7_13.2: type = bind_symbolic_name T 0, %T.loc7_13.1 [symbolic = constants.%T] -// CHECK:STDOUT: } -// CHECK:STDOUT: %.decl: = fn_decl @.1 [template = constants.%struct.3] { -// CHECK:STDOUT: %T.loc22_10.1: type = param T -// CHECK:STDOUT: @.1.%T: type = bind_symbolic_name T 0, %T.loc22_10.1 [symbolic = constants.%T] -// CHECK:STDOUT: } -// CHECK:STDOUT: } +// CHECK:STDOUT: file {} // CHECK:STDOUT: // CHECK:STDOUT: class @Class { -// CHECK:STDOUT: %T.ref.loc8: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T] +// CHECK:STDOUT: %T.ref.loc8: type = name_ref T, [symbolic = constants.%T] // CHECK:STDOUT: %.loc8: = field_decl a, element0 [template] // CHECK:STDOUT: %F.decl: F = fn_decl @F [template = constants.%struct.2] { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [template = constants.%Class.2] // CHECK:STDOUT: %self.loc9_8.1: Class = param self // CHECK:STDOUT: %self.loc9_8.2: Class = bind_name self, %self.loc9_8.1 -// CHECK:STDOUT: %T.ref.loc9: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T] +// CHECK:STDOUT: %T.ref.loc9: type = name_ref T, [symbolic = constants.%T] // CHECK:STDOUT: %n.loc9_20.1: T = param n // CHECK:STDOUT: %n.loc9_20.2: T = bind_name n, %n.loc9_20.1 // CHECK:STDOUT: } @@ -73,5 +50,3 @@ fn Class(T:! type).F[self: Self](n: T) {} // CHECK:STDOUT: // CHECK:STDOUT: fn @F[@Class.%self.loc9_8.2: Class](@Class.%n.loc9_20.2: T); // CHECK:STDOUT: -// CHECK:STDOUT: fn @.1(%T: type); -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/generic/fail_todo_member_out_of_line.carbon b/toolchain/check/testdata/class/generic/fail_todo_member_out_of_line.carbon index 82a2726223be..5bbd9cc86b8f 100644 --- a/toolchain/check/testdata/class/generic/fail_todo_member_out_of_line.carbon +++ b/toolchain/check/testdata/class/generic/fail_todo_member_out_of_line.carbon @@ -8,16 +8,9 @@ class Class(T:! type) { fn F(n: T) -> T; } -// CHECK:STDERR: fail_todo_member_out_of_line.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: fail_todo_member_out_of_line.carbon:[[@LINE+3]]:4: ERROR: Semantics TODO: `name qualifier with parameters`. // CHECK:STDERR: fn Class(T:! type).F(n: T) -> T { -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_member_out_of_line.carbon:[[@LINE-7]]:1: Name is previously declared here. -// CHECK:STDERR: class Class(T:! type) { -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_member_out_of_line.carbon:[[@LINE+3]]:19: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition. -// CHECK:STDERR: fn Class(T:! type).F(n: T) -> T { -// CHECK:STDERR: ^ +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ fn Class(T:! type).F(n: T) -> T { return n; } @@ -33,32 +26,16 @@ fn Class(T:! type).F(n: T) -> T { // CHECK:STDOUT: %F: type = fn_type @F [template] // CHECK:STDOUT: %struct.2: F = struct_value () [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] -// CHECK:STDOUT: %.3: type = fn_type @.1 [template] -// CHECK:STDOUT: %struct.3: = struct_value () [template] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .Core = %Core -// CHECK:STDOUT: .Class = %Class.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core: = namespace [template] {} -// CHECK:STDOUT: %Class.decl: Class = class_decl @Class [template = constants.%struct.1] { -// CHECK:STDOUT: %T.loc7_13.1: type = param T -// CHECK:STDOUT: %T.loc7_13.2: type = bind_symbolic_name T 0, %T.loc7_13.1 [symbolic = constants.%T] -// CHECK:STDOUT: } -// CHECK:STDOUT: %.decl: = fn_decl @.1 [template = constants.%struct.3] { -// CHECK:STDOUT: %T.loc21_10.1: type = param T -// CHECK:STDOUT: @.1.%T: type = bind_symbolic_name T 0, %T.loc21_10.1 [symbolic = constants.%T] -// CHECK:STDOUT: } -// CHECK:STDOUT: } +// CHECK:STDOUT: file {} // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %F.decl: F = fn_decl @F [template = constants.%struct.2] { -// CHECK:STDOUT: %T.ref.loc8_11: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T] +// CHECK:STDOUT: %T.ref.loc8_11: type = name_ref T, [symbolic = constants.%T] // CHECK:STDOUT: %n.loc8_8.1: T = param n // CHECK:STDOUT: %n.loc8_8.2: T = bind_name n, %n.loc8_8.1 -// CHECK:STDOUT: %T.ref.loc8_17: type = name_ref T, file.%T.loc7_13.2 [symbolic = constants.%T] +// CHECK:STDOUT: %T.ref.loc8_17: type = name_ref T, [symbolic = constants.%T] // CHECK:STDOUT: %return.var: ref T = var // CHECK:STDOUT: } // CHECK:STDOUT: @@ -69,5 +46,3 @@ fn Class(T:! type).F(n: T) -> T { // CHECK:STDOUT: // CHECK:STDOUT: fn @F(@Class.%n.loc8_8.2: T) -> T; // CHECK:STDOUT: -// CHECK:STDOUT: fn @.1(%T: type); -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/declaration/no_prelude/fail_todo_no_params.carbon b/toolchain/check/testdata/function/declaration/no_prelude/fail_todo_no_params.carbon new file mode 100644 index 000000000000..a6d563935831 --- /dev/null +++ b/toolchain/check/testdata/function/declaration/no_prelude/fail_todo_no_params.carbon @@ -0,0 +1,92 @@ +// 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 + +// --- fail_no_body.carbon + +library "no_body"; +// CHECK:STDERR: fail_no_body.carbon:[[@LINE+4]]:1: ERROR: Semantics TODO: `function with positional parameters`. +// CHECK:STDERR: fn A; +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +fn A; + +// --- fail_todo_brace_body.carbon + +library "brace_body"; +// CHECK:STDERR: fail_todo_brace_body.carbon:[[@LINE+4]]:1: ERROR: Semantics TODO: `function with positional parameters`. +// CHECK:STDERR: fn A { +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +fn A { +} + +// --- fail_todo_arrow_body.carbon + +// TODO: We don't have parsing support for this yet. +library "arrow_body"; +// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+7]]:1: ERROR: Semantics TODO: `function with positional parameters`. +// CHECK:STDERR: fn A => 0; +// CHECK:STDERR: ^~~~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+3]]:6: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition. +// CHECK:STDERR: fn A => 0; +// CHECK:STDERR: ^~ +fn A => 0; + +// CHECK:STDOUT: --- fail_no_body.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A: type = fn_type @A [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %struct: A = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: A = fn_decl @A [template = constants.%struct] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_brace_body.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A: type = fn_type @A [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %struct: A = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: A = fn_decl @A [template = constants.%struct] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_arrow_body.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A: type = fn_type @A [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %struct: A = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: A = fn_decl @A [template = constants.%struct] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/namespace/fail_params.carbon b/toolchain/check/testdata/namespace/fail_params.carbon new file mode 100644 index 000000000000..deac129910d5 --- /dev/null +++ b/toolchain/check/testdata/namespace/fail_params.carbon @@ -0,0 +1,71 @@ +// 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 + +// CHECK:STDERR: fail_params.carbon:[[@LINE+4]]:11: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: namespace A(); +// CHECK:STDERR: ^ +// CHECK:STDERR: +namespace A(); + +// Parameters are ignored for error recovery. +fn A.F() {} + +// CHECK:STDERR: fail_params.carbon:[[@LINE+4]]:11: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: namespace B(n: i32); +// CHECK:STDERR: ^ +// CHECK:STDERR: +namespace B(n: i32); + +// CHECK:STDERR: fail_params.carbon:[[@LINE+3]]:11: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: namespace C[T:! type](x: T); +// CHECK:STDERR: ^ +namespace C[T:! type](x: T); + +// CHECK:STDOUT: --- fail_params.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F: type = fn_type @F [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %struct.1: F = struct_value () [template] +// CHECK:STDOUT: %Int32: type = fn_type @Int32 [template] +// CHECK:STDOUT: %struct.2: Int32 = struct_value () [template] +// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .A = %A +// CHECK:STDOUT: .B = %B +// CHECK:STDOUT: .C = %C +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %A: = namespace [template] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: F = fn_decl @F [template = constants.%struct.1] {} +// CHECK:STDOUT: %import_ref: Int32 = import_ref ir3, inst+3, loaded [template = constants.%struct.2] +// CHECK:STDOUT: %int.make_type_32: init type = call constants.%struct.2() [template = i32] +// CHECK:STDOUT: %.loc20_16.1: type = value_of_initializer %int.make_type_32 [template = i32] +// CHECK:STDOUT: %.loc20_16.2: type = converted %int.make_type_32, %.loc20_16.1 [template = i32] +// CHECK:STDOUT: %n.loc20_13.1: i32 = param n +// CHECK:STDOUT: %n.loc20_13.2: i32 = bind_name n, %n.loc20_13.1 +// CHECK:STDOUT: %B: = namespace [template] {} +// CHECK:STDOUT: %T.loc25_13.1: type = param T +// CHECK:STDOUT: %T.loc25_13.2: type = bind_symbolic_name T 0, %T.loc25_13.1 [symbolic = constants.%T] +// CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc25_13.2 [symbolic = constants.%T] +// CHECK:STDOUT: %x.loc25_23.1: T = param x +// CHECK:STDOUT: %x.loc25_23.2: T = bind_name x, %x.loc25_23.1 +// CHECK:STDOUT: %C: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32"; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/packages/no_prelude/fail_export_name_member.carbon b/toolchain/check/testdata/packages/no_prelude/fail_export_name_member.carbon new file mode 100644 index 000000000000..f146da097712 --- /dev/null +++ b/toolchain/check/testdata/packages/no_prelude/fail_export_name_member.carbon @@ -0,0 +1,80 @@ +// 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 + +// --- a.carbon + +package Foo library "a"; + +class C { + var n: {}; +} + +// --- fail_b.carbon + +package Foo library "b"; + +import library "a"; + +// TODO: This diagnostic doesn't clearly explain the problem. We should instead +// say something like: Only namespace-scope names can be exported. +// CHECK:STDERR: fail_b.carbon:[[@LINE+6]]:10: ERROR: Name qualifiers are only allowed for entities that provide a scope. +// CHECK:STDERR: export C.n; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_b.carbon:[[@LINE+3]]:8: Non-scope entity referenced here. +// CHECK:STDERR: export C.n; +// CHECK:STDERR: ^ +export C.n; + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.3: type = unbound_element_type C, {} [template] +// CHECK:STDOUT: %.4: type = struct_type {.n: {}} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc5_11.1: {} = struct_literal () +// CHECK:STDOUT: %.loc5_11.2: type = converted %.loc5_11.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %.loc5_8: = field_decl n, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .n = %.loc5_8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: type = struct_type {.n: {}} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %import_ref.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: type = import_ref ir1, inst+1, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.2 = import_ref ir1, inst+2, unloaded +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+8, unloaded +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = file.%import_ref.2 +// CHECK:STDOUT: .n = file.%import_ref.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon b/toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon new file mode 100644 index 000000000000..6abf345f5b09 --- /dev/null +++ b/toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon @@ -0,0 +1,86 @@ +// 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 + +// --- a.carbon + +package Foo library "a"; + +class C1(T:! type); +class C2(T:! type); + +// --- fail_b.carbon + +package Foo library "b"; + +import library "a"; + +export C1; + +// CHECK:STDERR: fail_b.carbon:[[@LINE+3]]:8: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: export C2(T:! type); +// CHECK:STDERR: ^~ +export C2(T:! type); + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic] +// CHECK:STDOUT: %C1.1: type = generic_class_type @C1 [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %struct.1: C1 = struct_value () [template] +// CHECK:STDOUT: %C1.2: type = class_type @C1 [template] +// CHECK:STDOUT: %C2.1: type = generic_class_type @C2 [template] +// CHECK:STDOUT: %struct.2: C2 = struct_value () [template] +// CHECK:STDOUT: %C2.2: type = class_type @C2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C1 = %C1.decl +// CHECK:STDOUT: .C2 = %C2.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C1.decl: C1 = class_decl @C1 [template = constants.%struct.1] { +// CHECK:STDOUT: %T.loc4_10.1: type = param T +// CHECK:STDOUT: %T.loc4_10.2: type = bind_symbolic_name T 0, %T.loc4_10.1 [symbolic = constants.%T] +// CHECK:STDOUT: } +// CHECK:STDOUT: %C2.decl: C2 = class_decl @C2 [template = constants.%struct.2] { +// CHECK:STDOUT: %T.loc5_10.1: type = param T +// CHECK:STDOUT: %T.loc5_10.2: type = bind_symbolic_name T 0, %T.loc5_10.1 [symbolic = constants.%T] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C1; +// CHECK:STDOUT: +// CHECK:STDOUT: class @C2; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C1: type = generic_class_type @C1 [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %struct.1: C1 = struct_value () [template] +// CHECK:STDOUT: %T: type = bind_symbolic_name T 0, [symbolic] +// CHECK:STDOUT: %C2: type = generic_class_type @C2 [template] +// CHECK:STDOUT: %struct.2: C2 = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C1 = %C1 +// CHECK:STDOUT: .C2 = %C2 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1: C1 = import_ref ir1, inst+4, loaded [template = constants.%struct.1] +// CHECK:STDOUT: %import_ref.2: C2 = import_ref ir1, inst+11, loaded [template = constants.%struct.2] +// CHECK:STDOUT: %C1: C1 = export C1, %import_ref.1 [template = constants.%struct.1] +// CHECK:STDOUT: %T.loc11_11.1: type = param T +// CHECK:STDOUT: %T.loc11_11.2: type = bind_symbolic_name T 0, %T.loc11_11.1 [symbolic = constants.%T] +// CHECK:STDOUT: %C2: C2 = export C2, %import_ref.2 [template = constants.%struct.2] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C1; +// CHECK:STDOUT: +// CHECK:STDOUT: class @C2; +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index fc923fbabd52..55ad4ae5226f 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -110,6 +110,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedElseAfterIf) // Declaration diagnostics. CARBON_DIAGNOSTIC_KIND(ExpectedDeclName) +CARBON_DIAGNOSTIC_KIND(ExpectedDeclNameAfterPeriod) CARBON_DIAGNOSTIC_KIND(ExpectedDeclSemi) CARBON_DIAGNOSTIC_KIND(ExpectedDeclSemiOrDefinition) CARBON_DIAGNOSTIC_KIND(ParamsRequiredAfterImplicit) @@ -310,6 +311,7 @@ CARBON_DIAGNOSTIC_KIND(ReturnStatementMissingExpr) CARBON_DIAGNOSTIC_KIND(ImplicitAsConversionFailure) CARBON_DIAGNOSTIC_KIND(ExplicitAsConversionFailure) CARBON_DIAGNOSTIC_KIND(TypeExprEvaluationFailure) +CARBON_DIAGNOSTIC_KIND(UnexpectedDeclNameParams) CARBON_DIAGNOSTIC_KIND(QualifiedNameInNonScope) CARBON_DIAGNOSTIC_KIND(QualifiedNameNonScopeEntity) CARBON_DIAGNOSTIC_KIND(QualifiedExprInIncompleteClassScope) diff --git a/toolchain/parse/handle_alias.cpp b/toolchain/parse/handle_alias.cpp index 7810dc11fcc8..8d0c5557ccc6 100644 --- a/toolchain/parse/handle_alias.cpp +++ b/toolchain/parse/handle_alias.cpp @@ -14,7 +14,7 @@ auto HandleAlias(Context& context) -> void { auto state = context.PopState(); context.PushState(state, State::AliasAfterName); - context.PushState(State::DeclNameAndParamsAsNone, state.token); + context.PushState(State::DeclNameAndParams, state.token); } auto HandleAliasAfterName(Context& context) -> void { diff --git a/toolchain/parse/handle_choice.cpp b/toolchain/parse/handle_choice.cpp index e8983e748360..abd365c482a4 100644 --- a/toolchain/parse/handle_choice.cpp +++ b/toolchain/parse/handle_choice.cpp @@ -9,7 +9,7 @@ auto HandleChoiceIntroducer(Context& context) -> void { auto state = context.PopState(); context.PushState(state, State::ChoiceDefinitionStart); - context.PushState(State::DeclNameAndParamsAsOptional, state.token); + context.PushState(State::DeclNameAndParams, state.token); } auto HandleChoiceDefinitionStart(Context& context) -> void { diff --git a/toolchain/parse/handle_decl_name_and_params.cpp b/toolchain/parse/handle_decl_name_and_params.cpp index eff93583bb93..36bc1ab87191 100644 --- a/toolchain/parse/handle_decl_name_and_params.cpp +++ b/toolchain/parse/handle_decl_name_and_params.cpp @@ -6,118 +6,92 @@ namespace Carbon::Parse { -// Handles DeclNameAndParamsAs(Optional|Required). -static auto HandleDeclNameAndParams(Context& context, State after_name) - -> void { +// Handles DeclNameAndParams. +auto HandleDeclNameAndParams(Context& context) -> void { auto state = context.PopState(); - // TODO: Should handle designated names. - if (auto identifier = context.ConsumeIf(Lex::TokenKind::Identifier)) { - context.PushState(state, after_name); - - if (context.PositionIs(Lex::TokenKind::Period)) { - context.AddLeafNode(NodeKind::IdentifierName, *identifier); - context.PushState(state, State::PeriodAsDecl); - } else { - context.AddLeafNode(NodeKind::IdentifierName, *identifier); - } - } else { + auto identifier = context.ConsumeIf(Lex::TokenKind::Identifier); + if (!identifier) { CARBON_DIAGNOSTIC(ExpectedDeclName, Error, "`{0}` introducer should be followed by a name.", Lex::TokenKind); + CARBON_DIAGNOSTIC(ExpectedDeclNameAfterPeriod, Error, + "`.` should be followed by a name."); Lex::TokenIndex token = *context.position(); if (context.tokens().GetKind(token) == Lex::TokenKind::FileEnd) { // The end of file is an unhelpful diagnostic location. Instead, use the // introducer token. token = state.token; } - context.emitter().Emit(token, ExpectedDeclName, - context.tokens().GetKind(state.token)); + if (state.token == *context.position()) { + context.emitter().Emit(token, ExpectedDeclNameAfterPeriod); + } else { + context.emitter().Emit(token, ExpectedDeclName, + context.tokens().GetKind(state.token)); + } context.ReturnErrorOnState(); context.AddLeafNode(NodeKind::InvalidParse, *context.position(), /*has_error=*/true); - } -} - -auto HandleDeclNameAndParamsAsNone(Context& context) -> void { - HandleDeclNameAndParams(context, State::DeclNameAndParamsAfterNameAsNone); -} - -auto HandleDeclNameAndParamsAsOptional(Context& context) -> void { - HandleDeclNameAndParams(context, State::DeclNameAndParamsAfterNameAsOptional); -} - -auto HandleDeclNameAndParamsAsRequired(Context& context) -> void { - HandleDeclNameAndParams(context, State::DeclNameAndParamsAfterNameAsRequired); -} - -enum class Params : int8_t { - None, - Optional, - Required, -}; - -static auto HandleDeclNameAndParamsAfterName(Context& context, Params params) - -> void { - auto state = context.PopState(); - - if (context.PositionIs(Lex::TokenKind::Period)) { - // Continue designator processing. - context.PushState(state); - context.PushState(state, State::PeriodAsDecl); return; } - // TODO: We can have a parameter list after a name qualifier, regardless of - // whether the entity itself permits or requires parameters: - // - // fn Class(T:! type).AnotherClass(U:! type).Function(v: T) {} - // - // We should retain a `DeclNameAndParams...` state on the stack in all - // cases below to check for a period after a parameter list, which indicates - // that we've not finished parsing the declaration name. - - if (params == Params::None) { - return; - } - - if (context.PositionIs(Lex::TokenKind::OpenSquareBracket)) { - context.PushState(State::DeclNameAndParamsAfterImplicit); - context.PushState(State::PatternListAsImplicit); - } else if (context.PositionIs(Lex::TokenKind::OpenParen)) { - context.PushState(State::PatternListAsTuple); - } else if (params == Params::Required) { - CARBON_DIAGNOSTIC(ParamsRequiredByIntroducer, Error, - "`{0}` requires a `(` for parameters.", Lex::TokenKind); - context.emitter().Emit(*context.position(), ParamsRequiredByIntroducer, - context.tokens().GetKind(state.token)); - context.ReturnErrorOnState(); + context.AddLeafNode(NodeKind::IdentifierName, *identifier); + + switch (context.PositionKind()) { + case Lex::TokenKind::Period: + context.AddNode(NodeKind::NameQualifier, + context.ConsumeChecked(Lex::TokenKind::Period), + state.subtree_start, state.has_error); + context.PushState(State::DeclNameAndParams); + break; + + case Lex::TokenKind::OpenSquareBracket: + state.state = State::DeclNameAndParamsAfterImplicit; + context.PushState(state); + context.PushState(State::PatternListAsImplicit); + break; + + case Lex::TokenKind::OpenParen: + state.state = State::DeclNameAndParamsAfterParams; + context.PushState(state); + context.PushState(State::PatternListAsTuple); + break; + + default: + break; } } -auto HandleDeclNameAndParamsAfterNameAsNone(Context& context) -> void { - HandleDeclNameAndParamsAfterName(context, Params::None); -} - -auto HandleDeclNameAndParamsAfterNameAsOptional(Context& context) -> void { - HandleDeclNameAndParamsAfterName(context, Params::Optional); -} - -auto HandleDeclNameAndParamsAfterNameAsRequired(Context& context) -> void { - HandleDeclNameAndParamsAfterName(context, Params::Required); -} +// TODO: Check this in check. +// CARBON_DIAGNOSTIC(ParamsRequiredByIntroducer, Error, +// "`{0}` requires a `(` for parameters.", Lex::TokenKind); +// context.emitter().Emit(*context.position(), ParamsRequiredByIntroducer, +// context.tokens().GetKind(state.token)); auto HandleDeclNameAndParamsAfterImplicit(Context& context) -> void { - context.PopAndDiscardState(); + auto state = context.PopState(); - if (context.PositionIs(Lex::TokenKind::OpenParen)) { - context.PushState(State::PatternListAsTuple); - } else { + if (!context.PositionIs(Lex::TokenKind::OpenParen)) { CARBON_DIAGNOSTIC( ParamsRequiredAfterImplicit, Error, "A `(` for parameters is required after implicit parameters."); context.emitter().Emit(*context.position(), ParamsRequiredAfterImplicit); context.ReturnErrorOnState(); + return; + } + + state.state = State::DeclNameAndParamsAfterParams; + context.PushState(state); + context.PushState(State::PatternListAsTuple); +} + +auto HandleDeclNameAndParamsAfterParams(Context& context) -> void { + auto state = context.PopState(); + + if (auto period = context.ConsumeIf(Lex::TokenKind::Period)) { + context.AddNode(NodeKind::NameQualifier, *period, state.subtree_start, + state.has_error); + context.PushState(State::DeclNameAndParams); } } diff --git a/toolchain/parse/handle_function.cpp b/toolchain/parse/handle_function.cpp index 8a7670058341..c52d67431dbe 100644 --- a/toolchain/parse/handle_function.cpp +++ b/toolchain/parse/handle_function.cpp @@ -9,7 +9,7 @@ namespace Carbon::Parse { auto HandleFunctionIntroducer(Context& context) -> void { auto state = context.PopState(); context.PushState(state, State::FunctionAfterParams); - context.PushState(State::DeclNameAndParamsAsRequired, state.token); + context.PushState(State::DeclNameAndParams, state.token); } auto HandleFunctionAfterParams(Context& context) -> void { diff --git a/toolchain/parse/handle_import_and_package.cpp b/toolchain/parse/handle_import_and_package.cpp index fc9ce0fe62aa..e0921e419f0f 100644 --- a/toolchain/parse/handle_import_and_package.cpp +++ b/toolchain/parse/handle_import_and_package.cpp @@ -223,7 +223,7 @@ auto HandleExportName(Context& context) -> void { RestrictExportToApi(context, state); context.PushState(state, State::ExportNameFinish); - context.PushState(State::DeclNameAndParamsAsNone, state.token); + context.PushState(State::DeclNameAndParams, state.token); } auto HandleExportNameFinish(Context& context) -> void { diff --git a/toolchain/parse/handle_namespace.cpp b/toolchain/parse/handle_namespace.cpp index 3a1d4dbfc09e..14bfb7e475e1 100644 --- a/toolchain/parse/handle_namespace.cpp +++ b/toolchain/parse/handle_namespace.cpp @@ -9,7 +9,7 @@ namespace Carbon::Parse { auto HandleNamespace(Context& context) -> void { auto state = context.PopState(); context.PushState(state, State::NamespaceFinish); - context.PushState(State::DeclNameAndParamsAsNone, state.token); + context.PushState(State::DeclNameAndParams, state.token); } auto HandleNamespaceFinish(Context& context) -> void { diff --git a/toolchain/parse/handle_period.cpp b/toolchain/parse/handle_period.cpp index 570425681dd4..420a61ae4cc9 100644 --- a/toolchain/parse/handle_period.cpp +++ b/toolchain/parse/handle_period.cpp @@ -20,11 +20,9 @@ static auto HandlePeriodOrArrow(Context& context, NodeKind node_kind, if (context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::Identifier, NodeKind::IdentifierName)) { // OK, `.` identifier. - } else if (node_kind != NodeKind::QualifiedName && - context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::Base, + } else if (context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::Base, NodeKind::BaseName)) { - // OK, `.base`. This is allowed in any name context other than declaring a - // new qualified name: `fn Namespace.base() {}` + // OK, `.base`. } else if (paren_state != State::Invalid && context.PositionIs(Lex::TokenKind::OpenParen)) { state.state = paren_state; @@ -54,11 +52,6 @@ static auto HandlePeriodOrArrow(Context& context, NodeKind node_kind, context.AddNode(node_kind, dot, state.subtree_start, state.has_error); } -auto HandlePeriodAsDecl(Context& context) -> void { - HandlePeriodOrArrow(context, NodeKind::QualifiedName, State::Invalid, - /*is_arrow=*/false); -} - auto HandlePeriodAsExpr(Context& context) -> void { HandlePeriodOrArrow(context, NodeKind::MemberAccessExpr, State::CompoundMemberAccess, diff --git a/toolchain/parse/handle_type.cpp b/toolchain/parse/handle_type.cpp index aea94daa00ac..cf5db348e8e0 100644 --- a/toolchain/parse/handle_type.cpp +++ b/toolchain/parse/handle_type.cpp @@ -11,7 +11,7 @@ static auto HandleTypeAfterIntroducer(Context& context, State after_params_state) -> void { auto state = context.PopState(); context.PushState(state, after_params_state); - context.PushState(State::DeclNameAndParamsAsOptional, state.token); + context.PushState(State::DeclNameAndParams, state.token); } auto HandleTypeAfterIntroducerAsClass(Context& context) -> void { diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h index feaa6b29b1c3..1256cfe12549 100644 --- a/toolchain/parse/node_ids.h +++ b/toolchain/parse/node_ids.h @@ -73,7 +73,6 @@ using AnyImplAsId = NodeIdInCategory; using AnyMemberNameOrMemberExprId = NodeIdInCategory; using AnyModifierId = NodeIdInCategory; -using AnyNameComponentId = NodeIdInCategory; using AnyPatternId = NodeIdInCategory; using AnyStatementId = NodeIdInCategory; diff --git a/toolchain/parse/node_kind.cpp b/toolchain/parse/node_kind.cpp index f97761a10712..b5fe9c803555 100644 --- a/toolchain/parse/node_kind.cpp +++ b/toolchain/parse/node_kind.cpp @@ -23,9 +23,10 @@ auto operator<<(llvm::raw_ostream& output, NodeCategory category) } CARBON_NODE_CATEGORY(Decl); CARBON_NODE_CATEGORY(Expr); + CARBON_NODE_CATEGORY(ImplAs); + CARBON_NODE_CATEGORY(MemberExpr); CARBON_NODE_CATEGORY(MemberName); CARBON_NODE_CATEGORY(Modifier); - CARBON_NODE_CATEGORY(NameComponent); CARBON_NODE_CATEGORY(Pattern); CARBON_NODE_CATEGORY(Statement); #undef CARBON_NODE_CATEGORY diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 10c00973c7d3..ca7e72245784 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -215,9 +215,24 @@ CARBON_PARSE_NODE_KIND_BRACKET(LibraryDecl, LibraryIntroducer, // LibrarySpecifier CARBON_PARSE_NODE_KIND_CHILD_COUNT(LibrarySpecifier, 1, Library) +// Declaration names. +// +// _repeated_: NameQualifier +// _external_: IdentifierName +// _optional_ _external_: ImplicitParamList +// _optional_ _external_: TuplePattern +// _declaration name_ +// +// _external_: IdentifierName +// _optional_ _external_: ImplicitParamList +// _optional_ _external_: TuplePattern +// NameQualifier +CARBON_PARSE_NODE_KIND_BRACKET(NameQualifier, IdentifierName, + CARBON_IF_VALID(Period)) + // `export`: // ExportIntroducer -// _external_: IdentifierName or QualifiedName +// _external_: _declaration name_ // ExportDecl CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExportIntroducer, 0, Export) CARBON_PARSE_NODE_KIND_BRACKET(ExportDecl, ExportIntroducer, @@ -226,7 +241,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(ExportDecl, ExportIntroducer, // `namespace`: // NamespaceStart // _repeated_ _external_: modifier -// _external_: IdentifierName or QualifiedName +// _external_: _declaration name_ // Namespace CARBON_PARSE_NODE_KIND_CHILD_COUNT(NamespaceStart, 0, Namespace) CARBON_PARSE_NODE_KIND_BRACKET(Namespace, NamespaceStart, CARBON_IF_VALID(Semi)) @@ -244,10 +259,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(CodeBlock, CodeBlockStart, // // FunctionIntroducer // _repeated_ _external_: modifier -// _external_: IdentifierName or QualifiedName -// _optional_ _external_: ImplicitParamList -// _external_: TuplePattern -// _external_: type expression +// _external_: _declaration name_ // ReturnType // _function signature_ // @@ -283,7 +295,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(BuiltinFunctionDefinition, // `alias`: // AliasIntroducer // _repeated_ _external_: modifier -// _external_: IdentifierName or QualifiedName +// _external_: _declaration name_ // AliasInitializer // _external_: expression // Alias @@ -490,15 +502,6 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExprStart, 1, OpenParen) CARBON_PARSE_NODE_KIND_CHILD_COUNT(CallExprComma, 0, Comma) CARBON_PARSE_NODE_KIND_BRACKET(CallExpr, CallExprStart, CloseParen) -// A qualified declaration, such as `a.b`: -// _external_: IdentifierName or QualifiedName -// _external_: IdentifierName -// QualifiedName -// -// TODO: This will eventually more general expressions, for example with -// `GenericType(type_args).ChildType(child_type_args).Name`. -CARBON_PARSE_NODE_KIND_CHILD_COUNT(QualifiedName, 2, Period) - // A member access expression, such as `a.b` or // `GetObject().(Interface.member)`: // _external_: lhs expression @@ -655,7 +658,7 @@ CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Virtual) // `class`: // ClassIntroducer // _repeated_ _external_: modifier -// _external_: IdentifierName or QualifiedName +// _external_: _declaration name_ // _optional_ _external_: ImplicitParamList // _optional_ _external_: TuplePattern // ClassDefinitionStart @@ -695,7 +698,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(BaseDecl, BaseIntroducer, CARBON_IF_VALID(Semi)) // `interface`: // InterfaceIntroducer // _repeated_ _external_: modifier -// _external_: IdentifierName or QualifiedName +// _external_: _declaration name_ // _optional_ _external_: ImplicitParamList // _optional_ _external_: TuplePattern // InterfaceDefinitionStart @@ -749,7 +752,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(DefaultSelfImplAs, 0, As) // `constraint`: // NamedConstraintIntroducer // _repeated_ _external_: modifier -// _external_: IdentifierName or QualifiedName +// _external_: _declaration name_ // _optional_ _external_: ImplicitParamList // _optional_ _external_: TuplePattern // NamedConstraintDefinitionStart @@ -769,7 +772,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(NamedConstraintDecl, NamedConstraintIntroducer, // `choice`: // ChoiceIntroducer -// _external_: IdentifierName or QualifiedDecl +// _external_: _declaration name_ // ChoiceDefinitionStart // _optional_ _external_: ChoiceAlternativeList // ChoiceDefinition diff --git a/toolchain/parse/node_kind.h b/toolchain/parse/node_kind.h index 560aa4eb1127..555e8c67441f 100644 --- a/toolchain/parse/node_kind.h +++ b/toolchain/parse/node_kind.h @@ -26,9 +26,8 @@ enum class NodeCategory : uint32_t { MemberExpr = 1 << 3, MemberName = 1 << 4, Modifier = 1 << 5, - NameComponent = 1 << 6, - Pattern = 1 << 7, - Statement = 1 << 8, + Pattern = 1 << 6, + Statement = 1 << 7, None = 0, LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Statement) diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 840d87a432e0..d31fbd62a22d 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -227,50 +227,28 @@ CARBON_PARSE_STATE(CodeBlockFinish) // Handles a declaration name and parameters, such as `Foo[...](...)`. // -// Allowed parameters: -// - None: `Foo` only. -// - Optional: `Foo`, `Foo(...)`, or `Foo[...](...)`. -// - Required: `Foo(...)` or `Foo[...](...)`. -// // name . ... -// ^~~~ -// 1. PeriodAsDecl -// 2. DeclNameAndParamsAfterNameAs(None|Optional|Required) +// ^~~~~~ +// 1. DeclNameAndParams // -// name ... +// name [ ... ] // ^~~~ -// 1. DeclNameAndParamsAfterNameAs(None|Optional|Required) -// -// ??? -// ^ -// (state done) -CARBON_PARSE_STATE_VARIANTS3(DeclNameAndParams, None, Optional, Required) - -// Handles a declaration name between the main name and implicit parameters. -// -// name . ... -// ^ -// 1. PeriodAsDecl -// 2. DeclNameAndParamsAfterNameAs(None|Optional|Required) -// -// name [ ... ] (variant is not None) -// ^ // 1. PatternListAsImplicit // 2. DeclNameAndParamsAfterImplicit // -// name ( ... ) (variant is not None) -// ^ +// name ( ... ) +// ^~~~ // 1. PatternListAsTuple +// 2. DeclNameAndParamsAfterParams // -// name ... (variant is not Required) -// ^ +// name ... +// ^~~~ // (state done) // -// name ??? (variant is Required) -// ^ +// ??? +// ^ // (state done) -CARBON_PARSE_STATE_VARIANTS3(DeclNameAndParamsAfterName, None, Optional, - Required) +CARBON_PARSE_STATE(DeclNameAndParams) // Handles regular parameters such as `(...)` for the general declaration case. // Only used after implicit parameters. @@ -278,12 +256,25 @@ CARBON_PARSE_STATE_VARIANTS3(DeclNameAndParamsAfterName, None, Optional, // name [ ... ] ( ... ) // ^ // 1. PatternListAsTuple +// 1. DeclNameAndParamsAfterParams // // name [ ... ] ??? // ^ // (state done) CARBON_PARSE_STATE(DeclNameAndParamsAfterImplicit) +// Handles regular parameters such as `(...)` for the general declaration case. +// Only used after implicit parameters. +// +// name [ ... ] ( ... ) . +// ^ +// 1. DeclNameAndParams +// +// name [ ... ] ( ... ) ... +// ^ +// (state done) +CARBON_PARSE_STATE(DeclNameAndParamsAfterParams) + // Handles processing of a declaration scope. Things like fn, class, interface, // and so on. // @@ -402,12 +393,11 @@ CARBON_PARSE_STATE(DeclScopeLoop) // responsible for handling chaining. // // The forms of this are: -// - Qualified names in declarations. // - Member access expressions. // - Designated names in structs. // -// Declarations and expressions have qualifiers such as `x.y`, while structs -// have designators such as `.z`. +// Expressions have member accesses such as `x.y`, while structs have +// designators such as `.z`. // // . name // ^~~~~~ @@ -432,7 +422,7 @@ CARBON_PARSE_STATE(DeclScopeLoop) // ^~ // 1. OnlyParenExpr // 2. CompoundPointerMemberAccess -CARBON_PARSE_STATE_VARIANTS3(Period, Decl, Expr, Struct) +CARBON_PARSE_STATE_VARIANTS2(Period, Expr, Struct) // Handles a compound member access after we parse the name expression. // @@ -638,7 +628,7 @@ CARBON_PARSE_STATE(ExprStatementFinish) // // fn ... // ^ -// 1. DeclNameAndParamsAsRequired +// 1. DeclNameAndParams // 2. FunctionAfterParams CARBON_PARSE_STATE(FunctionIntroducer) @@ -694,7 +684,7 @@ CARBON_PARSE_STATE(FunctionDefinitionFinish) // // export Name; // ^ -// 1. DeclNameAndParamsAsNone +// 1. DeclNameAndParams // 2. ExportFinish CARBON_PARSE_STATE(ExportName) @@ -729,7 +719,7 @@ CARBON_PARSE_STATE(Library) // // namespace ... // ^ -// 1. DeclNameAndParamsAsNone +// 1. DeclNameAndParams // 2. NamespaceFinish CARBON_PARSE_STATE(Namespace) @@ -755,7 +745,7 @@ CARBON_PARSE_STATE(Package) // // alias ... // ^ -// 1. DeclNameAndParamsAsNone +// 1. DeclNameAndParams // 2. AliasInitializer CARBON_PARSE_STATE(Alias) @@ -1217,7 +1207,7 @@ CARBON_PARSE_STATE_VARIANTS4(DeclDefinitionFinish, Class, Impl, Interface, // // class/interface/constraint ... // ^ -// 1. DeclNameAndParamsAsOptional +// 1. DeclNameAndParams // 2. DeclOrDefinitionAs(Class|Interface|NamedConstraint) CARBON_PARSE_STATE_VARIANTS3(TypeAfterIntroducer, Class, Interface, NamedConstraint) @@ -1402,7 +1392,7 @@ CARBON_PARSE_STATE(LetFinish) // // choice ... // ^~~~~~ -// 1. DeclNameAndParamsAsOptional +// 1. DeclNameAndParams // 2. ChoiceAlternativeListStart // 3. ChoiceDefinitionFinish CARBON_PARSE_STATE(ChoiceIntroducer) diff --git a/toolchain/parse/testdata/alias/basic.carbon b/toolchain/parse/testdata/alias/basic.carbon index ec2c72493087..7d33aded7f8b 100644 --- a/toolchain/parse/testdata/alias/basic.carbon +++ b/toolchain/parse/testdata/alias/basic.carbon @@ -40,8 +40,8 @@ fn F() { // CHECK:STDOUT: {kind: 'Alias', text: ';', subtree_size: 7}, // CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'NS'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: 'ns'}, -// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'ns'}, // CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'foo'}, // CHECK:STDOUT: {kind: 'Alias', text: ';', subtree_size: 7}, diff --git a/toolchain/parse/testdata/function/declaration/fail_identifier_instead_of_sig.carbon b/toolchain/parse/testdata/function/declaration/fail_identifier_instead_of_sig.carbon index 43deb5f4f59a..858f0c692a3c 100644 --- a/toolchain/parse/testdata/function/declaration/fail_identifier_instead_of_sig.carbon +++ b/toolchain/parse/testdata/function/declaration/fail_identifier_instead_of_sig.carbon @@ -4,7 +4,7 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_identifier_instead_of_sig.carbon:[[@LINE+3]]:8: ERROR: `fn` requires a `(` for parameters. +// CHECK:STDERR: fail_identifier_instead_of_sig.carbon:[[@LINE+3]]:8: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition. // CHECK:STDERR: fn foo bar; // CHECK:STDERR: ^~~ fn foo bar; diff --git a/toolchain/parse/testdata/function/declaration/fail_missing_implicit_close.carbon b/toolchain/parse/testdata/function/declaration/fail_missing_implicit_close.carbon index 01a0150223c0..20206c75be69 100644 --- a/toolchain/parse/testdata/function/declaration/fail_missing_implicit_close.carbon +++ b/toolchain/parse/testdata/function/declaration/fail_missing_implicit_close.carbon @@ -9,7 +9,7 @@ // CHECK:STDERR: fn Div[(); // CHECK:STDERR: ^ // CHECK:STDERR: -// CHECK:STDERR: fail_missing_implicit_close.carbon:[[@LINE+3]]:7: ERROR: `fn` requires a `(` for parameters. +// CHECK:STDERR: fail_missing_implicit_close.carbon:[[@LINE+3]]:7: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition. // CHECK:STDERR: fn Div[(); // CHECK:STDERR: ^ fn Div[(); diff --git a/toolchain/parse/testdata/function/declaration/fail_no_sig_or_semi.carbon b/toolchain/parse/testdata/function/declaration/fail_no_sig_or_semi.carbon index 099cacbb1a4d..e3b8f8801e52 100644 --- a/toolchain/parse/testdata/function/declaration/fail_no_sig_or_semi.carbon +++ b/toolchain/parse/testdata/function/declaration/fail_no_sig_or_semi.carbon @@ -6,7 +6,7 @@ fn foo -// CHECK:STDERR: fail_no_sig_or_semi.carbon:[[@LINE+10]]:21: ERROR: `fn` requires a `(` for parameters. +// CHECK:STDERR: fail_no_sig_or_semi.carbon:[[@LINE+10]]:21: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition. // CHECK:STDERR: // CHECK:STDOUT: ] // CHECK:STDERR: ^ // CHECK:STDOUT: - filename: fail_no_sig_or_semi.carbon diff --git a/toolchain/parse/testdata/function/declaration/no_params.carbon b/toolchain/parse/testdata/function/declaration/no_params.carbon new file mode 100644 index 000000000000..c4b833ca75c5 --- /dev/null +++ b/toolchain/parse/testdata/function/declaration/no_params.carbon @@ -0,0 +1,17 @@ +// 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 + +fn foo {} + +// CHECK:STDOUT: - filename: no_params.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'foo'}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon b/toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon index 437dff4e7e1f..da96ff27514b 100644 --- a/toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon +++ b/toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon @@ -26,19 +26,9 @@ class C { } } -// TODO: The error recovery here is not very good. The `(` is treated as -// starting the function parameter list. -// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+11]]:6: ERROR: Expected identifier after `.`. +// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+3]]:6: ERROR: `.` should be followed by a name. // CHECK:STDERR: fn C.(Self as Interface).F() {} // CHECK:STDERR: ^ -// CHECK:STDERR: -// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+7]]:7: ERROR: Expected binding pattern. -// CHECK:STDERR: fn C.(Self as Interface).F() {} -// CHECK:STDERR: ^~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+3]]:25: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition. -// CHECK:STDERR: fn C.(Self as Interface).F() {} -// CHECK:STDERR: ^ fn C.(Self as Interface).F() {} // CHECK:STDOUT: - filename: fail_out_of_line_member.carbon @@ -84,13 +74,8 @@ fn C.(Self as Interface).F() {} // CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 15}, // CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: '(', has_error: yes}, -// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Self', has_error: yes}, -// CHECK:STDOUT: {kind: 'InvalidParse', text: 'Self', has_error: yes}, -// CHECK:STDOUT: {kind: 'BindingPattern', text: 'Self', has_error: yes, subtree_size: 3}, -// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5}, -// CHECK:STDOUT: {kind: 'FunctionDecl', text: '}', has_error: yes, subtree_size: 10}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '(', has_error: yes}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: '}', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/generics/params/name_qualifier.carbon b/toolchain/parse/testdata/generics/params/name_qualifier.carbon new file mode 100644 index 000000000000..e2d2e5259f9a --- /dev/null +++ b/toolchain/parse/testdata/generics/params/name_qualifier.carbon @@ -0,0 +1,169 @@ +// 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 + +class GenericClass1(T:! type) { + fn F(); +} + +fn GenericClass1(T:! type).F() {} + +class GenericClass2[T:! type](X:! T) { + fn F(); +} + +fn GenericClass2[T:! type](X:! T).F() {} + +class OuterGeneric(T:! type) { + class InnerGeneric(U:! type); +} + +class OuterGeneric(T:! type).InnerGeneric(U:! type) { + fn F(x: T, y: U); +} + +fn OuterGeneric(T:! type).InnerGeneric(U:! type).F(x: T, y: U) {} + +// CHECK:STDOUT: - filename: name_qualifier.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'GenericClass1'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'GenericClass1'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'GenericClass2'}, +// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'X'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 19}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'GenericClass2'}, +// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'X'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'OuterGeneric'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'InnerGeneric'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'U'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'OuterGeneric'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'InnerGeneric'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'U'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PatternListComma', text: ','}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'y'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'U'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 28}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'OuterGeneric'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'InnerGeneric'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'U'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PatternListComma', text: ','}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'y'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'U'}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 26}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 27}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/namespace/fail_args.carbon b/toolchain/parse/testdata/namespace/args.carbon similarity index 63% rename from toolchain/parse/testdata/namespace/fail_args.carbon rename to toolchain/parse/testdata/namespace/args.carbon index fdb7e7ad120b..c53c865fb051 100644 --- a/toolchain/parse/testdata/namespace/fail_args.carbon +++ b/toolchain/parse/testdata/namespace/args.carbon @@ -4,16 +4,16 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_args.carbon:[[@LINE+3]]:14: ERROR: `namespace` declarations must end with a `;`. -// CHECK:STDERR: namespace Foo(); -// CHECK:STDERR: ^ +// This is rejected in check. namespace Foo(); -// CHECK:STDOUT: - filename: fail_args.carbon +// CHECK:STDOUT: - filename: args.carbon // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'Namespace', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Namespace', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/namespace/fail_arrow.carbon b/toolchain/parse/testdata/namespace/fail_arrow.carbon index f097cb601cd4..09956ebc8f25 100644 --- a/toolchain/parse/testdata/namespace/fail_arrow.carbon +++ b/toolchain/parse/testdata/namespace/fail_arrow.carbon @@ -6,10 +6,10 @@ namespace Foo; -// CHECK:STDERR: fail_arrow.carbon:[[@LINE+3]]:7: ERROR: `fn` requires a `(` for parameters. -// CHECK:STDERR: fn Foo->Bar() {} -// CHECK:STDERR: ^~ -fn Foo->Bar() {} +// CHECK:STDERR: fail_arrow.carbon:[[@LINE+3]]:10: ERROR: `class` declarations must either end with a `;` or have a `{ ... }` block for a definition. +// CHECK:STDERR: class Foo->Bar {} +// CHECK:STDERR: ^~ +class Foo->Bar {} // CHECK:STDOUT: - filename: fail_arrow.carbon // CHECK:STDOUT: parse_tree: [ @@ -17,13 +17,8 @@ fn Foo->Bar() {} // CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, // CHECK:STDOUT: {kind: 'Namespace', text: ';', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Bar'}, -// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', has_error: yes, subtree_size: 7}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'ClassDecl', text: '}', has_error: yes, subtree_size: 3}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/namespace/fail_incomplete_name.carbon b/toolchain/parse/testdata/namespace/fail_incomplete_name.carbon index 3cd394e6e8bb..4b440f4acdd5 100644 --- a/toolchain/parse/testdata/namespace/fail_incomplete_name.carbon +++ b/toolchain/parse/testdata/namespace/fail_incomplete_name.carbon @@ -4,7 +4,7 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_incomplete_name.carbon:[[@LINE+3]]:15: ERROR: Expected identifier after `.`. +// CHECK:STDERR: fail_incomplete_name.carbon:[[@LINE+3]]:15: ERROR: `.` should be followed by a name. // CHECK:STDERR: namespace Foo.; // CHECK:STDERR: ^ namespace Foo.; @@ -14,8 +14,8 @@ namespace Foo.; // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'Namespace', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'Namespace', text: ';', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/namespace/nested.carbon b/toolchain/parse/testdata/namespace/nested.carbon index 748f9d680eda..f5f400b414bd 100644 --- a/toolchain/parse/testdata/namespace/nested.carbon +++ b/toolchain/parse/testdata/namespace/nested.carbon @@ -19,15 +19,15 @@ fn Foo.Bar.Baz() { // CHECK:STDOUT: {kind: 'Namespace', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Bar'}, -// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Bar'}, // CHECK:STDOUT: {kind: 'Namespace', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Bar'}, -// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Baz'}, -// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Bar'}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Baz'}, // CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, // CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 9}, diff --git a/toolchain/parse/testdata/packages/export.carbon b/toolchain/parse/testdata/packages/export.carbon index 02d30c5b3216..63c65bf2c819 100644 --- a/toolchain/parse/testdata/packages/export.carbon +++ b/toolchain/parse/testdata/packages/export.carbon @@ -74,7 +74,7 @@ export Foo package Pkg; -// CHECK:STDERR: fail_incomplete_qual_name.carbon:[[@LINE+4]]:12: ERROR: Expected identifier after `.`. +// CHECK:STDERR: fail_incomplete_qual_name.carbon:[[@LINE+4]]:12: ERROR: `.` should be followed by a name. // CHECK:STDERR: export Foo.; // CHECK:STDERR: ^ // CHECK:STDERR: @@ -157,8 +157,8 @@ export Foo; // CHECK:STDOUT: {kind: 'PackageDecl', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Bar'}, -// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Bar'}, // CHECK:STDOUT: {kind: 'ExportDecl', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] @@ -225,9 +225,9 @@ export Foo; // CHECK:STDOUT: {kind: 'PackageDecl', text: ';', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ExportIntroducer', text: 'export'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, -// CHECK:STDOUT: {kind: 'IdentifierName', text: ';', has_error: yes}, -// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'ExportDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameQualifier', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'ExportDecl', text: ';', has_error: yes, subtree_size: 5}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] // CHECK:STDOUT: - filename: fail_incomplete_qual_name2.carbon diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 73619e2d807c..af89d7935745 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -110,8 +110,7 @@ using EmptyDecl = // A name in a non-expression context, such as a declaration. using IdentifierName = - LeafNode; + LeafNode; // A name in an expression context. using IdentifierNameExpr = @@ -130,17 +129,26 @@ using SelfTypeNameExpr = // declared name, as in `{.base: partial B}`. using BaseName = LeafNode; -// A qualified name: `A.B`. -struct QualifiedName { - static constexpr auto Kind = - NodeKind::QualifiedName.Define(NodeCategory::NameComponent); +// An unqualified name and optionally a following sequence of parameters. +// For example, `A`, `A(n: i32)`, or `A[T:! type](n: T)`. +struct NameAndParams { + IdentifierNameId name; + std::optional implicit_params; + std::optional params; +}; - // For now, this is either an IdentifierName or a QualifiedName. - AnyNameComponentId lhs; +// A name qualifier: `A.`, `A(T:! type).`, or `A[T:! type](N:! T).`. +struct NameQualifier { + static constexpr auto Kind = NodeKind::NameQualifier.Define(); - // TODO: This will eventually need to support more general expressions, for - // example `GenericType(type_args).ChildType(child_type_args).Name`. - IdentifierNameId rhs; + NameAndParams name_and_params; +}; + +// A complete name in a declaration: `A.C(T:! type).F(n: i32)`. +// Note that this includes the parameters of the entity itself. +struct DeclName { + llvm::SmallVector qualifiers; + NameAndParams name_and_params; }; // Library, package, import, export @@ -202,7 +210,7 @@ struct ExportDecl { ExportIntroducerId introducer; llvm::SmallVector modifiers; - AnyNameComponentId name; + DeclName name; }; // Namespace nodes @@ -216,7 +224,7 @@ struct Namespace { NamespaceStartId introducer; llvm::SmallVector modifiers; - AnyNameComponentId name; + DeclName name; }; // Pattern nodes @@ -297,10 +305,7 @@ struct FunctionSignature { FunctionIntroducerId introducer; llvm::SmallVector modifiers; - // For now, this is either an IdentifierName or a QualifiedName. - AnyNameComponentId name; - std::optional implicit_params; - TuplePatternId params; + DeclName name; std::optional return_type; }; @@ -345,8 +350,7 @@ struct Alias { AliasIntroducerId introducer; llvm::SmallVector modifiers; - // For now, this is either an IdentifierName or a QualifiedName. - AnyNameComponentId name; + DeclName name; AliasInitializerId equals; AnyExprId initializer; }; @@ -805,9 +809,7 @@ struct ChoiceSignature { ChoiceIntroducerId introducer; llvm::SmallVector modifiers; - AnyNameComponentId name; - std::optional implicit_params; - std::optional params; + DeclName name; }; using ChoiceDefinitionStart = ChoiceSignature; @@ -890,9 +892,7 @@ struct ClassSignature { ClassIntroducerId introducer; llvm::SmallVector modifiers; - AnyNameComponentId name; - std::optional implicit_params; - std::optional params; + DeclName name; }; // `class C;` @@ -953,9 +953,7 @@ struct InterfaceSignature { InterfaceIntroducerId introducer; llvm::SmallVector modifiers; - AnyNameComponentId name; - std::optional implicit_params; - std::optional params; + DeclName name; }; // `interface I;` @@ -1039,9 +1037,7 @@ struct NamedConstraintSignature { NamedConstraintIntroducerId introducer; llvm::SmallVector modifiers; - AnyNameComponentId name; - std::optional implicit_params; - std::optional params; + DeclName name; }; // `constraint NC;` diff --git a/toolchain/sem_ir/bind_name.h b/toolchain/sem_ir/bind_name.h index 6713f7177d6e..53331c7eb3ef 100644 --- a/toolchain/sem_ir/bind_name.h +++ b/toolchain/sem_ir/bind_name.h @@ -25,7 +25,7 @@ struct BindNameInfo : public Printable { CompileTimeBindIndex bind_index; }; -// Hashing for BindNameInfo. +// Hashing for BindNameInfo. See common/hashing.h. inline auto CarbonHashValue(const BindNameInfo& value, uint64_t seed) -> HashCode { Hasher hasher(seed); From b1ae4eb4559f0939bb2b054b007e3a771cccf303 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 24 May 2024 23:43:15 +0000 Subject: [PATCH 2/7] Underline parameters when rejecting them. --- toolchain/check/decl_name_stack.cpp | 15 ++++++++++----- .../testdata/alias/no_prelude/fail_params.carbon | 4 ++-- .../check/testdata/namespace/fail_params.carbon | 12 ++++++------ .../no_prelude/fail_export_name_params.carbon | 4 ++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index 7412eb818e19..56464359e3ff 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -6,6 +6,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" +#include "toolchain/check/diagnostic_helpers.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/sem_ir/ids.h" @@ -57,10 +58,11 @@ auto DeclNameStack::PushScopeAndStartName() -> void { auto DeclNameStack::FinishName() -> NameContext { CARBON_CHECK(decl_name_stack_.back().state != NameContext::State::Finished) << "Finished name twice"; - auto params_id = - context_->node_stack().PopIf(); - auto implicit_params_id = - context_->node_stack().PopIf(); + auto [params_loc_id, params_id] = + context_->node_stack().PopWithNodeIdIf(); + auto [implicit_params_loc_id, implicit_params_id] = + context_->node_stack() + .PopWithNodeIdIf(); auto [loc_id, name_id] = context_->node_stack().PopNameWithNodeId(); ApplyNameQualifier(loc_id, name_id); @@ -69,7 +71,10 @@ auto DeclNameStack::FinishName() -> NameContext { // TODO: Say which kind of declaraation we're parsing. CARBON_DIAGNOSTIC(UnexpectedDeclNameParams, Error, "Declaration cannot have parameters."); - context_->emitter().Emit(decl_name_stack_.back().loc_id, + // Point to the lexically first parameter list in the diagnostic. + context_->emitter().Emit(implicit_params_id + ? static_cast(implicit_params_loc_id) + : params_loc_id, UnexpectedDeclNameParams); } diff --git a/toolchain/check/testdata/alias/no_prelude/fail_params.carbon b/toolchain/check/testdata/alias/no_prelude/fail_params.carbon index 56105e094ae9..1cadc96aa6b8 100644 --- a/toolchain/check/testdata/alias/no_prelude/fail_params.carbon +++ b/toolchain/check/testdata/alias/no_prelude/fail_params.carbon @@ -4,9 +4,9 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_params.carbon:[[@LINE+7]]:7: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: fail_params.carbon:[[@LINE+7]]:8: ERROR: Declaration cannot have parameters. // CHECK:STDERR: alias A(T:! type) = T*; -// CHECK:STDERR: ^ +// CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_params.carbon:[[@LINE+3]]:21: ERROR: Alias initializer must be a name reference. // CHECK:STDERR: alias A(T:! type) = T*; diff --git a/toolchain/check/testdata/namespace/fail_params.carbon b/toolchain/check/testdata/namespace/fail_params.carbon index deac129910d5..9ff4406fd419 100644 --- a/toolchain/check/testdata/namespace/fail_params.carbon +++ b/toolchain/check/testdata/namespace/fail_params.carbon @@ -4,24 +4,24 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_params.carbon:[[@LINE+4]]:11: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: fail_params.carbon:[[@LINE+4]]:12: ERROR: Declaration cannot have parameters. // CHECK:STDERR: namespace A(); -// CHECK:STDERR: ^ +// CHECK:STDERR: ^~ // CHECK:STDERR: namespace A(); // Parameters are ignored for error recovery. fn A.F() {} -// CHECK:STDERR: fail_params.carbon:[[@LINE+4]]:11: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: fail_params.carbon:[[@LINE+4]]:12: ERROR: Declaration cannot have parameters. // CHECK:STDERR: namespace B(n: i32); -// CHECK:STDERR: ^ +// CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: namespace B(n: i32); -// CHECK:STDERR: fail_params.carbon:[[@LINE+3]]:11: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: fail_params.carbon:[[@LINE+3]]:12: ERROR: Declaration cannot have parameters. // CHECK:STDERR: namespace C[T:! type](x: T); -// CHECK:STDERR: ^ +// CHECK:STDERR: ^~~~~~~~~~ namespace C[T:! type](x: T); // CHECK:STDOUT: --- fail_params.carbon diff --git a/toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon b/toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon index 6abf345f5b09..b370b1686e2c 100644 --- a/toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon +++ b/toolchain/check/testdata/packages/no_prelude/fail_export_name_params.carbon @@ -19,9 +19,9 @@ import library "a"; export C1; -// CHECK:STDERR: fail_b.carbon:[[@LINE+3]]:8: ERROR: Declaration cannot have parameters. +// CHECK:STDERR: fail_b.carbon:[[@LINE+3]]:10: ERROR: Declaration cannot have parameters. // CHECK:STDERR: export C2(T:! type); -// CHECK:STDERR: ^~ +// CHECK:STDERR: ^~~~~~~~~~ export C2(T:! type); // CHECK:STDOUT: --- a.carbon From 5058d05848ddf3e2b1c03f99e738b91f957c45a4 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 24 May 2024 23:54:46 +0000 Subject: [PATCH 3/7] Cleanups. --- toolchain/diagnostics/diagnostic_kind.def | 1 - toolchain/parse/handle_decl_name_and_params.cpp | 7 ------- 2 files changed, 8 deletions(-) diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 55ad4ae5226f..a19385b40ece 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -114,7 +114,6 @@ CARBON_DIAGNOSTIC_KIND(ExpectedDeclNameAfterPeriod) CARBON_DIAGNOSTIC_KIND(ExpectedDeclSemi) CARBON_DIAGNOSTIC_KIND(ExpectedDeclSemiOrDefinition) CARBON_DIAGNOSTIC_KIND(ParamsRequiredAfterImplicit) -CARBON_DIAGNOSTIC_KIND(ParamsRequiredByIntroducer) CARBON_DIAGNOSTIC_KIND(ExpectedAfterBase) CARBON_DIAGNOSTIC_KIND(ExpectedBuiltinName) CARBON_DIAGNOSTIC_KIND(ImplExpectedAfterForall) diff --git a/toolchain/parse/handle_decl_name_and_params.cpp b/toolchain/parse/handle_decl_name_and_params.cpp index 36bc1ab87191..bdc0438a5b0d 100644 --- a/toolchain/parse/handle_decl_name_and_params.cpp +++ b/toolchain/parse/handle_decl_name_and_params.cpp @@ -6,7 +6,6 @@ namespace Carbon::Parse { -// Handles DeclNameAndParams. auto HandleDeclNameAndParams(Context& context) -> void { auto state = context.PopState(); @@ -62,12 +61,6 @@ auto HandleDeclNameAndParams(Context& context) -> void { } } -// TODO: Check this in check. -// CARBON_DIAGNOSTIC(ParamsRequiredByIntroducer, Error, -// "`{0}` requires a `(` for parameters.", Lex::TokenKind); -// context.emitter().Emit(*context.position(), ParamsRequiredByIntroducer, -// context.tokens().GetKind(state.token)); - auto HandleDeclNameAndParamsAfterImplicit(Context& context) -> void { auto state = context.PopState(); From 30ec4bc8f2f5ceb41a823739777126ccea496704 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 25 May 2024 00:07:43 +0000 Subject: [PATCH 4/7] Fix unit test. --- toolchain/parse/typed_nodes_test.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/toolchain/parse/typed_nodes_test.cpp b/toolchain/parse/typed_nodes_test.cpp index 1252b73f0a6f..522dc58bb9d8 100644 --- a/toolchain/parse/typed_nodes_test.cpp +++ b/toolchain/parse/typed_nodes_test.cpp @@ -231,7 +231,7 @@ Aggregate [^:]*: success // Use Regex matching to avoid hard-coding the result of `typeinfo(T).name()`. EXPECT_THAT(err2.message(), testing::MatchesRegex( R"Trace(Aggregate [^:]*: begin -NodeIdInCategory MemberName: kind IdentifierName consumed +NodeIdInCategory MemberExpr\|MemberName: kind IdentifierName consumed NodeIdInCategory Expr: kind PointerMemberAccessExpr consumed Aggregate [^:]*: success )Trace")); @@ -251,13 +251,21 @@ TEST_F(TypedNodeTest, VerifyExtractTraceClassDecl) { // Use Regex matching to avoid hard-coding the result of `typeinfo(T).name()`. EXPECT_THAT(err.message(), testing::MatchesRegex( R"Trace(Aggregate [^:]*: begin +Aggregate [^:]*: begin +Aggregate [^:]*: begin Optional [^:]*: begin NodeIdForKind: TuplePattern consumed Optional [^:]*: found Optional [^:]*: begin -NodeIdForKind error: wrong kind QualifiedName, expected ImplicitParamList +NodeIdForKind error: wrong kind IdentifierName, expected ImplicitParamList Optional [^:]*: missing -NodeIdInCategory NameComponent: kind QualifiedName consumed +NodeIdForKind: IdentifierName consumed +Aggregate [^:]*: success +Vector: begin +NodeIdForKind: NameQualifier consumed +NodeIdForKind error: wrong kind AbstractModifier, expected NameQualifier +Vector: end +Aggregate [^:]*: success Vector: begin NodeIdInCategory Modifier: kind AbstractModifier consumed NodeIdInCategory Modifier: kind PrivateModifier consumed From b30b41b587579ba13048c0ba2fd22d59c49c9e63 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 28 May 2024 13:23:46 -0700 Subject: [PATCH 5/7] Fix typo. Co-authored-by: Jon Ross-Perkins --- toolchain/check/decl_name_stack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index 56464359e3ff..4ddf3d9bfa26 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -68,7 +68,7 @@ auto DeclNameStack::FinishName() -> NameContext { ApplyNameQualifier(loc_id, name_id); if (params_id || implicit_params_id) { - // TODO: Say which kind of declaraation we're parsing. + // TODO: Say which kind of declaration we're parsing. CARBON_DIAGNOSTIC(UnexpectedDeclNameParams, Error, "Declaration cannot have parameters."); // Point to the lexically first parameter list in the diagnostic. From 4abfc8550388c762a5a2440b27a782f42c119ac2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 28 May 2024 15:36:04 -0700 Subject: [PATCH 6/7] Fix numbering. Co-authored-by: Jon Ross-Perkins --- toolchain/parse/state.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index d31fbd62a22d..27a33c2e8819 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -256,7 +256,7 @@ CARBON_PARSE_STATE(DeclNameAndParams) // name [ ... ] ( ... ) // ^ // 1. PatternListAsTuple -// 1. DeclNameAndParamsAfterParams +// 2. DeclNameAndParamsAfterParams // // name [ ... ] ??? // ^ From c99f62a54e823c6ceab2e0c5e52a64f6148c762f Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 28 May 2024 22:47:14 +0000 Subject: [PATCH 7/7] Review comment. --- toolchain/parse/handle_decl_name_and_params.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/toolchain/parse/handle_decl_name_and_params.cpp b/toolchain/parse/handle_decl_name_and_params.cpp index bdc0438a5b0d..4c3e5214a86c 100644 --- a/toolchain/parse/handle_decl_name_and_params.cpp +++ b/toolchain/parse/handle_decl_name_and_params.cpp @@ -11,11 +11,6 @@ auto HandleDeclNameAndParams(Context& context) -> void { auto identifier = context.ConsumeIf(Lex::TokenKind::Identifier); if (!identifier) { - CARBON_DIAGNOSTIC(ExpectedDeclName, Error, - "`{0}` introducer should be followed by a name.", - Lex::TokenKind); - CARBON_DIAGNOSTIC(ExpectedDeclNameAfterPeriod, Error, - "`.` should be followed by a name."); Lex::TokenIndex token = *context.position(); if (context.tokens().GetKind(token) == Lex::TokenKind::FileEnd) { // The end of file is an unhelpful diagnostic location. Instead, use the @@ -23,8 +18,13 @@ auto HandleDeclNameAndParams(Context& context) -> void { token = state.token; } if (state.token == *context.position()) { + CARBON_DIAGNOSTIC(ExpectedDeclNameAfterPeriod, Error, + "`.` should be followed by a name."); context.emitter().Emit(token, ExpectedDeclNameAfterPeriod); } else { + CARBON_DIAGNOSTIC(ExpectedDeclName, Error, + "`{0}` introducer should be followed by a name.", + Lex::TokenKind); context.emitter().Emit(token, ExpectedDeclName, context.tokens().GetKind(state.token)); }