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.
This commit is contained in:
Lucile Rose Nihlen
2026-09-04 01:22:16 +00:00
committed by GitHub
parent 1b969c292b
commit 2952ec9c10
10 changed files with 141 additions and 2 deletions
+11
View File
@@ -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;
}
+1
View File
@@ -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:
@@ -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: <elided>
// 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 {
+2 -1
View File
@@ -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, "<")
+4
View File
@@ -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);
+1
View File
@@ -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)
@@ -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 ')'
+3
View File
@@ -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 ','
@@ -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
+10 -1
View File
@@ -377,13 +377,22 @@ using DefaultValueUnspecified =
LeafNode<NodeKind::DefaultValueUnspecified, Lex::UnderscoreTokenIndex,
NodeCategory::Expr>;
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;
};