From fc5a9541ce8b00083ffb9f00febff91c8653abaf Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 10 Aug 2023 11:39:37 -0700 Subject: [PATCH] Update precedence rules to match design. (#3081) - Only allow assignment at the top level in an expression statement. - Allow both negation and complement as subexpressions of both bitwise and numeric operators. - Remove parsing support for postincrement and postdecrement. - Add parsing support for `as` operator. - Use the same ambient precedence for types and non-type expressions. --------- Co-authored-by: Chandler Carruth --- toolchain/diagnostics/diagnostic_kind.def | 1 + toolchain/parser/parser_handle_expression.cpp | 45 ++++++--- toolchain/parser/parser_handle_statement.cpp | 2 +- toolchain/parser/precedence.cpp | 70 ++++++------- toolchain/parser/precedence.h | 13 ++- toolchain/parser/precedence_test.cpp | 30 ++---- .../testdata/if_expression/in_type.carbon | 43 ++++++++ .../parser/testdata/operators/assign.carbon | 97 +++++++++++++++++++ .../operators/fail_chained_assign.carbon | 42 ++++++++ .../operators/fail_postincrement.carbon | 39 ++++++++ .../operators/fail_precedence_as.carbon | 53 ++++++++++ .../operators/fail_precedence_assign.carbon | 61 ++++++++++++ .../testdata/operators/fail_variety.carbon | 29 +++--- .../testdata/operators/postfix_repeat.carbon | 14 +-- .../testdata/operators/precedence_as.carbon | 61 ++++++++++++ .../operators/precedence_assign.carbon | 78 +++++++++++++++ .../operators/precedence_unary.carbon | 52 ++++++++++ .../pointer/fail_dereference_type.carbon | 3 - 18 files changed, 634 insertions(+), 99 deletions(-) create mode 100644 toolchain/parser/testdata/if_expression/in_type.carbon create mode 100644 toolchain/parser/testdata/operators/assign.carbon create mode 100644 toolchain/parser/testdata/operators/fail_chained_assign.carbon create mode 100644 toolchain/parser/testdata/operators/fail_postincrement.carbon create mode 100644 toolchain/parser/testdata/operators/fail_precedence_as.carbon create mode 100644 toolchain/parser/testdata/operators/fail_precedence_assign.carbon create mode 100644 toolchain/parser/testdata/operators/precedence_as.carbon create mode 100644 toolchain/parser/testdata/operators/precedence_assign.carbon create mode 100644 toolchain/parser/testdata/operators/precedence_unary.carbon diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index a7b68c7b05780..ced9686f41e66 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -63,6 +63,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedStructLiteralField) CARBON_DIAGNOSTIC_KIND(ExpectedVariableDeclaration) CARBON_DIAGNOSTIC_KIND(ExpectedVariableName) CARBON_DIAGNOSTIC_KIND(OperatorRequiresParentheses) +CARBON_DIAGNOSTIC_KIND(StatementOperatorAsSubexpression) CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresParentheses) CARBON_DIAGNOSTIC_KIND(UnaryOperatorHasWhitespace) CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresWhitespace) diff --git a/toolchain/parser/parser_handle_expression.cpp b/toolchain/parser/parser_handle_expression.cpp index fb62e9ccacc3f..ce52f6d1c0101 100644 --- a/toolchain/parser/parser_handle_expression.cpp +++ b/toolchain/parser/parser_handle_expression.cpp @@ -6,6 +6,15 @@ namespace Carbon { +static auto DiagnoseStatementOperatorAsSubexpression(ParserContext& context) + -> void { + CARBON_DIAGNOSTIC(StatementOperatorAsSubexpression, Error, + "Operator `{0}` can only be used as a complete statement.", + TokenKind); + context.emitter().Emit(*context.position(), StatementOperatorAsSubexpression, + context.PositionKind()); +} + auto ParserHandleExpression(ParserContext& context) -> void { auto state = context.PopState(); @@ -17,13 +26,20 @@ auto ParserHandleExpression(ParserContext& context) -> void { OperatorPriority::RightFirst) { // The precedence rules don't permit this prefix operator in this // context. Diagnose this, but carry on and parse it anyway. - CARBON_DIAGNOSTIC( - UnaryOperatorRequiresParentheses, Error, - "Parentheses are required around this unary `{0}` operator.", - TokenKind); - context.emitter().Emit(*context.position(), - UnaryOperatorRequiresParentheses, - context.PositionKind()); + if (PrecedenceGroup::GetPriority(PrecedenceGroup::ForTopLevelExpression(), + *operator_precedence) == + OperatorPriority::RightFirst) { + CARBON_DIAGNOSTIC( + UnaryOperatorRequiresParentheses, Error, + "Parentheses are required around this unary `{0}` operator.", + TokenKind); + context.emitter().Emit(*context.position(), + UnaryOperatorRequiresParentheses, + context.PositionKind()); + } else { + // This operator wouldn't be allowed even if parenthesized. + DiagnoseStatementOperatorAsSubexpression(context); + } } else { // Check that this operator follows the proper whitespace rules. context.DiagnoseOperatorFixity(ParserContext::OperatorFixity::Prefix); @@ -188,10 +204,17 @@ auto ParserHandleExpressionLoop(ParserContext& context) -> void { // Either the LHS operator and this operator are ambiguous, or the // LHS operator is a unary operator that can't be nested within // this operator. Either way, parentheses are required. - CARBON_DIAGNOSTIC( - OperatorRequiresParentheses, Error, - "Parentheses are required to disambiguate operator precedence."); - context.emitter().Emit(*context.position(), OperatorRequiresParentheses); + if (PrecedenceGroup::GetPriority(PrecedenceGroup::ForTopLevelExpression(), + operator_precedence) == + OperatorPriority::RightFirst) { + CARBON_DIAGNOSTIC( + OperatorRequiresParentheses, Error, + "Parentheses are required to disambiguate operator precedence."); + context.emitter().Emit(*context.position(), OperatorRequiresParentheses); + } else { + // This operator wouldn't be allowed even if parenthesized. + DiagnoseStatementOperatorAsSubexpression(context); + } state.has_error = true; } else { context.DiagnoseOperatorFixity( diff --git a/toolchain/parser/parser_handle_statement.cpp b/toolchain/parser/parser_handle_statement.cpp index 81a5423c5149b..37f7a19107b44 100644 --- a/toolchain/parser/parser_handle_statement.cpp +++ b/toolchain/parser/parser_handle_statement.cpp @@ -46,7 +46,7 @@ auto ParserHandleStatement(ParserContext& context) -> void { } default: { context.PushState(ParserState::ExpressionStatementFinish); - context.PushState(ParserState::Expression); + context.PushStateForExpression(PrecedenceGroup::ForExpressionStatement()); break; } } diff --git a/toolchain/parser/precedence.cpp b/toolchain/parser/precedence.cpp index 39782486c1d8d..96a312d99f677 100644 --- a/toolchain/parser/precedence.cpp +++ b/toolchain/parser/precedence.cpp @@ -18,7 +18,6 @@ enum PrecedenceLevel : int8_t { TermPrefix, // Numeric. NumericPrefix, - NumericPostfix, Modulo, Multiplicative, Additive, @@ -31,8 +30,8 @@ enum PrecedenceLevel : int8_t { // Type formation. TypePrefix, TypePostfix, - // Sentinel representing a type context. - Type, + // Casts. + As, // Logical. LogicalPrefix, Relational, @@ -41,8 +40,7 @@ enum PrecedenceLevel : int8_t { // Conditional. If, // Assignment. - SimpleAssignment, - CompoundAssignment, + Assignment, // Sentinel representing a context in which any operator can appear. Lowest, }; @@ -54,27 +52,24 @@ struct OperatorPriorityTable { constexpr OperatorPriorityTable() : table() { // Start with a list of , // relationships. - MarkHigherThan({Highest}, {TermPrefix}); - MarkHigherThan({TermPrefix}, {NumericPrefix, BitwisePrefix, LogicalPrefix, - NumericPostfix}); - MarkHigherThan({NumericPrefix, NumericPostfix}, - {Modulo, Multiplicative, BitShift}); + MarkHigherThan({Highest}, {TermPrefix, LogicalPrefix}); + MarkHigherThan({TermPrefix}, {NumericPrefix, BitwisePrefix}); + MarkHigherThan({NumericPrefix, BitwisePrefix}, + {As, Multiplicative, Modulo, BitwiseAnd, BitwiseOr, + BitwiseXor, BitShift}); MarkHigherThan({Multiplicative}, {Additive}); - MarkHigherThan({BitwisePrefix}, - {BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}); MarkHigherThan( - {Modulo, Additive, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}, + {As, Additive, Modulo, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}, {Relational}); MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr}); MarkHigherThan({LogicalAnd, LogicalOr}, {If}); - MarkHigherThan({If}, {SimpleAssignment, CompoundAssignment}); - MarkHigherThan({SimpleAssignment, CompoundAssignment}, {Lowest}); + MarkHigherThan({If}, {Assignment}); + MarkHigherThan({Assignment}, {Lowest}); // Types are mostly a separate precedence graph. MarkHigherThan({Highest}, {TypePrefix}); MarkHigherThan({TypePrefix}, {TypePostfix}); - MarkHigherThan({TypePostfix}, {Type}); - MarkHigherThan({Type}, {If}); + MarkHigherThan({TypePostfix}, {As}); // Compute the transitive closure of the above relationships: if we parse // `a $ b @ c` as `(a $ b) @ c` and parse `b @ c % d` as `(b @ c) % d`, @@ -142,17 +137,13 @@ struct OperatorPriorityTable { // Associativity rules occupy the diagonal // For prefix operators, RightFirst would mean `@@x` is `@(@x)` and - // Ambiguous would mean it's an error. LeftFirst is meaningless. For now we - // allow all prefix operators other than `const` to be repeated. - // - // TODO: The design does not permit repeating most unary operators. - for (PrecedenceLevel prefix : - {TermPrefix, NumericPrefix, BitwisePrefix, LogicalPrefix, If}) { + // Ambiguous would mean it's an error. LeftFirst is meaningless. + for (PrecedenceLevel prefix : {TermPrefix, If}) { table[prefix][prefix] = OperatorPriority::RightFirst; } // Postfix operators are symmetric with prefix operators. - for (PrecedenceLevel postfix : {NumericPostfix, TypePostfix}) { + for (PrecedenceLevel postfix : {TypePostfix}) { table[postfix][postfix] = OperatorPriority::LeftFirst; } @@ -164,12 +155,7 @@ struct OperatorPriorityTable { table[assoc][assoc] = OperatorPriority::LeftFirst; } - // Assignment is given right-to-left associativity in order to support - // chained assignment. - table[SimpleAssignment][SimpleAssignment] = OperatorPriority::RightFirst; - - // For other operators, there isn't an obvious answer and we require - // explicit parentheses. + // For other operators, we require explicit parentheses. } constexpr void ConsistencyCheck() { @@ -196,11 +182,15 @@ auto PrecedenceGroup::ForPostfixExpression() -> PrecedenceGroup { } auto PrecedenceGroup::ForTopLevelExpression() -> PrecedenceGroup { + return PrecedenceGroup(If); +} + +auto PrecedenceGroup::ForExpressionStatement() -> PrecedenceGroup { return PrecedenceGroup(Lowest); } auto PrecedenceGroup::ForType() -> PrecedenceGroup { - return PrecedenceGroup(Type); + return ForTopLevelExpression(); } auto PrecedenceGroup::ForLeading(TokenKind kind) @@ -214,9 +204,11 @@ auto PrecedenceGroup::ForLeading(TokenKind kind) return PrecedenceGroup(LogicalPrefix); case TokenKind::Minus: + return PrecedenceGroup(NumericPrefix); + case TokenKind::MinusMinus: case TokenKind::PlusPlus: - return PrecedenceGroup(NumericPrefix); + return PrecedenceGroup(Assignment); case TokenKind::Caret: return PrecedenceGroup(BitwisePrefix); @@ -237,7 +229,6 @@ auto PrecedenceGroup::ForTrailing(TokenKind kind, bool infix) switch (kind) { // Assignment operators. case TokenKind::Equal: - return Trailing{.level = SimpleAssignment, .is_binary = true}; case TokenKind::PlusEqual: case TokenKind::MinusEqual: case TokenKind::StarEqual: @@ -248,7 +239,7 @@ auto PrecedenceGroup::ForTrailing(TokenKind kind, bool infix) case TokenKind::CaretEqual: case TokenKind::GreaterGreaterEqual: case TokenKind::LessLessEqual: - return Trailing{.level = CompoundAssignment, .is_binary = true}; + return Trailing{.level = Assignment, .is_binary = true}; // Logical operators. case TokenKind::And: @@ -293,14 +284,15 @@ auto PrecedenceGroup::ForTrailing(TokenKind kind, bool infix) return infix ? Trailing{.level = Multiplicative, .is_binary = true} : Trailing{.level = TypePostfix, .is_binary = false}; - // Postfix operators. - case TokenKind::MinusMinus: - case TokenKind::PlusPlus: - return Trailing{.level = NumericPostfix, .is_binary = false}; + // Cast operator. + case TokenKind::As: + return Trailing{.level = As, .is_binary = true}; // Prefix-only operators. - case TokenKind::Not: case TokenKind::Const: + case TokenKind::MinusMinus: + case TokenKind::Not: + case TokenKind::PlusPlus: break; // Symbolic tokens that might be operators eventually. diff --git a/toolchain/parser/precedence.h b/toolchain/parser/precedence.h index a983702c172c9..138c309f470fb 100644 --- a/toolchain/parser/precedence.h +++ b/toolchain/parser/precedence.h @@ -38,15 +38,20 @@ class PrecedenceGroup { PrecedenceGroup() = delete; // Get the sentinel precedence level for a postfix expression. All operators - // should have lower precedence than this. + // have lower precedence than this. static auto ForPostfixExpression() -> PrecedenceGroup; - // Get the sentinel precedence level for a top-level expression context. All - // operators should have higher precedence than this. + // Get the precedence level for a top-level or parenthesized expression. All + // expression operators have higher precedence than this. static auto ForTopLevelExpression() -> PrecedenceGroup; + // Get the sentinel precedence level for a statement context. All operators, + // including statement operators like `=` and `++`, have higher precedence + // than this. + static auto ForExpressionStatement() -> PrecedenceGroup; + // Get the precedence level at which to parse a type expression. All type - // operators should have higher precedence than this. + // operators have higher precedence than this. static auto ForType() -> PrecedenceGroup; // Look up the operator information of the given prefix operator token, or diff --git a/toolchain/parser/precedence_test.cpp b/toolchain/parser/precedence_test.cpp index 97d669a37a2ec..5fb035f6ce037 100644 --- a/toolchain/parser/precedence_test.cpp +++ b/toolchain/parser/precedence_test.cpp @@ -33,29 +33,19 @@ TEST(PrecedenceTest, OperatorsAreRecognized) { EXPECT_TRUE(PrecedenceGroup::ForTrailing(TokenKind::Minus, true)->is_binary); EXPECT_FALSE( - PrecedenceGroup::ForTrailing(TokenKind::MinusMinus, false)->is_binary); + PrecedenceGroup::ForTrailing(TokenKind::MinusMinus, false).has_value()); } TEST(PrecedenceTest, InfixVsPostfix) { - // A trailing `-` is always infix; a trailing `--` is always postfix. - EXPECT_TRUE(PrecedenceGroup::ForTrailing(TokenKind::Minus, false)->is_binary); - EXPECT_FALSE( - PrecedenceGroup::ForTrailing(TokenKind::MinusMinus, true)->is_binary); + // A trailing `-` is always binary, even when written with whitespace that + // suggests it's postfix. + EXPECT_TRUE(PrecedenceGroup::ForTrailing(TokenKind::Minus, /*infix*/ false) + ->is_binary); // A trailing `*` is interpreted based on context. EXPECT_TRUE(PrecedenceGroup::ForTrailing(TokenKind::Star, true)->is_binary); EXPECT_FALSE(PrecedenceGroup::ForTrailing(TokenKind::Star, false)->is_binary); - // Postfix `*` can appear in type contexts; infix `*` cannot. - EXPECT_THAT(PrecedenceGroup::GetPriority( - PrecedenceGroup::ForTrailing(TokenKind::Star, true)->level, - PrecedenceGroup::ForType()), - Eq(OperatorPriority::Ambiguous)); - EXPECT_THAT(PrecedenceGroup::GetPriority( - PrecedenceGroup::ForTrailing(TokenKind::Star, false)->level, - PrecedenceGroup::ForType()), - Eq(OperatorPriority::LeftFirst)); - // Infix `*` can appear in `+` contexts; postfix `*` cannot. EXPECT_THAT(PrecedenceGroup::GetPriority( PrecedenceGroup::ForTrailing(TokenKind::Star, true)->level, @@ -69,18 +59,14 @@ TEST(PrecedenceTest, InfixVsPostfix) { TEST(PrecedenceTest, Associativity) { EXPECT_THAT(PrecedenceGroup::ForLeading(TokenKind::Minus)->GetAssociativity(), + Eq(Associativity::None)); + EXPECT_THAT(PrecedenceGroup::ForLeading(TokenKind::Star)->GetAssociativity(), Eq(Associativity::RightToLeft)); - EXPECT_THAT(PrecedenceGroup::ForTrailing(TokenKind::PlusPlus, false) - ->level.GetAssociativity(), - Eq(Associativity::LeftToRight)); EXPECT_THAT(PrecedenceGroup::ForTrailing(TokenKind::Plus, true) ->level.GetAssociativity(), Eq(Associativity::LeftToRight)); EXPECT_THAT(PrecedenceGroup::ForTrailing(TokenKind::Equal, true) ->level.GetAssociativity(), - Eq(Associativity::RightToLeft)); - EXPECT_THAT(PrecedenceGroup::ForTrailing(TokenKind::PlusEqual, true) - ->level.GetAssociativity(), Eq(Associativity::None)); } @@ -134,7 +120,7 @@ TEST(PrecedenceTest, IncomparableOperators) { *PrecedenceGroup::ForLeading(TokenKind::Minus)), Eq(OperatorPriority::Ambiguous)); EXPECT_THAT(PrecedenceGroup::GetPriority( - *PrecedenceGroup::ForLeading(TokenKind::Minus), + *PrecedenceGroup::ForLeading(TokenKind::Not), PrecedenceGroup::ForTrailing(TokenKind::Amp, true)->level), Eq(OperatorPriority::Ambiguous)); EXPECT_THAT( diff --git a/toolchain/parser/testdata/if_expression/in_type.carbon b/toolchain/parser/testdata/if_expression/in_type.carbon new file mode 100644 index 0000000000000..823db8666adf6 --- /dev/null +++ b/toolchain/parser/testdata/if_expression/in_type.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 +// +// AUTOUPDATE + +var n: i32; + +fn F() -> if true then i32 else i32* { + return if true then n else &n; +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'n'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'true'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'true'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '&', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 23}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/assign.carbon b/toolchain/parser/testdata/operators/assign.carbon new file mode 100644 index 0000000000000..d2c4b834546f0 --- /dev/null +++ b/toolchain/parser/testdata/operators/assign.carbon @@ -0,0 +1,97 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() { + var a: i32 = 0; + var b: i32 = 1; + a = b; + a *= b; + a /= b; + a += b; + a -= b; + a %= b; + a &= b; + a |= b; + a ^= b; + a <<= b; + a >>= b; + ++a; + --a; +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'a'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'Literal', text: '0'}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'b'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '*=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '/=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '+=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '-=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '%=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '&=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '|=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '^=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '<<=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '>>=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '--', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 70}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/fail_chained_assign.carbon b/toolchain/parser/testdata/operators/fail_chained_assign.carbon new file mode 100644 index 0000000000000..26309949c1db6 --- /dev/null +++ b/toolchain/parser/testdata/operators/fail_chained_assign.carbon @@ -0,0 +1,42 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() { + var a: i32; + var b: i32; + + // TODO: Produce a custom diagnostic for this. + // CHECK:STDERR: fail_chained_assign.carbon:[[@LINE+3]]:9: Operator `=` can only be used as a complete statement. + // CHECK:STDERR: a = b = 1; + // CHECK:STDERR: ^ + a = b = 1; +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'a'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'b'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 22}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/fail_postincrement.carbon b/toolchain/parser/testdata/operators/fail_postincrement.carbon new file mode 100644 index 0000000000000..d44ed4116003f --- /dev/null +++ b/toolchain/parser/testdata/operators/fail_postincrement.carbon @@ -0,0 +1,39 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() { + var n: i32 = 0; + // TODO: It'd be nice to produce a custom diagnostic here. + // CHECK:STDERR: fail_postincrement.carbon:[[@LINE+3]]:4: Expected `;` after expression statement. + // CHECK:STDERR: n++; + // CHECK:STDERR: ^ + n++; + // CHECK:STDERR: fail_postincrement.carbon:[[@LINE+3]]:4: Expected `;` after expression statement. + // CHECK:STDERR: n--; + // CHECK:STDERR: ^ + n--; +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'n'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'Literal', text: '0'}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/fail_precedence_as.carbon b/toolchain/parser/testdata/operators/fail_precedence_as.carbon new file mode 100644 index 0000000000000..42b998f5f487f --- /dev/null +++ b/toolchain/parser/testdata/operators/fail_precedence_as.carbon @@ -0,0 +1,53 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F(n: i32) { + // No ordering between `not` and `as`. + // CHECK:STDERR: fail_precedence_as.carbon:[[@LINE+3]]:12: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: not true as bool; + // CHECK:STDERR: ^ + not true as bool; + + // No ordering between most binary operators and `as`. + // CHECK:STDERR: fail_precedence_as.carbon:[[@LINE+3]]:9: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: 1 + 1 as i32; + // CHECK:STDERR: ^ + 1 + 1 as i32; + // CHECK:STDERR: fail_precedence_as.carbon:[[@LINE+3]]:9: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: 5 % 2 as i32; + // CHECK:STDERR: ^ + 5 % 2 as i32; +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'Name', text: 'n'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'Literal', text: 'true'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '+', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'Literal', text: '5'}, +// CHECK:STDOUT: {kind: 'Literal', text: '2'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '%', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 26}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/fail_precedence_assign.carbon b/toolchain/parser/testdata/operators/fail_precedence_assign.carbon new file mode 100644 index 0000000000000..253dfe243cf08 --- /dev/null +++ b/toolchain/parser/testdata/operators/fail_precedence_assign.carbon @@ -0,0 +1,61 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F() { + var a: i32; + // Assignment can only appear as an expression statement, not as a subexpression. + // TODO: Produce the "can only be used as a complete statement" diagnostic in each of these cases. + // CHECK:STDERR: fail_precedence_assign.carbon:[[@LINE+3]]:10: Expected `,` or `)`. + // CHECK:STDERR: 1 + (a = 1); + // CHECK:STDERR: ^ + 1 + (a = 1); + // CHECK:STDERR: fail_precedence_assign.carbon:[[@LINE+6]]:19: Expected `else` after `if ... then ...`. + // CHECK:STDERR: (if true then a += 1 else a /= 2); + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_precedence_assign.carbon:[[@LINE+3]]:19: Expected `,` or `)`. + // CHECK:STDERR: (if true then a += 1 else a /= 2); + // CHECK:STDERR: ^ + (if true then a += 1 else a /= 2); + // CHECK:STDERR: fail_precedence_assign.carbon:[[@LINE+3]]:7: Operator `++` can only be used as a complete statement. + // CHECK:STDERR: a + ++a; + // CHECK:STDERR: ^ + a + ++a; +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'a'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'ParenExpressionOrTupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'ParenExpression', text: ')', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '+', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ParenExpressionOrTupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'Literal', text: 'true'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '+=', has_error: yes}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'if', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ParenExpression', text: ')', has_error: yes, subtree_size: 8}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '+', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 31}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/fail_variety.carbon b/toolchain/parser/testdata/operators/fail_variety.carbon index 21dd3e44adc9e..ddf34e677477d 100644 --- a/toolchain/parser/testdata/operators/fail_variety.carbon +++ b/toolchain/parser/testdata/operators/fail_variety.carbon @@ -5,6 +5,9 @@ // AUTOUPDATE fn F() { + // CHECK:STDERR: fail_variety.carbon:[[@LINE+15]]:21: Operator `=` can only be used as a complete statement. + // CHECK:STDERR: n = a * b + c * d = d * d << e & f - not g; + // CHECK:STDERR: ^ // CHECK:STDERR: fail_variety.carbon:[[@LINE+12]]:29: Parentheses are required to disambiguate operator precedence. // CHECK:STDERR: n = a * b + c * d = d * d << e & f - not g; // CHECK:STDERR: ^ @@ -26,7 +29,7 @@ fn F() { // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, // CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, // CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, // CHECK:STDOUT: {kind: 'InfixOperator', text: '*', subtree_size: 3}, @@ -34,18 +37,18 @@ fn F() { // CHECK:STDOUT: {kind: 'NameExpression', text: 'd'}, // CHECK:STDOUT: {kind: 'InfixOperator', text: '*', subtree_size: 3}, // CHECK:STDOUT: {kind: 'InfixOperator', text: '+', subtree_size: 7}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'd'}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'd'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: '*', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'e'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: '<<', has_error: yes, subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'f'}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: '&', has_error: yes, subtree_size: 7}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'g'}, -// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: '-', has_error: yes, subtree_size: 10}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', subtree_size: 18}, -// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', subtree_size: 20}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'd'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'd'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '*', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'e'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '<<', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'f'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '&', has_error: yes, subtree_size: 7}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'g'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'not', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '-', has_error: yes, subtree_size: 10}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', has_error: yes, subtree_size: 20}, // CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 21}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 27}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, diff --git a/toolchain/parser/testdata/operators/postfix_repeat.carbon b/toolchain/parser/testdata/operators/postfix_repeat.carbon index 280313421b427..4e2a522859a08 100644 --- a/toolchain/parser/testdata/operators/postfix_repeat.carbon +++ b/toolchain/parser/testdata/operators/postfix_repeat.carbon @@ -5,7 +5,7 @@ // AUTOUPDATE fn F() { - n++++; + i32****; } // CHECK:STDOUT: [ @@ -14,10 +14,12 @@ fn F() { // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, -// CHECK:STDOUT: {kind: 'PostfixOperator', text: '++', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'PostfixOperator', text: '++', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 12}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/precedence_as.carbon b/toolchain/parser/testdata/operators/precedence_as.carbon new file mode 100644 index 0000000000000..d29838879f323 --- /dev/null +++ b/toolchain/parser/testdata/operators/precedence_as.carbon @@ -0,0 +1,61 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F(n: i32) { + // Type operators and unary operators are lower precedence than `as`. + -n as const i32; + &n as i32*; + + // `as` is lower precedence than relational comparisons and logical operators. + if (1 as i32 < 2 as i32 and true as bool and false as bool) {} +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'Name', text: 'n'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '-', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'const', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '&', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'IfConditionStart', text: '('}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'Literal', text: '2'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '<', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'Literal', text: 'true'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperand', text: 'and', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'Literal', text: 'false'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'as', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: 'and', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'IfCondition', text: ')', subtree_size: 19}, +// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'}, +// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IfStatement', text: 'if', subtree_size: 22}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 43}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/precedence_assign.carbon b/toolchain/parser/testdata/operators/precedence_assign.carbon new file mode 100644 index 0000000000000..151724b993e2f --- /dev/null +++ b/toolchain/parser/testdata/operators/precedence_assign.carbon @@ -0,0 +1,78 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F(c: bool) { + var a: i32; + var b: i32; + var p: i32*; + *p = if c then 1 else 2; + // These are valid to _parse_ even though rejected semantically. + (if c then a else b) += if c then 1 else 2; + ++if c then a else b; +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'Name', text: 'c'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'bool'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'a'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'b'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'p'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'c'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: '2'}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'ParenExpressionOrTupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'c'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ParenExpression', text: ')', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'c'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: '2'}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '+=', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'c'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 59}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/precedence_unary.carbon b/toolchain/parser/testdata/operators/precedence_unary.carbon new file mode 100644 index 0000000000000..bf4d194e9fd0f --- /dev/null +++ b/toolchain/parser/testdata/operators/precedence_unary.carbon @@ -0,0 +1,52 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +fn F(p: i32*) { + // Numeric, bitwise, and bit-shift binary operators are lower precedence than + // Numeric and bitwise unary operators, which are lower precedence than + // dereference. + -*p + ^*p; + -*p | ^*p; + -*p << ^*p; +} + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'F'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'Name', text: 'p'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '-', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '^', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '+', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '-', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '^', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '|', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '-', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '^', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '<<', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 34}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/semantics/testdata/pointer/fail_dereference_type.carbon b/toolchain/semantics/testdata/pointer/fail_dereference_type.carbon index e0ea3aa9783cc..bf15150f49685 100644 --- a/toolchain/semantics/testdata/pointer/fail_dereference_type.carbon +++ b/toolchain/semantics/testdata/pointer/fail_dereference_type.carbon @@ -4,9 +4,6 @@ // // AUTOUPDATE -// CHECK:STDERR: fail_dereference_type.carbon:[[@LINE+9]]:8: Parentheses are required around this unary `*` operator. -// CHECK:STDERR: var p: *i32; -// CHECK:STDERR: ^ // CHECK:STDERR: fail_dereference_type.carbon:[[@LINE+6]]:8: Cannot dereference operand of non-pointer type `type`. // CHECK:STDERR: var p: *i32; // CHECK:STDERR: ^