From 91c1e1049d7d0cc9b160dbce569e6321f2aa1a62 Mon Sep 17 00:00:00 2001 From: Lucile Rose Nihlen Date: Tue, 1 Sep 2026 01:19:13 +0000 Subject: [PATCH] Add parsing of default value exprs in pattern lists (#7631) Adds parsing support only for default value expressions in pattern lists, including leaving the default value unspecified with an underscore `_`. Per #7521. --- toolchain/check/handle_pattern_list.cpp | 10 ++ .../facet/validate_rewrite_constraints.carbon | 16 +- toolchain/parse/context.h | 2 +- toolchain/parse/handle_pattern_list.cpp | 52 ++++++ toolchain/parse/node_kind.def | 2 + toolchain/parse/state.def | 26 ++- .../testdata/function/default_values.carbon | 102 ++++++++++++ .../testdata/struct/struct_pattern.carbon | 24 +-- .../testdata/where_expr/fail_rewrite.carbon | 150 ++++++++++-------- toolchain/parse/typed_nodes.h | 18 ++- 10 files changed, 319 insertions(+), 83 deletions(-) create mode 100644 toolchain/parse/testdata/function/default_values.carbon diff --git a/toolchain/check/handle_pattern_list.cpp b/toolchain/check/handle_pattern_list.cpp index 152ac2fdfbe2..e2d16117fc30 100644 --- a/toolchain/check/handle_pattern_list.cpp +++ b/toolchain/check/handle_pattern_list.cpp @@ -151,4 +151,14 @@ auto HandleParseNode(Context& context, Parse::PatternListCommaId /*node_id*/) return true; } +auto HandleParseNode(Context& context, Parse::DefaultValueUnspecifiedId node_id) + -> bool { + return context.TODO(node_id, "pattern default values"); +} + +auto HandleParseNode(Context& context, Parse::DefaultValuePatternId node_id) + -> bool { + return context.TODO(node_id, "pattern default values"); +} + } // namespace Carbon::Check diff --git a/toolchain/check/testdata/facet/validate_rewrite_constraints.carbon b/toolchain/check/testdata/facet/validate_rewrite_constraints.carbon index ea844447a2fe..2475ef4e493a 100644 --- a/toolchain/check/testdata/facet/validate_rewrite_constraints.carbon +++ b/toolchain/check/testdata/facet/validate_rewrite_constraints.carbon @@ -107,14 +107,18 @@ library "[[@TEST_NAME]]"; interface I { let X: type; } interface J { let Y: type; } -// CHECK:STDERR: fail_todo_dependent_rules_compound.carbon:[[@LINE+8]]:30: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] +// CHECK:STDERR: fail_todo_dependent_rules_compound.carbon:[[@LINE+12]]:30: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] // CHECK:STDERR: fn F(generic T: I & J where .(I.X) = .(J.Y)) { // CHECK:STDERR: ^ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_dependent_rules_compound.carbon:[[@LINE+4]]:30: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: fail_todo_dependent_rules_compound.carbon:[[@LINE+8]]:30: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: fn F(generic T: I & J where .(I.X) = .(J.Y)) { // CHECK:STDERR: ^ // CHECK:STDERR: +// CHECK:STDERR: fail_todo_dependent_rules_compound.carbon:[[@LINE+4]]:39: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] +// CHECK:STDERR: fn F(generic T: I & J where .(I.X) = .(J.Y)) { +// CHECK:STDERR: ^ +// CHECK:STDERR: fn F(generic T: I & J where .(I.X) = .(J.Y)) { // Allowed since `T impls I`, `T impls J`, and has constraint providing // T.(I.X) = T.(J.Y)`. @@ -141,14 +145,18 @@ interface I(T: type) { let X: type; } final impl forall [J: I(())] J as I({}) where .X = {} {} -// CHECK:STDERR: fail_todo_parameterized_interface_compound.carbon:[[@LINE+8]]:38: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] +// CHECK:STDERR: fail_todo_parameterized_interface_compound.carbon:[[@LINE+12]]:38: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] // CHECK:STDERR: fn F(generic T: I(()) & I({}) where .(I(()).X) = () and .(I({}).X) = {}) {} // CHECK:STDERR: ^ // CHECK:STDERR: -// CHECK:STDERR: fail_todo_parameterized_interface_compound.carbon:[[@LINE+4]]:38: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: fail_todo_parameterized_interface_compound.carbon:[[@LINE+8]]:38: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: fn F(generic T: I(()) & I({}) where .(I(()).X) = () and .(I({}).X) = {}) {} // CHECK:STDERR: ^ // CHECK:STDERR: +// CHECK:STDERR: fail_todo_parameterized_interface_compound.carbon:[[@LINE+4]]:58: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] +// CHECK:STDERR: fn F(generic T: I(()) & I({}) where .(I(()).X) = () and .(I({}).X) = {}) {} +// CHECK:STDERR: ^ +// CHECK:STDERR: fn F(generic T: I(()) & I({}) where .(I(()).X) = () and .(I({}).X) = {}) {} fn G(generic T: I(()) where .X = ()) { diff --git a/toolchain/parse/context.h b/toolchain/parse/context.h index 6a60db105d91..cbb05f3932e7 100644 --- a/toolchain/parse/context.h +++ b/toolchain/parse/context.h @@ -378,7 +378,7 @@ class Context { // Pushes a new state for handling a pattern. `in_var_pattern` and // `in_unused_pattern` and `in_field_shorthand_pattern` indicate whether that // pattern is nested inside a `var`, `unused` or a struct shorthand field - // pattern.. `binding_context` is the binding context that determines the + // pattern. `binding_context` is the binding context that determines the // default phase of bindings in this pattern. auto PushStateForPattern(StateKind kind, bool in_var_pattern, bool in_unused_pattern, diff --git a/toolchain/parse/handle_pattern_list.cpp b/toolchain/parse/handle_pattern_list.cpp index 2a9d0857e0a9..8b9134b6f12b 100644 --- a/toolchain/parse/handle_pattern_list.cpp +++ b/toolchain/parse/handle_pattern_list.cpp @@ -16,6 +16,18 @@ static auto HandlePatternListElement(Context& context, StateKind pattern_state, state.in_unused_pattern, state.in_field_shorthand_pattern, state.binding_context, state.ambient_precedence); + + // For patterns inside of list elements we normally check for a default value. + // Parsing a struct designator is special case as that also is looking for a + // required `=` sign, so we handle looking for an optional default value in + // that state. + if (pattern_state != StateKind::StructPatternFieldAfterDesignator) { + context.PushStateForPattern( + StateKind::PatternListElementCheckForDefaultValue, state.in_var_pattern, + state.in_unused_pattern, state.in_field_shorthand_pattern, + state.binding_context, state.ambient_precedence); + } + context.PushStateForPattern(pattern_state, state.in_var_pattern, state.in_unused_pattern, state.in_field_shorthand_pattern, @@ -150,6 +162,46 @@ auto HandleStructPatternUnderscore(Context& context) -> void { } } +auto HandlePatternListElementCheckForDefaultValue(Context& context) -> void { + auto state = context.PopState(); + + if (state.has_error) { + context.ReturnErrorOnState(); + } + + // Check for the optional `=` indicating a default value. + auto equals_token = context.ConsumeIf(Lex::TokenKind::Equal); + if (!equals_token) { + return; + } + + state.token = *equals_token; + state.kind = StateKind::PatternListElementFinishDefaultValue; + context.PushState(state); + + // Check for the underscore `_` indicating the default value is unspecified. + auto underscore = context.ConsumeIf(Lex::TokenKind::Underscore); + if (underscore) { + context.AddLeafNode(NodeKind::DefaultValueUnspecified, *underscore); + return; + } + + // No underscore, we parse this as a normal expression. + context.PushStateForExpr(state.ambient_precedence); +} + +auto HandlePatternListElementFinishDefaultValue(Context& context) -> void { + auto state = context.PopState(); + + // Propagate any expr parsing errors upwards to initiate error recovery + // at the pattern list level. + if (state.has_error) { + context.ReturnErrorOnState(); + } + + context.AddNode(NodeKind::DefaultValuePattern, state.token, state.has_error); +} + // Handles PatternListElementFinishAs(Tuple|Struct|Explicit|Implicit). static auto HandlePatternListElementFinish(Context& context, Lex::TokenKind close_token, diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 735d7c5bad8e..a94b93bd368a 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -242,6 +242,8 @@ CARBON_PARSE_NODE_KIND_PATTERN(FormBindingPattern) CARBON_PARSE_NODE_KIND_PATTERN(TemplateBindingName) CARBON_PARSE_NODE_KIND_PATTERN(CompileTimeBindingPatternTypeStart) CARBON_PARSE_NODE_KIND_PATTERN(CompileTimeBindingPattern) +CARBON_PARSE_NODE_KIND_PATTERN(DefaultValuePattern) +CARBON_PARSE_NODE_KIND_EXPRESSION(DefaultValueUnspecified) CARBON_PARSE_NODE_KIND_DECLARATION(LetIntroducer) CARBON_PARSE_NODE_KIND_DECLARATION(LetInitializer) diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index e7583162ee39..444f74f5b376 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -1003,13 +1003,15 @@ CARBON_PARSE_STATE(AliasFinish) // ... // ^ // 1. Pattern +// 2. PatternListElementCheckForDefaultValue // 2. PatternListElementFinishAs(Tuple|Struct|Explicit|Implicit) // // .foo = ... (variant is Struct) // ^ // 1. PeriodAsStruct // 2. StructPatternFieldAfterDesignator -// 3 PatternListElementFinishAsStruct +// 3. PatternListElementCheckForDefaultValue +// 4. PatternListElementFinishAsStruct // // _ ... (variant is Struct) // ^ @@ -1018,6 +1020,28 @@ CARBON_PARSE_STATE(AliasFinish) CARBON_PARSE_STATE_VARIANTS4(PatternListElement, Tuple, Struct, Explicit, Implicit) +// Some pattern list elements support optional default value expressions, +// this state checks for the `=` and if present parses it before moving on +// to finish the list element. +// +// ... = ... +// ^ +// 1. Expr +// 2. PatternListElementFinishDefaultValue +// +// ... +// ^ +// (state done) +CARBON_PARSE_STATE(PatternListElementCheckForDefaultValue) + +// After parsing the default value Expr this state attaches that Expr +// as a default value to the pattern. +// +// ... = ... +// ^ +// (state done) +CARBON_PARSE_STATE(PatternListElementFinishDefaultValue) + // Finishes parsing a pattern in a comma-separated list, including the // optional trailing `,`. If there are more patterns, enqueues another // pattern parsing state. diff --git a/toolchain/parse/testdata/function/default_values.carbon b/toolchain/parse/testdata/function/default_values.carbon new file mode 100644 index 000000000000..4575139b3ea6 --- /dev/null +++ b/toolchain/parse/testdata/function/default_values.carbon @@ -0,0 +1,102 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/function/default_values.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/function/default_values.carbon + +fn F(x: i32, y: i32 = 0); +fn G((x: i32, y: i32) = (1, 2)); +fn H(unused x: i32 = 25); +var b: auto = fn (x: i32 = 0) => x; +// Can be used in def to avoid repeating values defined in decl. +fn I(x: i32 = _); + +// CHECK:STDOUT: - filename: default_values.carbon +// CHECK:STDOUT: ╭─FileStart '' +// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' +// CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'F' +// CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x' +// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' +// CHECK:STDOUT: │ │ ├─LetBindingPattern ':' +// CHECK:STDOUT: │ │ ├─PatternListComma ',' +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'y' +// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' +// CHECK:STDOUT: │ │ │ ╭─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ ├─IntLiteral '0' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' +// CHECK:STDOUT: │ ├─ExplicitParamList ')' +// CHECK:STDOUT: ├─FunctionDecl ';' +// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' +// CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'G' +// CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' +// CHECK:STDOUT: │ │ │ ╭─TuplePatternStart '(' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'x' +// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' +// CHECK:STDOUT: │ │ │ ├─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ ├─PatternListComma ',' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'y' +// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' +// CHECK:STDOUT: │ │ │ ├─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ ╭─TuplePattern ')' +// CHECK:STDOUT: │ │ │ │ ╭─TupleLiteralStart '(' +// CHECK:STDOUT: │ │ │ │ ├─IntLiteral '1' +// CHECK:STDOUT: │ │ │ │ ├─TupleLiteralComma ',' +// CHECK:STDOUT: │ │ │ │ ├─IntLiteral '2' +// CHECK:STDOUT: │ │ │ ├─TupleLiteral ')' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' +// CHECK:STDOUT: │ ├─ExplicitParamList ')' +// CHECK:STDOUT: ├─FunctionDecl ';' +// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' +// CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'H' +// CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x' +// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' +// CHECK:STDOUT: │ │ │ ╭─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ ╭─UnusedPattern 'unused' +// CHECK:STDOUT: │ │ │ ├─IntLiteral '25' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' +// CHECK:STDOUT: │ ├─ExplicitParamList ')' +// CHECK:STDOUT: ├─FunctionDecl ';' +// CHECK:STDOUT: │ ╭─VariableIntroducer 'var' +// CHECK:STDOUT: │ │ ╭─IdentifierNameNotBeforeSignature 'b' +// CHECK:STDOUT: │ │ ├─BindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ ├─AutoTypeLiteral 'auto' +// CHECK:STDOUT: │ │ ╭─VarBindingPattern ':' +// CHECK:STDOUT: │ ├─VariablePattern 'var' +// CHECK:STDOUT: │ ├─VariableInitializer '=' +// CHECK:STDOUT: │ │ ╭─LambdaIntroducer 'fn' +// CHECK:STDOUT: │ │ │ ╭─ExplicitParamListStart '(' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'x' +// CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' +// CHECK:STDOUT: │ │ │ │ ╭─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ │ ├─IntLiteral '0' +// CHECK:STDOUT: │ │ │ ├─DefaultValuePattern '=' +// CHECK:STDOUT: │ │ ├─ExplicitParamList ')' +// CHECK:STDOUT: │ │ ├─TerseBodyArrow '=>' +// CHECK:STDOUT: │ │ ├─IdentifierNameExpr 'x' +// CHECK:STDOUT: │ ├─Lambda 'fn' +// CHECK:STDOUT: ├─VariableDecl ';' +// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' +// CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'I' +// CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x' +// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' +// CHECK:STDOUT: │ │ │ ╭─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ ├─DefaultValueUnspecified '_' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' +// CHECK:STDOUT: │ ├─ExplicitParamList ')' +// CHECK:STDOUT: ├─FunctionDecl ';' +// CHECK:STDOUT: ├─FileEnd '' +// CHECK:STDOUT: (root) diff --git a/toolchain/parse/testdata/struct/struct_pattern.carbon b/toolchain/parse/testdata/struct/struct_pattern.carbon index a7bf593777e2..e9c05666ce29 100644 --- a/toolchain/parse/testdata/struct/struct_pattern.carbon +++ b/toolchain/parse/testdata/struct/struct_pattern.carbon @@ -186,9 +186,9 @@ let {.x = x: i32 foo, .y = y: i32} = {}; // --- fail_struct_pattern_extra_token_start.carbon -// CHECK:STDERR: fail_struct_pattern_extra_token_start.carbon:[[@LINE+4]]:26: error: expected `,` or `}` [UnexpectedTokenAfterListElement] +// CHECK:STDERR: fail_struct_pattern_extra_token_start.carbon:[[@LINE+4]]:29: error: expected `,` or `}` [UnexpectedTokenAfterListElement] // CHECK:STDERR: let {.x = x: i32, foo .y = y: i32} = {}; -// CHECK:STDERR: ^ +// CHECK:STDERR: ^ // CHECK:STDERR: let {.x = x: i32, foo .y = y: i32} = {}; @@ -248,9 +248,9 @@ let {.x foo x: (), .y = y: ()} = {}; // --- fail_struct_pattern_invalid_field.carbon -// CHECK:STDERR: fail_struct_pattern_invalid_field.carbon:[[@LINE+4]]:12: error: expected `,` or `}` [UnexpectedTokenAfterListElement] +// CHECK:STDERR: fail_struct_pattern_invalid_field.carbon:[[@LINE+4]]:14: error: expected expression [ExpectedExpr] // CHECK:STDERR: let {"foo" = , .x = x:()} = {}; -// CHECK:STDERR: ^ +// CHECK:STDERR: ^ // CHECK:STDERR: let {"foo" = , .x = x:()} = {}; @@ -566,7 +566,9 @@ let {var _: i32} = {}; // CHECK:STDOUT: ╭─FileStart '' // CHECK:STDOUT: │ ╭─LetIntroducer 'let' // CHECK:STDOUT: │ │ ╭─StructPatternStart '{' -// CHECK:STDOUT: │ │ ├─InvalidParse '=' has_error +// CHECK:STDOUT: │ │ │ ╭─InvalidParse '=' has_error +// CHECK:STDOUT: │ │ │ ├─IdentifierNameExpr 'y' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─StructPattern '}' has_error // CHECK:STDOUT: │ ├─LetInitializer '=' // CHECK:STDOUT: │ │ ╭─StructLiteralStart '{' @@ -829,9 +831,11 @@ let {var _: i32} = {}; // CHECK:STDOUT: │ │ │ ├─LetBindingPattern ':' // CHECK:STDOUT: │ │ ├─StructPatternDesignatedField '=' // CHECK:STDOUT: │ │ ├─PatternListComma ',' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameExpr 'foo' -// CHECK:STDOUT: │ │ │ ├─IdentifierNameNotBeforeSignature 'y' -// CHECK:STDOUT: │ │ ├─MemberAccessExpr '.' +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameExpr 'foo' +// CHECK:STDOUT: │ │ │ ├─IdentifierNameNotBeforeSignature 'y' +// CHECK:STDOUT: │ │ │ ╭─MemberAccessExpr '.' +// CHECK:STDOUT: │ │ │ ├─IdentifierNameExpr 'y' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' // CHECK:STDOUT: │ ├─StructPattern '}' has_error // CHECK:STDOUT: │ ├─LetInitializer '=' // CHECK:STDOUT: │ │ ╭─StructLiteralStart '{' @@ -954,7 +958,9 @@ let {var _: i32} = {}; // CHECK:STDOUT: ╭─FileStart '' // CHECK:STDOUT: │ ╭─LetIntroducer 'let' // CHECK:STDOUT: │ │ ╭─StructPatternStart '{' -// CHECK:STDOUT: │ │ ├─StringLiteral '"foo"' +// CHECK:STDOUT: │ │ │ ╭─StringLiteral '"foo"' +// CHECK:STDOUT: │ │ │ ├─InvalidParse ',' has_error +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ │ ├─PatternListComma ',' // CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x' // CHECK:STDOUT: │ │ │ ╭─StructFieldDesignator '.' diff --git a/toolchain/parse/testdata/where_expr/fail_rewrite.carbon b/toolchain/parse/testdata/where_expr/fail_rewrite.carbon index 0d57f9eff8c8..90db37d1dbcb 100644 --- a/toolchain/parse/testdata/where_expr/fail_rewrite.carbon +++ b/toolchain/parse/testdata/where_expr/fail_rewrite.carbon @@ -72,13 +72,16 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' // CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'NotDesignator' // CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'T' -// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' -// CHECK:STDOUT: │ │ │ │ ╭─TypeTypeLiteral 'type' -// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' -// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' -// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error -// CHECK:STDOUT: │ │ ├─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'T' +// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ╭─TypeTypeLiteral 'type' +// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' +// CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' +// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error +// CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'U' +// CHECK:STDOUT: │ │ │ ├─DesignatorExpr '.' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error // CHECK:STDOUT: ├─FunctionDecl ';' // CHECK:STDOUT: ├─FileEnd '' @@ -88,16 +91,18 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' // CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'DesignatorInParens' // CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'V' -// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' -// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'I' -// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' -// CHECK:STDOUT: │ │ │ │ │ ╭─ParenExprStart '(' -// CHECK:STDOUT: │ │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'J' -// CHECK:STDOUT: │ │ │ │ │ ├─DesignatorExpr '.' -// CHECK:STDOUT: │ │ │ │ ├─ParenExpr ')' -// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error -// CHECK:STDOUT: │ │ ├─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'V' +// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'I' +// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' +// CHECK:STDOUT: │ │ │ │ │ ╭─ParenExprStart '(' +// CHECK:STDOUT: │ │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'J' +// CHECK:STDOUT: │ │ │ │ │ ├─DesignatorExpr '.' +// CHECK:STDOUT: │ │ │ │ ├─ParenExpr ')' +// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error +// CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─BoolTypeLiteral 'bool' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error // CHECK:STDOUT: ├─FunctionDecl ';' // CHECK:STDOUT: ├─FileEnd '' @@ -107,14 +112,16 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' // CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'DotSelf' // CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'W' -// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' -// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'K' -// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' -// CHECK:STDOUT: │ │ │ │ │ ╭─SelfTypeName 'Self' -// CHECK:STDOUT: │ │ │ │ ├─DesignatorExpr '.' -// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error -// CHECK:STDOUT: │ │ ├─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'W' +// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'K' +// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' +// CHECK:STDOUT: │ │ │ │ │ ╭─SelfTypeName 'Self' +// CHECK:STDOUT: │ │ │ │ ├─DesignatorExpr '.' +// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error +// CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─FloatTypeLiteral 'f32' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error // CHECK:STDOUT: ├─FunctionDecl ';' // CHECK:STDOUT: ├─FileEnd '' @@ -124,14 +131,16 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' // CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'DotKeyword' // CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'W' -// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' -// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'K' -// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' -// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'and' has_error -// CHECK:STDOUT: │ │ │ │ ├─DesignatorExpr '.' has_error -// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error -// CHECK:STDOUT: │ │ ├─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'W' +// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'K' +// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' +// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'and' has_error +// CHECK:STDOUT: │ │ │ │ ├─DesignatorExpr '.' has_error +// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error +// CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─UnsignedIntTypeLiteral 'u8' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error // CHECK:STDOUT: ├─FunctionDecl ';' // CHECK:STDOUT: ├─FileEnd '' @@ -141,15 +150,18 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' // CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'PostfixAfterDesignator' // CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'X' -// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' -// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'L' -// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' -// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'M' -// CHECK:STDOUT: │ │ │ │ │ ╭─DesignatorExpr '.' -// CHECK:STDOUT: │ │ │ │ ├─PostfixOperatorStar '*' -// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error -// CHECK:STDOUT: │ │ ├─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'X' +// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'L' +// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' +// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'M' +// CHECK:STDOUT: │ │ │ │ │ ╭─DesignatorExpr '.' +// CHECK:STDOUT: │ │ │ │ ├─PostfixOperatorStar '*' +// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error +// CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ │ ╭─UnsignedIntTypeLiteral 'u64' +// CHECK:STDOUT: │ │ │ ├─PostfixOperatorStar '*' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error // CHECK:STDOUT: ├─FunctionDecl ';' // CHECK:STDOUT: ├─FileEnd '' @@ -159,17 +171,20 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' // CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'BinaryOpAfterDesignator' // CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'Y' -// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' -// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'N' -// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' -// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'O' -// CHECK:STDOUT: │ │ │ │ │ ╭─DesignatorExpr '.' -// CHECK:STDOUT: │ │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'P' -// CHECK:STDOUT: │ │ │ │ │ ├─DesignatorExpr '.' -// CHECK:STDOUT: │ │ │ │ ├─InfixOperatorPlus '+' -// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error -// CHECK:STDOUT: │ │ ├─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'Y' +// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'N' +// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' +// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'O' +// CHECK:STDOUT: │ │ │ │ │ ╭─DesignatorExpr '.' +// CHECK:STDOUT: │ │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'P' +// CHECK:STDOUT: │ │ │ │ │ ├─DesignatorExpr '.' +// CHECK:STDOUT: │ │ │ │ ├─InfixOperatorPlus '+' +// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error +// CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ │ ╭─StructLiteralStart '{' +// CHECK:STDOUT: │ │ │ ├─StructLiteral '}' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error // CHECK:STDOUT: ├─FunctionDecl ';' // CHECK:STDOUT: ├─FileEnd '' @@ -179,19 +194,22 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' // CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'AfterAnd' // CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'Z' -// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' -// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'Q' -// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' -// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'R' -// CHECK:STDOUT: │ │ │ │ │ ╭─DesignatorExpr '.' -// CHECK:STDOUT: │ │ │ │ │ ├─IdentifierNameExpr 'S' -// CHECK:STDOUT: │ │ │ │ ├─RequirementImpls 'impls' -// CHECK:STDOUT: │ │ │ │ ├─RequirementAnd 'and' -// CHECK:STDOUT: │ │ │ │ │ ╭─TupleLiteralStart '(' -// CHECK:STDOUT: │ │ │ │ ├─TupleLiteral ')' -// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error -// CHECK:STDOUT: │ │ ├─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'Z' +// CHECK:STDOUT: │ │ │ ├─CompileTimeBindingPatternTypeStart ':' +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameExpr 'Q' +// CHECK:STDOUT: │ │ │ │ ╭─WhereOperand 'where' +// CHECK:STDOUT: │ │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'R' +// CHECK:STDOUT: │ │ │ │ │ ╭─DesignatorExpr '.' +// CHECK:STDOUT: │ │ │ │ │ ├─IdentifierNameExpr 'S' +// CHECK:STDOUT: │ │ │ │ ├─RequirementImpls 'impls' +// CHECK:STDOUT: │ │ │ │ ├─RequirementAnd 'and' +// CHECK:STDOUT: │ │ │ │ │ ╭─TupleLiteralStart '(' +// CHECK:STDOUT: │ │ │ │ ├─TupleLiteral ')' +// CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error +// CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'A' +// CHECK:STDOUT: │ │ │ ├─DesignatorExpr '.' +// CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error // CHECK:STDOUT: ├─FunctionDecl ';' // CHECK:STDOUT: ├─FileEnd '' diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 7f1c9a34ba37..a4dd10877707 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -43,7 +43,7 @@ struct LeafNode { // // Each of these types should start with a `static constexpr Kind` member // initialized by calling `Define` on the corresponding `NodeKind`, and passing -// in the `NodeCategory` of that kind. This will both associate the category +// in the `NodeCategory` of that kind. This will both associate the category // with the node kind and create the necessary kind object for the typed node. // // This should be followed by field declarations that describe the child nodes, @@ -373,6 +373,20 @@ struct UnusedPattern { AnyPatternId inner; }; +using DefaultValueUnspecified = + LeafNode; + +// A pattern with a default value specified: `pattern = expr`. +struct DefaultValuePattern { + static constexpr auto Kind = NodeKind::DefaultValuePattern.Define( + {.category = NodeCategory::Pattern, .child_count = 2}); + + AnyPatternId pattern; + Lex::EqualTokenIndex token; + AnyExprId default_value_expr; +}; + // A ref binding name: `ref name`. struct RefBindingName { static constexpr auto Kind = @@ -1520,7 +1534,7 @@ struct StructPatternDesignatedField { using StructPatternFieldId = NodeIdOneOf; + UnderscoreName, DefaultValuePattern>; struct StructPattern { static constexpr auto Kind = NodeKind::StructPattern.Define(