diff --git a/docs/design/expressions/README.md b/docs/design/expressions/README.md index 75858c5d6c08..221c616a06d5 100644 --- a/docs/design/expressions/README.md +++ b/docs/design/expressions/README.md @@ -145,6 +145,8 @@ graph BT logicalExpression((" ")) + ref["ref x"] + if>"if x then y else z"] click if "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/if.md" @@ -187,7 +189,8 @@ graph BT and & or --> logicalOperand logicalExpression --> as & where & and & or - if & expressionStatement --> logicalExpression + ref & expressionStatement --> logicalExpression + if ---> ref insideParens & assignment --> if ``` diff --git a/toolchain/check/handle_call_expr.cpp b/toolchain/check/handle_call_expr.cpp index 9ce520abbf4a..fedd664ed300 100644 --- a/toolchain/check/handle_call_expr.cpp +++ b/toolchain/check/handle_call_expr.cpp @@ -33,18 +33,4 @@ auto HandleParseNode(Context& context, Parse::CallExprId node_id) -> bool { return true; } -auto HandleParseNode(Context& context, Parse::RefTagId node_id) -> bool { - auto expr_id = context.node_stack().Peek(); - - if (SemIR::GetExprCategory(context.sem_ir(), expr_id) != - SemIR::ExprCategory::DurableRef) { - CARBON_DIAGNOSTIC( - RefTagNotDurableRef, Error, - "expression tagged with `ref` is not a durable reference"); - context.emitter().Emit(node_id, RefTagNotDurableRef); - } - context.ref_tags().Insert(expr_id, Context::RefTag::Present); - return true; -} - } // namespace Carbon::Check diff --git a/toolchain/check/handle_operator.cpp b/toolchain/check/handle_operator.cpp index 251f59bdb00c..328356b10b90 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -357,6 +357,21 @@ auto HandleParseNode(Context& context, Parse::PrefixOperatorPlusPlusId node_id) return HandleUnaryOperator(context, node_id, CoreIdentifier::Inc); } +auto HandleParseNode(Context& context, Parse::PrefixOperatorRefId node_id) + -> bool { + auto expr_id = context.node_stack().Peek(); + + if (SemIR::GetExprCategory(context.sem_ir(), expr_id) != + SemIR::ExprCategory::DurableRef) { + CARBON_DIAGNOSTIC( + RefTagNotDurableRef, Error, + "expression tagged with `ref` is not a durable reference"); + context.emitter().Emit(node_id, RefTagNotDurableRef); + } + context.ref_tags().Insert(expr_id, Context::RefTag::Present); + return true; +} + auto HandleParseNode(Context& context, Parse::PrefixOperatorStarId node_id) -> bool { auto base_id = context.node_stack().PopExpr(); diff --git a/toolchain/check/testdata/operators/builtin/ref.carbon b/toolchain/check/testdata/operators/builtin/ref.carbon new file mode 100644 index 000000000000..0e8d08e4179c --- /dev/null +++ b/toolchain/check/testdata/operators/builtin/ref.carbon @@ -0,0 +1,43 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon +// EXTRA-ARGS: --dump-sem-ir-ranges=only +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/operators/builtin/ref.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/operators/builtin/ref.carbon + +// --- fail_discarded_ref.carbon + +library "[[@TEST_NAME]]"; + +fn F() { + var x: () = (); + // CHECK:STDERR: fail_discarded_ref.carbon:[[@LINE+4]]:7: error: `ref` tag is not an argument to a `ref` parameter [RefTagNoRefParam] + // CHECK:STDERR: ref x; + // CHECK:STDERR: ^ + // CHECK:STDERR: + ref x; +} + +// --- fail_ref_out_of_place.carbon + +library "[[@TEST_NAME]]"; + +fn F() { + var x: () = (); + // CHECK:STDERR: fail_ref_out_of_place.carbon:[[@LINE+4]]:19: error: `ref` tag is not an argument to a `ref` parameter [RefTagNoRefParam] + // CHECK:STDERR: let y: () = ref x; + // CHECK:STDERR: ^ + // CHECK:STDERR: + let y: () = ref x; + // CHECK:STDERR: fail_ref_out_of_place.carbon:[[@LINE+4]]:20: error: `ref` tag is not an argument to a `ref` parameter [RefTagNoRefParam] + // CHECK:STDERR: let z: () = (ref x); + // CHECK:STDERR: ^ + // CHECK:STDERR: + let z: () = (ref x); +} diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 03e2e3103e58..bf55773154c4 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -119,7 +119,6 @@ CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresWhitespace) CARBON_DIAGNOSTIC_KIND(UnexpectedTokenAfterListElement) CARBON_DIAGNOSTIC_KIND(UnrecognizedDecl) CARBON_DIAGNOSTIC_KIND(UnexpectedTokenInCompoundMemberAccess) -CARBON_DIAGNOSTIC_KIND(UnexpectedRef) // Match diagnostics. CARBON_DIAGNOSTIC_KIND(ExpectedMatchCaseArrow) diff --git a/toolchain/parse/handle_brace_expr.cpp b/toolchain/parse/handle_brace_expr.cpp index f44114a3fb8d..205b74ca893a 100644 --- a/toolchain/parse/handle_brace_expr.cpp +++ b/toolchain/parse/handle_brace_expr.cpp @@ -133,11 +133,6 @@ static auto HandleBraceExprParamAfterDesignator(Context& context, // that one has a `:` separator and the other has an `=` separator. state.token = context.Consume(); context.PushState(state, param_finish_kind); - if (param_finish_kind == StateKind::BraceExprParamFinishAsValue && - context.PositionIs(Lex::TokenKind::Ref)) { - context.PushState(StateKind::RefTagFinishAsRegular); - context.ConsumeChecked(Lex::TokenKind::Ref); - } context.PushState(StateKind::Expr); } diff --git a/toolchain/parse/handle_paren_expr.cpp b/toolchain/parse/handle_paren_expr.cpp index 38ed89643251..b3cef4d87a78 100644 --- a/toolchain/parse/handle_paren_expr.cpp +++ b/toolchain/parse/handle_paren_expr.cpp @@ -43,34 +43,6 @@ auto HandleOnlyParenExprFinish(Context& context) -> void { FinishParenExpr(context, state); } -static auto HandleRefTagFinish(Context& context, StateKind state_kind) -> void { - auto state = context.PopState(); - if (state_kind == StateKind::RefTagFinishAsAfterOpenParen && - !context.PositionIs(Lex::TokenKind::Comma)) { - CARBON_DIAGNOSTIC(UnexpectedRef, Error, - "found `ref` in unexpected position"); - context.emitter().Emit(state.token, UnexpectedRef); - } - context.AddNode(NodeKind::RefTag, state.token, state.has_error); -} - -auto HandleRefTagFinishAsRegular(Context& context) -> void { - HandleRefTagFinish(context, StateKind::RefTagFinishAsRegular); -} - -auto HandleRefTagFinishAsAfterOpenParen(Context& context) -> void { - HandleRefTagFinish(context, StateKind::RefTagFinishAsAfterOpenParen); -} - -static auto StartTupleLiteralElement(Context& context) -> void { - context.PushState(StateKind::TupleLiteralElementFinish); - if (context.PositionIs(Lex::TokenKind::Ref)) { - context.PushState(StateKind::RefTagFinishAsRegular); - context.ConsumeChecked(Lex::TokenKind::Ref); - } - context.PushState(StateKind::Expr); -} - auto HandleParenExpr(Context& context) -> void { auto state = context.PopState(); @@ -84,10 +56,6 @@ auto HandleParenExpr(Context& context) -> void { } else { context.PushState(state, StateKind::ParenExprFinish); context.PushState(StateKind::ExprAfterOpenParenFinish); - if (context.PositionIs(Lex::TokenKind::Ref)) { - context.PushState(StateKind::RefTagFinishAsAfterOpenParen); - context.ConsumeChecked(Lex::TokenKind::Ref); - } context.PushState(StateKind::Expr); } } @@ -110,7 +78,8 @@ auto HandleExprAfterOpenParenFinish(Context& context) -> void { // If the comma is not immediately followed by a close paren, push handlers // for the next tuple element. if (list_token_kind != Context::ListTokenKind::CommaClose) { - StartTupleLiteralElement(context); + context.PushState(StateKind::TupleLiteralElementFinish); + context.PushState(StateKind::Expr); } } @@ -124,7 +93,8 @@ auto HandleTupleLiteralElementFinish(Context& context) -> void { if (context.ConsumeListToken(NodeKind::TupleLiteralComma, Lex::TokenKind::CloseParen, state.has_error) == Context::ListTokenKind::Comma) { - StartTupleLiteralElement(context); + context.PushState(StateKind::TupleLiteralElementFinish); + context.PushState(StateKind::Expr); } } @@ -149,7 +119,8 @@ auto HandleCallExpr(Context& context) -> void { context.AddNode(NodeKind::CallExprStart, context.Consume(), state.has_error); if (!context.PositionIs(Lex::TokenKind::CloseParen)) { - StartTupleLiteralElement(context); + context.PushState(StateKind::TupleLiteralElementFinish); + context.PushState(StateKind::Expr); } } diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 65b4daa2de6e..10cd23e869af 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -242,8 +242,6 @@ CARBON_PARSE_NODE_KIND(PointerMemberAccessExpr) CARBON_PARSE_NODE_KIND(IntLiteral) -CARBON_PARSE_NODE_KIND(RefTag) - CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(BoolLiteralFalse, False) CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(BoolLiteralTrue, True) CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(CharLiteral, CharLiteral) @@ -268,6 +266,7 @@ CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Minus) CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(MinusMinus) CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Partial) CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(PlusPlus) +CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Ref) CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Star) CARBON_PARSE_NODE_KIND_INFIX_OPERATOR(Amp) diff --git a/toolchain/parse/precedence.cpp b/toolchain/parse/precedence.cpp index 7b6eecf49dd1..da877e7dabef 100644 --- a/toolchain/parse/precedence.cpp +++ b/toolchain/parse/precedence.cpp @@ -30,7 +30,8 @@ struct PrecedenceGroup::OperatorPriorityTable { {Additive, Modulo, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}, {Relational, Where}); MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr}); - MarkHigherThan({As, LogicalAnd, LogicalOr, Where}, {If}); + MarkHigherThan({As, LogicalAnd, LogicalOr, Where}, {Ref}); + MarkHigherThan({Ref}, {If}); MarkHigherThan({If}, {Assignment}); MarkHigherThan({Assignment, IncrementDecrement}, {Lowest}); @@ -166,6 +167,9 @@ auto PrecedenceGroup::ForLeading(Lex::TokenKind kind) case Lex::TokenKind::If: return PrecedenceGroup(If); + case Lex::TokenKind::Ref: + return PrecedenceGroup(Ref); + case Lex::TokenKind::Const: case Lex::TokenKind::Partial: return PrecedenceGroup(TypePrefix); diff --git a/toolchain/parse/precedence.h b/toolchain/parse/precedence.h index 208ca32c2fa8..13f9a7eb51d4 100644 --- a/toolchain/parse/precedence.h +++ b/toolchain/parse/precedence.h @@ -123,6 +123,8 @@ class PrecedenceGroup { LogicalOr, // Conditional. If, + // `ref` + Ref, // Assignment. Assignment, // Sentinel representing a context in which any operator can appear. diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index be16022804b5..c1534adfa553 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -125,12 +125,6 @@ CARBON_PARSE_STATE_VARIANTS3(BraceExprParam, Type, Value, Unknown) // Handles a brace expression parameter after the initial designator. This // should be at a `:` or `=`, depending on whether it's a type or value literal. // -// { .foo = ref bar ... } -// ^~~~~ -// 1. Expr -// 2. RefTagFinishAsRegular -// 3. BraceExprParamFinishAsValue -// // { .foo = bar ... } // ^ // 1. Expr @@ -173,28 +167,12 @@ CARBON_PARSE_STATE_VARIANTS3(BraceExprParamFinish, Type, Value, Unknown) // (state done) CARBON_PARSE_STATE_VARIANTS3(BraceExprFinish, Type, Value, Unknown) -// Handles the end of an argument expression tagged with `ref`. The -// `AfterOpenParen` variant diagnoses if the expression is not followed by `,`. -// -// ref ... -// ^ -// (state done) -CARBON_PARSE_STATE_VARIANTS2(RefTagFinish, Regular, AfterOpenParen) - -// Handles a call expression `(...)`, including the initial `ref`, if any. +// Handles a call expression `(...)`. // // F() // ^ // 1. CallExprFinish // -// F(ref ... -// ^~~~ -// -// 1. Expr -// 2. RefTagFinishAsRegular -// 2. TupleLiteralElementFinish -// 3. CallExprFinish -// // F( ... // ^ // 1. Expr @@ -936,22 +914,11 @@ CARBON_PARSE_STATE(OnlyParenExprFinish) // Handles the `(` of an expression that's presumed to be a parenthesized // single expression, but may later be reclassified as a tuple literal. -// This also handles a `ref` following the `(`, if present. That's only -// valid in tuple literals, but doesn't cause the expression to be a tuple -// literal. // // ( ) // ^ // 1. TupleLiteralFinish // -// (ref ... ) -// ^~~~ -// -// 1. Expr -// 2. RefTagFinishAsAfterOpenParen -// 3. ExprAfterOpenParenFinish -// 4. ParenExprFinish (SPECIAL: may be replaced) -// // ( ... ) // ^ // 1. Expr @@ -973,13 +940,6 @@ CARBON_PARSE_STATE(TupleLiteralFinish) // (state done) // SPECIAL: parent becomes TupleLiteralFinish // -// ( ... , ref ... ) -// ^~~~~ -// 1. Expr -// 2. RefTagFinishAsRegular -// 3. TupleLiteralElementFinish -// SPECIAL: parent becomes TupleLiteralFinish -// // ( ... , ... ) // ^ // 1. Expr @@ -998,12 +958,6 @@ CARBON_PARSE_STATE(ExprAfterOpenParenFinish) // ^ // (state done) // -// ( ... , ref ... ) -// ^~~~~ -// 1. Expr -// 2. RefTagFinishAsRegular -// 3. TupleLiteralElementFinish -// // ( ... , ... ) // ^ // 1. Expr @@ -1044,6 +998,9 @@ CARBON_PARSE_STATE(Pattern) // Handles the initial part of a binding pattern, enqueuing type expression // processing. // +// TODO: treat `ref` as a unary pattern operator in order to avoid ambiguity +// with `ref` in expression patterns. See issue #6342. +// // [ref] name: ... // ^~~~~ // [ref] self: ... diff --git a/toolchain/parse/testdata/function/call.carbon b/toolchain/parse/testdata/function/call.carbon index e04fb2e85d59..230f94ab6b82 100644 --- a/toolchain/parse/testdata/function/call.carbon +++ b/toolchain/parse/testdata/function/call.carbon @@ -32,18 +32,6 @@ fn F() { G(x, {.y = ref y, .z = z}); } -// --- fail_ref_out_of_place.carbon - -fn F() { - (ref x,); - G(ref x); - // CHECK:STDERR: fail_ref_out_of_place.carbon:[[@LINE+4]]:4: error: found `ref` in unexpected position [UnexpectedRef] - // CHECK:STDERR: (ref x); - // CHECK:STDERR: ^~~ - // CHECK:STDERR: - (ref x); -} - // CHECK:STDOUT: - filename: basic.carbon // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, @@ -85,13 +73,13 @@ fn F() { // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'G'}, // CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, // CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 5}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 6}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'G'}, // CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, // CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, // CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 7}, @@ -101,7 +89,7 @@ fn F() { // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, // CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, // CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 7}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 8}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 28}, @@ -121,7 +109,7 @@ fn F() { // CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, // CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, // CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, // CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 5}, // CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 10}, @@ -132,7 +120,7 @@ fn F() { // CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, // CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, // CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'z'}, // CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 6}, @@ -146,7 +134,7 @@ fn F() { // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, // CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'z'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, // CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 6}, // CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 11}, // CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 12}, @@ -158,7 +146,7 @@ fn F() { // CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'y'}, // CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, // CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, // CHECK:STDOUT: {kind: 'StructLiteralField', text: '=', subtree_size: 5}, // CHECK:STDOUT: {kind: 'StructLiteralComma', text: ','}, // CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'z'}, @@ -171,31 +159,3 @@ fn F() { // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 59}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDOUT: - filename: fail_ref_out_of_place.carbon -// CHECK:STDOUT: parse_tree: [ -// CHECK:STDOUT: {kind: 'FileStart', text: ''}, -// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, -// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'F'}, -// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, -// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'TupleLiteralComma', text: ','}, -// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 6}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'G'}, -// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 6}, -// CHECK:STDOUT: {kind: 'ParenExprStart', text: '('}, -// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'}, -// CHECK:STDOUT: {kind: 'RefTag', text: 'ref', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'ParenExpr', text: ')', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 23}, -// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, -// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/operators/ref.carbon b/toolchain/parse/testdata/operators/ref.carbon new file mode 100644 index 000000000000..86b266ebb9b9 --- /dev/null +++ b/toolchain/parse/testdata/operators/ref.carbon @@ -0,0 +1,90 @@ +// 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/operators/ref.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/operators/ref.carbon + +// --- ref_precedence.carbon + +var x: i32 = ref y as i32; + +// --- fail_if_ref.carbon + +// CHECK:STDERR: fail_if_ref.carbon:[[@LINE+4]]:18: error: parentheses are required around this unary `if` operator [UnaryOperatorRequiresParentheses] +// CHECK:STDERR: var x: i32 = ref if b then c else d; +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var x: i32 = ref if b then c else d; +var x: i32 = ref (if b then c else d); +var x: i32 = if b then ref c else ref d; + +// CHECK:STDOUT: - filename: ref_precedence.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'y'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'InfixOperatorAs', text: 'as', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_if_ref.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'b'}, +// CHECK:STDOUT: {kind: 'IfExprIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'c'}, +// CHECK:STDOUT: {kind: 'IfExprThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'd'}, +// CHECK:STDOUT: {kind: 'IfExprElse', text: 'else', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'ParenExprStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'b'}, +// CHECK:STDOUT: {kind: 'IfExprIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'c'}, +// CHECK:STDOUT: {kind: 'IfExprThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'd'}, +// CHECK:STDOUT: {kind: 'IfExprElse', text: 'else', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ParenExpr', text: ')', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'b'}, +// CHECK:STDOUT: {kind: 'IfExprIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'c'}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IfExprThen', text: 'then', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'd'}, +// CHECK:STDOUT: {kind: 'PrefixOperatorRef', text: 'ref', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IfExprElse', text: 'else', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 4896fb8907b5..32da26da4f7f 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -1006,15 +1006,6 @@ struct CallExpr { Lex::CloseParenTokenIndex token; }; -// A callsite `ref` tag: `F(ref x)` or `F({.x = ref x}) -struct RefTag { - static constexpr auto Kind = NodeKind::RefTag.Define( - {.category = NodeCategory::Expr, .child_count = 1}); - - Lex::RefTokenIndex token; - AnyExprId tagged_expr; -}; - // A member access expression: `a.b` or `a.(b)`. struct MemberAccessExpr { static constexpr auto Kind = NodeKind::MemberAccessExpr.Define(