From 2952ec9c10567f3e255dc69871fbe21e7002e4be Mon Sep 17 00:00:00 2001 From: Lucile Rose Nihlen Date: Fri, 4 Sep 2026 01:22:16 +0000 Subject: [PATCH] Fix a crash when checking a nested tuple-pattern (#7716) Adds a virtual node to `DefaultValuePattern` to end the `ExprRegionForPattern` before checking the expression for the default value. When checking the default value expression, the context was still configured to interpret expressions as patterns, which caused some corruption of state with tuple-pattern subpatterns. Corrects an assertion failure I found while working on feedback from #7665. --- toolchain/check/handle_pattern_list.cpp | 11 +++ toolchain/check/node_stack.h | 1 + .../declaration/default_values.carbon | 97 +++++++++++++++++++ toolchain/lex/token_kind.def | 3 +- toolchain/parse/handle_pattern_list.cpp | 4 + toolchain/parse/node_kind.def | 1 + .../testdata/function/default_values.carbon | 5 + .../testdata/struct/struct_pattern.carbon | 3 + .../testdata/where_expr/fail_rewrite.carbon | 7 ++ toolchain/parse/typed_nodes.h | 11 ++- 10 files changed, 141 insertions(+), 2 deletions(-) diff --git a/toolchain/check/handle_pattern_list.cpp b/toolchain/check/handle_pattern_list.cpp index ab9b3f008c8b..0be2f7b5bc7f 100644 --- a/toolchain/check/handle_pattern_list.cpp +++ b/toolchain/check/handle_pattern_list.cpp @@ -157,6 +157,14 @@ auto HandleParseNode(Context& context, Parse::DefaultValueUnspecifiedId node_id) return context.TODO(node_id, "pattern default values"); } +auto HandleParseNode(Context& context, + Parse::DefaultValueExprStartId /*node_id*/) -> bool { + // We want to check the default value expression as a normal expression, + // and not convert it into a pattern. + EndEmptyExprRegionForPattern(context); + return true; +} + auto HandleParseNode(Context& context, Parse::DefaultValuePatternId node_id) -> bool { // On entry, the top of the node stack should have an expression for the @@ -205,6 +213,9 @@ auto HandleParseNode(Context& context, Parse::DefaultValuePatternId node_id) .default_value_id = default_value_id}); context.node_stack().Push(node_id, default_value_inst_id); + // We turned off expr region for pattern checking while parsing the default + // value expression, so turn it back on again for further pattern checking. + BeginExprRegionForPattern(context); return true; } diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index e1e065691d82..7210f9d04f69 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -498,6 +498,7 @@ class NodeStack { case Parse::NodeKind::ContinueStatementStart: case Parse::NodeKind::CorePackageName: case Parse::NodeKind::CppPackageName: + case Parse::NodeKind::DefaultValueExprStart: case Parse::NodeKind::ExportIntroducer: case Parse::NodeKind::FileEnd: case Parse::NodeKind::FileStart: diff --git a/toolchain/check/testdata/function/declaration/default_values.carbon b/toolchain/check/testdata/function/declaration/default_values.carbon index 8ec9f4a85605..a277684f419f 100644 --- a/toolchain/check/testdata/function/declaration/default_values.carbon +++ b/toolchain/check/testdata/function/declaration/default_values.carbon @@ -64,6 +64,13 @@ library "[[@TEST_NAME]]"; // CHECK:STDERR: fn Z(v: i32, x: i32 = 3, (y: i32 = 2, z: i32), w: i32 = 4, k: i32); +// --- nested.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F(a: i32, (b: i32, (c: i32, (d: i32, e: i32))) = (1, (2, (3, 4)))); +//@dump-sem-ir-end + // --- basic.carbon library "[[@TEST_NAME]]"; @@ -72,6 +79,96 @@ library "[[@TEST_NAME]]"; fn F(x: i32 = 0); //@dump-sem-ir-end +// CHECK:STDOUT: --- nested.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %pattern_type.6b6: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %a.param_patt: %pattern_type.6b6 = value_param_pattern [concrete] +// CHECK:STDOUT: %a.patt: %pattern_type.6b6 = wrapper_binding_pattern a, %a.param_patt [concrete] +// CHECK:STDOUT: %b.param_patt: %pattern_type.6b6 = value_param_pattern [concrete] +// CHECK:STDOUT: %b.patt: %pattern_type.6b6 = wrapper_binding_pattern b, %b.param_patt [concrete] +// CHECK:STDOUT: %c.param_patt: %pattern_type.6b6 = value_param_pattern [concrete] +// CHECK:STDOUT: %c.patt: %pattern_type.6b6 = wrapper_binding_pattern c, %c.param_patt [concrete] +// CHECK:STDOUT: %d.param_patt: %pattern_type.6b6 = value_param_pattern [concrete] +// CHECK:STDOUT: %d.patt: %pattern_type.6b6 = wrapper_binding_pattern d, %d.param_patt [concrete] +// CHECK:STDOUT: %e.param_patt: %pattern_type.6b6 = value_param_pattern [concrete] +// CHECK:STDOUT: %e.patt: %pattern_type.6b6 = wrapper_binding_pattern e, %e.param_patt [concrete] +// CHECK:STDOUT: %tuple.type.e55: type = tuple_type (%i32, %i32) [concrete] +// CHECK:STDOUT: %pattern_type.394: type = pattern_type %tuple.type.e55 [concrete] +// CHECK:STDOUT: %.70d: %pattern_type.394 = tuple_pattern (%d.patt, %e.patt) [concrete] +// CHECK:STDOUT: %tuple.type.920: type = tuple_type (%i32, %tuple.type.e55) [concrete] +// CHECK:STDOUT: %pattern_type.e87: type = pattern_type %tuple.type.920 [concrete] +// CHECK:STDOUT: %.aa9: %pattern_type.e87 = tuple_pattern (%c.patt, %.70d) [concrete] +// CHECK:STDOUT: %tuple.type.007: type = tuple_type (%i32, %tuple.type.920) [concrete] +// CHECK:STDOUT: %pattern_type.802: type = pattern_type %tuple.type.007 [concrete] +// CHECK:STDOUT: %.203: %pattern_type.802 = tuple_pattern (%b.patt, %.aa9) [concrete] +// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] +// CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] +// CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] +// CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] +// CHECK:STDOUT: %tuple.type.f94: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete] +// CHECK:STDOUT: %tuple.302: %tuple.type.f94 = tuple_value (%int_3.1ba, %int_4.0c1) [concrete] +// CHECK:STDOUT: %tuple.type.bd0: type = tuple_type (Core.IntLiteral, %tuple.type.f94) [concrete] +// CHECK:STDOUT: %tuple.b26: %tuple.type.bd0 = tuple_value (%int_2.ecc, %tuple.302) [concrete] +// CHECK:STDOUT: %tuple.type.c4f: type = tuple_type (Core.IntLiteral, %tuple.type.bd0) [concrete] +// CHECK:STDOUT: %tuple.326: %tuple.type.c4f = tuple_value (%int_1.5b8, %tuple.b26) [concrete] +// CHECK:STDOUT: %.19b: %pattern_type.802 = default_value_pattern %.203, index: 0 [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %a.param_patt: %pattern_type.6b6 = value_param_pattern [concrete = constants.%a.param_patt] +// CHECK:STDOUT: %a.patt: %pattern_type.6b6 = wrapper_binding_pattern a, %a.param_patt [concrete = constants.%a.patt] +// CHECK:STDOUT: %b.param_patt: %pattern_type.6b6 = value_param_pattern [concrete = constants.%b.param_patt] +// CHECK:STDOUT: %b.patt: %pattern_type.6b6 = wrapper_binding_pattern b, %b.param_patt [concrete = constants.%b.patt] +// CHECK:STDOUT: %c.param_patt: %pattern_type.6b6 = value_param_pattern [concrete = constants.%c.param_patt] +// CHECK:STDOUT: %c.patt: %pattern_type.6b6 = wrapper_binding_pattern c, %c.param_patt [concrete = constants.%c.patt] +// CHECK:STDOUT: %d.param_patt: %pattern_type.6b6 = value_param_pattern [concrete = constants.%d.param_patt] +// CHECK:STDOUT: %d.patt: %pattern_type.6b6 = wrapper_binding_pattern d, %d.param_patt [concrete = constants.%d.patt] +// CHECK:STDOUT: %e.param_patt: %pattern_type.6b6 = value_param_pattern [concrete = constants.%e.param_patt] +// CHECK:STDOUT: %e.patt: %pattern_type.6b6 = wrapper_binding_pattern e, %e.param_patt [concrete = constants.%e.patt] +// CHECK:STDOUT: %.loc4_47: %pattern_type.394 = tuple_pattern (%d.patt, %e.patt) [concrete = constants.%.70d] +// CHECK:STDOUT: %.loc4_48: %pattern_type.e87 = tuple_pattern (%c.patt, %.loc4_47) [concrete = constants.%.aa9] +// CHECK:STDOUT: %.loc4_49: %pattern_type.802 = tuple_pattern (%b.patt, %.loc4_48) [concrete = constants.%.203] +// CHECK:STDOUT: %.loc4_51: %pattern_type.802 = default_value_pattern %.loc4_49, index: 0 [concrete = constants.%.19b] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] +// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] +// CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] +// CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] +// CHECK:STDOUT: %.loc4_66: %tuple.type.f94 = tuple_literal (%int_3, %int_4) [concrete = constants.%tuple.302] +// CHECK:STDOUT: %.loc4_67: %tuple.type.bd0 = tuple_literal (%int_2, %.loc4_66) [concrete = constants.%tuple.b26] +// CHECK:STDOUT: %.loc4_68: %tuple.type.c4f = tuple_literal (%int_1, %.loc4_67) [concrete = constants.%tuple.326] +// CHECK:STDOUT: %a.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %i32.loc4_9: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %a: %i32 = wrapper_binding a, %a.param +// CHECK:STDOUT: %b.param: %i32 = value_param call_param1 +// CHECK:STDOUT: %i32.loc4_18: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %b: %i32 = wrapper_binding b, %b.param +// CHECK:STDOUT: %c.param: %i32 = value_param call_param2 +// CHECK:STDOUT: %i32.loc4_27: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %c: %i32 = wrapper_binding c, %c.param +// CHECK:STDOUT: %d.param: %i32 = value_param call_param3 +// CHECK:STDOUT: %i32.loc4_36: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %d: %i32 = wrapper_binding d, %d.param +// CHECK:STDOUT: %e.param: %i32 = value_param call_param4 +// CHECK:STDOUT: %i32.loc4_44: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %e: %i32 = wrapper_binding e, %e.param +// CHECK:STDOUT: %tuple.loc4_47: %tuple.type.e55 = tuple_value (%d.param, %e.param) +// CHECK:STDOUT: %tuple.loc4_48: %tuple.type.920 = tuple_value (%c.param, %tuple.loc4_47) +// CHECK:STDOUT: %tuple.loc4_49: %tuple.type.007 = tuple_value (%b.param, %tuple.loc4_48) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%a.param: %i32, %b.param: %i32, %c.param: %i32, %d.param: %i32, %e.param: %i32) default_values: { +// CHECK:STDOUT: constants.%tuple.326: %tuple.type.c4f = tuple_value (constants.%int_1.5b8, constants.%tuple.b26) [concrete] +// CHECK:STDOUT: }; +// CHECK:STDOUT: // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/lex/token_kind.def b/toolchain/lex/token_kind.def index bd5b90b2f4e8..29cf3eb19843 100644 --- a/toolchain/lex/token_kind.def +++ b/toolchain/lex/token_kind.def @@ -92,7 +92,8 @@ CARBON_SYMBOL_TOKEN(Backslash, "\\") CARBON_SYMBOL_TOKEN(Caret, "^") CARBON_TOKEN_WITH_VIRTUAL_NODE( CARBON_SYMBOL_TOKEN(Colon, ":")) -CARBON_SYMBOL_TOKEN(Equal, "=") +CARBON_TOKEN_WITH_VIRTUAL_NODE( + CARBON_SYMBOL_TOKEN(Equal, "=")) CARBON_SYMBOL_TOKEN(Exclaim, "!") CARBON_SYMBOL_TOKEN(Greater, ">") CARBON_SYMBOL_TOKEN(Less, "<") diff --git a/toolchain/parse/handle_pattern_list.cpp b/toolchain/parse/handle_pattern_list.cpp index 8b9134b6f12b..bdf378af81d1 100644 --- a/toolchain/parse/handle_pattern_list.cpp +++ b/toolchain/parse/handle_pattern_list.cpp @@ -175,6 +175,10 @@ auto HandlePatternListElementCheckForDefaultValue(Context& context) -> void { return; } + // Add the first virtual node surrounding the value expr node, to facilitate + // handling in check. + context.AddLeafNode(NodeKind::DefaultValueExprStart, *equals_token); + state.token = *equals_token; state.kind = StateKind::PatternListElementFinishDefaultValue; context.PushState(state); diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index e03c1eb070a7..ab645b46b918 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -242,6 +242,7 @@ 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(DefaultValueExprStart) CARBON_PARSE_NODE_KIND_PATTERN(DefaultValuePattern) CARBON_PARSE_NODE_KIND_EXPRESSION(DefaultValueUnspecified) diff --git a/toolchain/parse/testdata/function/default_values.carbon b/toolchain/parse/testdata/function/default_values.carbon index 4575139b3ea6..05de54f497d6 100644 --- a/toolchain/parse/testdata/function/default_values.carbon +++ b/toolchain/parse/testdata/function/default_values.carbon @@ -29,6 +29,7 @@ fn I(x: i32 = _); // CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' // CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' // CHECK:STDOUT: │ │ │ ╭─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─IntLiteral '0' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' // CHECK:STDOUT: │ ├─ExplicitParamList ')' @@ -47,6 +48,7 @@ fn I(x: i32 = _); // CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' // CHECK:STDOUT: │ │ │ ├─LetBindingPattern ':' // CHECK:STDOUT: │ │ │ ╭─TuplePattern ')' +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ │ ╭─TupleLiteralStart '(' // CHECK:STDOUT: │ │ │ │ ├─IntLiteral '1' // CHECK:STDOUT: │ │ │ │ ├─TupleLiteralComma ',' @@ -63,6 +65,7 @@ fn I(x: i32 = _); // CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' // CHECK:STDOUT: │ │ │ ╭─LetBindingPattern ':' // CHECK:STDOUT: │ │ │ ╭─UnusedPattern 'unused' +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─IntLiteral '25' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' // CHECK:STDOUT: │ ├─ExplicitParamList ')' @@ -80,6 +83,7 @@ fn I(x: i32 = _); // CHECK:STDOUT: │ │ │ │ ├─BindingPatternTypeStart ':' // CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' // CHECK:STDOUT: │ │ │ │ ╭─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ │ ├─IntLiteral '0' // CHECK:STDOUT: │ │ │ ├─DefaultValuePattern '=' // CHECK:STDOUT: │ │ ├─ExplicitParamList ')' @@ -94,6 +98,7 @@ fn I(x: i32 = _); // CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' // CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' // CHECK:STDOUT: │ │ │ ╭─LetBindingPattern ':' +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─DefaultValueUnspecified '_' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' // CHECK:STDOUT: │ ├─ExplicitParamList ')' diff --git a/toolchain/parse/testdata/struct/struct_pattern.carbon b/toolchain/parse/testdata/struct/struct_pattern.carbon index e9c05666ce29..1a5584593895 100644 --- a/toolchain/parse/testdata/struct/struct_pattern.carbon +++ b/toolchain/parse/testdata/struct/struct_pattern.carbon @@ -567,6 +567,7 @@ let {var _: i32} = {}; // CHECK:STDOUT: │ ╭─LetIntroducer 'let' // CHECK:STDOUT: │ │ ╭─StructPatternStart '{' // CHECK:STDOUT: │ │ │ ╭─InvalidParse '=' has_error +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─IdentifierNameExpr 'y' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─StructPattern '}' has_error @@ -834,6 +835,7 @@ let {var _: i32} = {}; // CHECK:STDOUT: │ │ │ ╭─IdentifierNameExpr 'foo' // CHECK:STDOUT: │ │ │ ├─IdentifierNameNotBeforeSignature 'y' // CHECK:STDOUT: │ │ │ ╭─MemberAccessExpr '.' +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─IdentifierNameExpr 'y' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' // CHECK:STDOUT: │ ├─StructPattern '}' has_error @@ -959,6 +961,7 @@ let {var _: i32} = {}; // CHECK:STDOUT: │ ╭─LetIntroducer 'let' // CHECK:STDOUT: │ │ ╭─StructPatternStart '{' // CHECK:STDOUT: │ │ │ ╭─StringLiteral '"foo"' +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─InvalidParse ',' has_error // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ │ ├─PatternListComma ',' diff --git a/toolchain/parse/testdata/where_expr/fail_rewrite.carbon b/toolchain/parse/testdata/where_expr/fail_rewrite.carbon index 90db37d1dbcb..55530e3ee832 100644 --- a/toolchain/parse/testdata/where_expr/fail_rewrite.carbon +++ b/toolchain/parse/testdata/where_expr/fail_rewrite.carbon @@ -79,6 +79,7 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' // CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error // CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'U' // CHECK:STDOUT: │ │ │ ├─DesignatorExpr '.' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error @@ -101,6 +102,7 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ │ │ │ ├─ParenExpr ')' // CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error // CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─BoolTypeLiteral 'bool' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error @@ -120,6 +122,7 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ │ │ │ ├─DesignatorExpr '.' // CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error // CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─FloatTypeLiteral 'f32' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error @@ -139,6 +142,7 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ │ │ │ ├─DesignatorExpr '.' has_error // CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error // CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ ├─UnsignedIntTypeLiteral 'u8' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error // CHECK:STDOUT: │ ├─ExplicitParamList ')' has_error @@ -159,6 +163,7 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ │ │ │ ├─PostfixOperatorStar '*' // CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error // CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ │ ╭─UnsignedIntTypeLiteral 'u64' // CHECK:STDOUT: │ │ │ ├─PostfixOperatorStar '*' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error @@ -182,6 +187,7 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ │ │ │ ├─InfixOperatorPlus '+' // CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error // CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ │ ╭─StructLiteralStart '{' // CHECK:STDOUT: │ │ │ ├─StructLiteral '}' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error @@ -207,6 +213,7 @@ fn AfterAnd(generic Z: Q where .R impls S and () = .A); // CHECK:STDOUT: │ │ │ │ ├─TupleLiteral ')' // CHECK:STDOUT: │ │ │ ├─WhereExpr 'where' has_error // CHECK:STDOUT: │ │ │ ╭─CompileTimeBindingPattern ':' has_error +// CHECK:STDOUT: │ │ │ ├─DefaultValueExprStart '=' // CHECK:STDOUT: │ │ │ │ ╭─IdentifierNameNotBeforeSignature 'A' // CHECK:STDOUT: │ │ │ ├─DesignatorExpr '.' // CHECK:STDOUT: │ │ ├─DefaultValuePattern '=' has_error diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 09762792a911..39c655367802 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -377,13 +377,22 @@ using DefaultValueUnspecified = LeafNode; +struct DefaultValueExprStart { + static constexpr auto Kind = + NodeKind::DefaultValueExprStart.Define({.child_count = 0}); + // This is a virtual token. The `=` token is owned by the + // DefaultValuePattern node. + Lex::EqualTokenIndex token; +}; + // A pattern with a default value specified: `pattern = expr`. struct DefaultValuePattern { static constexpr auto Kind = NodeKind::DefaultValuePattern.Define( - {.category = NodeCategory::Pattern, .child_count = 2}); + {.category = NodeCategory::Pattern, .child_count = 3}); AnyPatternId pattern; Lex::EqualTokenIndex token; + DefaultValueExprStartId start; AnyExprId default_value_expr; };