From c4b880c6efa2de51cba413ca3c6809e7294a55e1 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 26 Jul 2023 14:24:54 -0700 Subject: [PATCH] Parsing for pointer types and pointer operators. (#3026) This provides parsing support for the functionality added in #2006. --- toolchain/diagnostics/diagnostic_kind.def | 3 +- toolchain/lexer/testdata/keywords.carbon | 98 ++++++++++--------- toolchain/lexer/token_kind.def | 1 + toolchain/parser/parse_node_kind.def | 7 ++ toolchain/parser/parser_handle_expression.cpp | 21 +++- toolchain/parser/parser_handle_period.cpp | 30 ++++-- toolchain/parser/parser_state.def | 10 ++ toolchain/parser/precedence.cpp | 21 +++- .../testdata/class/fn_definitions.carbon | 23 ++++- .../testdata/namespace/fail_arrow.carbon | 26 +++++ .../testdata/operators/fail_variety.carbon | 2 +- .../testdata/operators/prefix_repeat.carbon | 14 +-- .../operators/recover_prefix_repeat.carbon | 25 +++++ .../testdata/pointer/const_pointer.carbon | 61 ++++++++++++ .../fail_pointer_type_in_expression.carbon | 79 +++++++++++++++ .../testdata/pointer/pointer_type.carbon | 44 +++++++++ .../testdata/pointer/pointer_value.carbon | 54 ++++++++++ toolchain/semantics/semantics_handle_name.cpp | 6 ++ 18 files changed, 450 insertions(+), 75 deletions(-) create mode 100644 toolchain/parser/testdata/namespace/fail_arrow.carbon create mode 100644 toolchain/parser/testdata/operators/recover_prefix_repeat.carbon create mode 100644 toolchain/parser/testdata/pointer/const_pointer.carbon create mode 100644 toolchain/parser/testdata/pointer/fail_pointer_type_in_expression.carbon create mode 100644 toolchain/parser/testdata/pointer/pointer_type.carbon create mode 100644 toolchain/parser/testdata/pointer/pointer_value.carbon diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 6e4639a2f449..58da67203898 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -53,7 +53,7 @@ CARBON_DIAGNOSTIC_KIND(BinaryOperatorRequiresWhitespace) CARBON_DIAGNOSTIC_KIND(ExpectedCloseParen) CARBON_DIAGNOSTIC_KIND(ExpectedCodeBlock) CARBON_DIAGNOSTIC_KIND(ExpectedExpression) -CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterDot) +CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterDotOrArrow) CARBON_DIAGNOSTIC_KIND(ExpectedParameterName) CARBON_DIAGNOSTIC_KIND(ExpectedParenAfter) CARBON_DIAGNOSTIC_KIND(ExpectedExpressionSemi) @@ -62,6 +62,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedStructLiteralField) CARBON_DIAGNOSTIC_KIND(ExpectedVariableDeclaration) CARBON_DIAGNOSTIC_KIND(ExpectedVariableName) CARBON_DIAGNOSTIC_KIND(OperatorRequiresParentheses) +CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresParentheses) CARBON_DIAGNOSTIC_KIND(UnaryOperatorHasWhitespace) CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresWhitespace) CARBON_DIAGNOSTIC_KIND(UnexpectedTokenAfterListElement) diff --git a/toolchain/lexer/testdata/keywords.carbon b/toolchain/lexer/testdata/keywords.carbon index a08dd1a8e286..54611b2fc2c8 100644 --- a/toolchain/lexer/testdata/keywords.carbon +++ b/toolchain/lexer/testdata/keywords.carbon @@ -34,100 +34,102 @@ case choice // CHECK:STDOUT: { index: 13, kind: 'Class', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'class', has_trailing_space: true }, class -// CHECK:STDOUT: { index: 14, kind: 'Constraint', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'constraint', has_trailing_space: true }, +// CHECK:STDOUT: { index: 14, kind: 'Const', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'const', has_trailing_space: true }, +const +// CHECK:STDOUT: { index: 15, kind: 'Constraint', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'constraint', has_trailing_space: true }, constraint -// CHECK:STDOUT: { index: 15, kind: 'Continue', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'continue', has_trailing_space: true }, +// CHECK:STDOUT: { index: 16, kind: 'Continue', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'continue', has_trailing_space: true }, continue -// CHECK:STDOUT: { index: 16, kind: 'Default', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'default', has_trailing_space: true }, +// CHECK:STDOUT: { index: 17, kind: 'Default', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'default', has_trailing_space: true }, default -// CHECK:STDOUT: { index: 17, kind: 'Destructor', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'destructor', has_trailing_space: true }, +// CHECK:STDOUT: { index: 18, kind: 'Destructor', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'destructor', has_trailing_space: true }, destructor -// CHECK:STDOUT: { index: 18, kind: 'Else', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'else', has_trailing_space: true }, +// CHECK:STDOUT: { index: 19, kind: 'Else', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'else', has_trailing_space: true }, else -// CHECK:STDOUT: { index: 19, kind: 'Extend', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'extend', has_trailing_space: true }, +// CHECK:STDOUT: { index: 20, kind: 'Extend', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'extend', has_trailing_space: true }, extend -// CHECK:STDOUT: { index: 20, kind: 'False', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'false', has_trailing_space: true }, +// CHECK:STDOUT: { index: 21, kind: 'False', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'false', has_trailing_space: true }, false -// CHECK:STDOUT: { index: 21, kind: 'Final', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'final', has_trailing_space: true }, +// CHECK:STDOUT: { index: 22, kind: 'Final', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'final', has_trailing_space: true }, final -// CHECK:STDOUT: { index: 22, kind: 'Fn', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'fn', has_trailing_space: true }, +// CHECK:STDOUT: { index: 23, kind: 'Fn', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'fn', has_trailing_space: true }, fn -// CHECK:STDOUT: { index: 23, kind: 'For', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'for', has_trailing_space: true }, +// CHECK:STDOUT: { index: 24, kind: 'For', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'for', has_trailing_space: true }, for -// CHECK:STDOUT: { index: 24, kind: 'Forall', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'forall', has_trailing_space: true }, +// CHECK:STDOUT: { index: 25, kind: 'Forall', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'forall', has_trailing_space: true }, forall -// CHECK:STDOUT: { index: 25, kind: 'Friend', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'friend', has_trailing_space: true }, +// CHECK:STDOUT: { index: 26, kind: 'Friend', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'friend', has_trailing_space: true }, friend -// CHECK:STDOUT: { index: 26, kind: 'If', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'if', has_trailing_space: true }, +// CHECK:STDOUT: { index: 27, kind: 'If', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'if', has_trailing_space: true }, if -// CHECK:STDOUT: { index: 27, kind: 'Impl', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'impl', has_trailing_space: true }, +// CHECK:STDOUT: { index: 28, kind: 'Impl', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'impl', has_trailing_space: true }, impl -// CHECK:STDOUT: { index: 28, kind: 'Impls', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'impls', has_trailing_space: true }, +// CHECK:STDOUT: { index: 29, kind: 'Impls', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'impls', has_trailing_space: true }, impls -// CHECK:STDOUT: { index: 29, kind: 'Import', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'import', has_trailing_space: true }, +// CHECK:STDOUT: { index: 30, kind: 'Import', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'import', has_trailing_space: true }, import -// CHECK:STDOUT: { index: 30, kind: 'In', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'in', has_trailing_space: true }, +// CHECK:STDOUT: { index: 31, kind: 'In', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'in', has_trailing_space: true }, in -// CHECK:STDOUT: { index: 31, kind: 'Interface', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'interface', has_trailing_space: true }, +// CHECK:STDOUT: { index: 32, kind: 'Interface', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'interface', has_trailing_space: true }, interface -// CHECK:STDOUT: { index: 32, kind: 'Let', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'let', has_trailing_space: true }, +// CHECK:STDOUT: { index: 33, kind: 'Let', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'let', has_trailing_space: true }, let -// CHECK:STDOUT: { index: 33, kind: 'Library', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'library', has_trailing_space: true }, +// CHECK:STDOUT: { index: 34, kind: 'Library', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'library', has_trailing_space: true }, library -// CHECK:STDOUT: { index: 34, kind: 'Like', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'like', has_trailing_space: true }, +// CHECK:STDOUT: { index: 35, kind: 'Like', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'like', has_trailing_space: true }, like -// CHECK:STDOUT: { index: 35, kind: 'Match', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'match', has_trailing_space: true }, +// CHECK:STDOUT: { index: 36, kind: 'Match', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'match', has_trailing_space: true }, match -// CHECK:STDOUT: { index: 36, kind: 'Namespace', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'namespace', has_trailing_space: true }, +// CHECK:STDOUT: { index: 37, kind: 'Namespace', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'namespace', has_trailing_space: true }, namespace -// CHECK:STDOUT: { index: 37, kind: 'Not', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'not', has_trailing_space: true }, +// CHECK:STDOUT: { index: 38, kind: 'Not', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'not', has_trailing_space: true }, not -// CHECK:STDOUT: { index: 38, kind: 'Observe', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'observe', has_trailing_space: true }, +// CHECK:STDOUT: { index: 39, kind: 'Observe', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'observe', has_trailing_space: true }, observe -// CHECK:STDOUT: { index: 39, kind: 'Or', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'or', has_trailing_space: true }, +// CHECK:STDOUT: { index: 40, kind: 'Or', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'or', has_trailing_space: true }, or -// CHECK:STDOUT: { index: 40, kind: 'Override', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'override', has_trailing_space: true }, +// CHECK:STDOUT: { index: 41, kind: 'Override', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'override', has_trailing_space: true }, override -// CHECK:STDOUT: { index: 41, kind: 'Package', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'package', has_trailing_space: true }, +// CHECK:STDOUT: { index: 42, kind: 'Package', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'package', has_trailing_space: true }, package -// CHECK:STDOUT: { index: 42, kind: 'Partial', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'partial', has_trailing_space: true }, +// CHECK:STDOUT: { index: 43, kind: 'Partial', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'partial', has_trailing_space: true }, partial -// CHECK:STDOUT: { index: 43, kind: 'Private', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'private', has_trailing_space: true }, +// CHECK:STDOUT: { index: 44, kind: 'Private', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'private', has_trailing_space: true }, private -// CHECK:STDOUT: { index: 44, kind: 'Protected', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'protected', has_trailing_space: true }, +// CHECK:STDOUT: { index: 45, kind: 'Protected', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'protected', has_trailing_space: true }, protected -// CHECK:STDOUT: { index: 45, kind: 'Require', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'require', has_trailing_space: true }, +// CHECK:STDOUT: { index: 46, kind: 'Require', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'require', has_trailing_space: true }, require -// CHECK:STDOUT: { index: 46, kind: 'Return', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'return', has_trailing_space: true }, +// CHECK:STDOUT: { index: 47, kind: 'Return', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'return', has_trailing_space: true }, return -// CHECK:STDOUT: { index: 47, kind: 'Returned', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'returned', has_trailing_space: true }, +// CHECK:STDOUT: { index: 48, kind: 'Returned', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'returned', has_trailing_space: true }, returned -// CHECK:STDOUT: { index: 48, kind: 'SelfTypeIdentifier', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'Self', has_trailing_space: true }, +// CHECK:STDOUT: { index: 49, kind: 'SelfTypeIdentifier', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'Self', has_trailing_space: true }, Self -// CHECK:STDOUT: { index: 49, kind: 'SelfValueIdentifier', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'self', has_trailing_space: true }, +// CHECK:STDOUT: { index: 50, kind: 'SelfValueIdentifier', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'self', has_trailing_space: true }, self -// CHECK:STDOUT: { index: 50, kind: 'StringTypeLiteral', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'String', has_trailing_space: true }, +// CHECK:STDOUT: { index: 51, kind: 'StringTypeLiteral', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'String', has_trailing_space: true }, String -// CHECK:STDOUT: { index: 51, kind: 'Template', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'template', has_trailing_space: true }, +// CHECK:STDOUT: { index: 52, kind: 'Template', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'template', has_trailing_space: true }, template -// CHECK:STDOUT: { index: 52, kind: 'Then', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'then', has_trailing_space: true }, +// CHECK:STDOUT: { index: 53, kind: 'Then', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'then', has_trailing_space: true }, then -// CHECK:STDOUT: { index: 53, kind: 'True', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'true', has_trailing_space: true }, +// CHECK:STDOUT: { index: 54, kind: 'True', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'true', has_trailing_space: true }, true -// CHECK:STDOUT: { index: 54, kind: 'Type', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'type', has_trailing_space: true }, +// CHECK:STDOUT: { index: 55, kind: 'Type', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'type', has_trailing_space: true }, type -// CHECK:STDOUT: { index: 55, kind: 'Underscore', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: '_', has_trailing_space: true }, +// CHECK:STDOUT: { index: 56, kind: 'Underscore', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: '_', has_trailing_space: true }, _ -// CHECK:STDOUT: { index: 56, kind: 'Var', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'var', has_trailing_space: true }, +// CHECK:STDOUT: { index: 57, kind: 'Var', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'var', has_trailing_space: true }, var -// CHECK:STDOUT: { index: 57, kind: 'Virtual', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'virtual', has_trailing_space: true }, +// CHECK:STDOUT: { index: 58, kind: 'Virtual', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'virtual', has_trailing_space: true }, virtual -// CHECK:STDOUT: { index: 58, kind: 'Where', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'where', has_trailing_space: true }, +// CHECK:STDOUT: { index: 59, kind: 'Where', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'where', has_trailing_space: true }, where -// CHECK:STDOUT: { index: 59, kind: 'While', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'while', has_trailing_space: true }, +// CHECK:STDOUT: { index: 60, kind: 'While', line: {{ *}}[[@LINE+1]], column: 1, indent: 1, spelling: 'while', has_trailing_space: true }, while -// CHECK:STDOUT: { index: 60, kind: 'Identifier', line: {{ *}}[[@LINE+3]], column: 1, indent: 1, spelling: 'notakeyword', identifier: 0, has_trailing_space: true }, -// CHECK:STDOUT: { index: 61, kind: 'EndOfFile', line: {{ *}}[[@LINE+2]], column: {{[0-9]+}}, indent: 1, spelling: '' }, +// CHECK:STDOUT: { index: 61, kind: 'Identifier', line: {{ *}}[[@LINE+3]], column: 1, indent: 1, spelling: 'notakeyword', identifier: 0, has_trailing_space: true }, +// CHECK:STDOUT: { index: 62, kind: 'EndOfFile', line: {{ *}}[[@LINE+2]], column: {{[0-9]+}}, indent: 1, spelling: '' }, // CHECK:STDOUT: ] notakeyword diff --git a/toolchain/lexer/token_kind.def b/toolchain/lexer/token_kind.def index e902e3c07831..f8c2ad84a0fd 100644 --- a/toolchain/lexer/token_kind.def +++ b/toolchain/lexer/token_kind.def @@ -135,6 +135,7 @@ CARBON_KEYWORD_TOKEN(Break, "break") CARBON_KEYWORD_TOKEN(Case, "case") CARBON_KEYWORD_TOKEN(Choice, "choice") CARBON_KEYWORD_TOKEN(Class, "class") +CARBON_KEYWORD_TOKEN(Const, "const") CARBON_KEYWORD_TOKEN(Constraint, "constraint") CARBON_KEYWORD_TOKEN(Continue, "continue") CARBON_KEYWORD_TOKEN(Default, "default") diff --git a/toolchain/parser/parse_node_kind.def b/toolchain/parser/parse_node_kind.def index ea69a4eafc04..7dde952b05f2 100644 --- a/toolchain/parser/parse_node_kind.def +++ b/toolchain/parser/parse_node_kind.def @@ -256,6 +256,13 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(QualifiedDeclaration, 2) // QualifiedExpression CARBON_PARSE_NODE_KIND_CHILD_COUNT(MemberAccessExpression, 2) +// A pointer member access expression, such as `a->b` or +// `GetObject()->(Interface.member)`: +// _external_: lhs expression +// _external_: rhs expression +// QualifiedExpression +CARBON_PARSE_NODE_KIND_CHILD_COUNT(PointerMemberAccessExpression, 2) + // A literal. CARBON_PARSE_NODE_KIND_CHILD_COUNT(Literal, 0) diff --git a/toolchain/parser/parser_handle_expression.cpp b/toolchain/parser/parser_handle_expression.cpp index 12884097a2c2..d6461e8788be 100644 --- a/toolchain/parser/parser_handle_expression.cpp +++ b/toolchain/parser/parser_handle_expression.cpp @@ -6,10 +6,6 @@ namespace Carbon { -CARBON_DIAGNOSTIC( - OperatorRequiresParentheses, Error, - "Parentheses are required to disambiguate operator precedence."); - auto ParserHandleExpression(ParserContext& context) -> void { auto state = context.PopState(); @@ -21,7 +17,13 @@ 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. - context.emitter().Emit(*context.position(), OperatorRequiresParentheses); + CARBON_DIAGNOSTIC( + UnaryOperatorRequiresParentheses, Error, + "Parentheses are required around this unary `{0}` operator.", + TokenKind); + context.emitter().Emit(*context.position(), + UnaryOperatorRequiresParentheses, + context.PositionKind()); } else { // Check that this operator follows the proper whitespace rules. context.DiagnoseOperatorFixity(ParserContext::OperatorFixity::Prefix); @@ -123,6 +125,12 @@ auto ParserHandleExpressionInPostfixLoop(ParserContext& context) -> void { context.PushState(state); break; } + case TokenKind::MinusGreater: { + context.PushState(state); + state.state = ParserState::ArrowExpression; + context.PushState(state); + break; + } case TokenKind::OpenParen: { context.PushState(state); state.state = ParserState::CallExpression; @@ -171,6 +179,9 @@ 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); state.has_error = true; } else { diff --git a/toolchain/parser/parser_handle_period.cpp b/toolchain/parser/parser_handle_period.cpp index 9156837fff37..ee18f974361a 100644 --- a/toolchain/parser/parser_handle_period.cpp +++ b/toolchain/parser/parser_handle_period.cpp @@ -6,21 +6,25 @@ namespace Carbon { -// Handles PeriodAs variants. +// Handles PeriodAs variants and ArrowExpression. // TODO: This currently only supports identifiers on the rhs, but will in the // future need to handle things like `object.(Interface.member)` for qualifiers. -auto ParserHandlePeriod(ParserContext& context, ParseNodeKind node_kind) +static auto ParserHandlePeriodOrArrow(ParserContext& context, + ParseNodeKind node_kind, bool is_arrow) -> void { auto state = context.PopState(); // `.` identifier - auto dot = context.ConsumeChecked(TokenKind::Period); + auto dot = context.ConsumeChecked(is_arrow ? TokenKind::MinusGreater + : TokenKind::Period); if (!context.ConsumeAndAddLeafNodeIf(TokenKind::Identifier, ParseNodeKind::Name)) { - CARBON_DIAGNOSTIC(ExpectedIdentifierAfterDot, Error, - "Expected identifier after `.`."); - context.emitter().Emit(*context.position(), ExpectedIdentifierAfterDot); + CARBON_DIAGNOSTIC(ExpectedIdentifierAfterDotOrArrow, Error, + "Expected identifier after `{0}`.", llvm::StringRef); + context.emitter().Emit(*context.position(), + ExpectedIdentifierAfterDotOrArrow, + is_arrow ? "->" : "."); // If we see a keyword, assume it was intended to be a name. // TODO: Should keywords be valid here? if (context.PositionKind().is_keyword()) { @@ -39,15 +43,23 @@ auto ParserHandlePeriod(ParserContext& context, ParseNodeKind node_kind) } auto ParserHandlePeriodAsDeclaration(ParserContext& context) -> void { - ParserHandlePeriod(context, ParseNodeKind::QualifiedDeclaration); + ParserHandlePeriodOrArrow(context, ParseNodeKind::QualifiedDeclaration, + /*is_arrow=*/false); } auto ParserHandlePeriodAsExpression(ParserContext& context) -> void { - ParserHandlePeriod(context, ParseNodeKind::MemberAccessExpression); + ParserHandlePeriodOrArrow(context, ParseNodeKind::MemberAccessExpression, + /*is_arrow=*/false); } auto ParserHandlePeriodAsStruct(ParserContext& context) -> void { - ParserHandlePeriod(context, ParseNodeKind::StructFieldDesignator); + ParserHandlePeriodOrArrow(context, ParseNodeKind::StructFieldDesignator, + /*is_arrow=*/false); +} + +auto ParserHandleArrowExpression(ParserContext& context) -> void { + ParserHandlePeriodOrArrow( + context, ParseNodeKind::PointerMemberAccessExpression, /*is_arrow=*/true); } } // namespace Carbon diff --git a/toolchain/parser/parser_state.def b/toolchain/parser/parser_state.def index 6ba382ca7d93..c49d75169a4b 100644 --- a/toolchain/parser/parser_state.def +++ b/toolchain/parser/parser_state.def @@ -215,6 +215,13 @@ CARBON_PARSER_STATE(DeclarationScopeLoop) // (state done) CARBON_PARSER_STATE_VARIANTS3(Period, Declaration, Expression, Struct) +// Handles `->name` expressions. Identical to PeriodAsExpression except for the +// leading token. +// +// Always: +// (state done) +CARBON_PARSER_STATE(ArrowExpression) + // Handles processing of an expression. // // If `If`: @@ -250,6 +257,9 @@ CARBON_PARSER_STATE(ExpressionInPostfix) // If `Period`: // 1. PeriodAsExpression // 2. ExpressionInPostfixLoop +// If `MinusGreater`: +// 1. ArrowExpression +// 2. ExpressionInPostfixLoop // If `OpenParen`: // 1. CallExpression // 2. ExpressionInPostfixLoop diff --git a/toolchain/parser/precedence.cpp b/toolchain/parser/precedence.cpp index 6674e1248a86..39782486c1d8 100644 --- a/toolchain/parser/precedence.cpp +++ b/toolchain/parser/precedence.cpp @@ -29,6 +29,7 @@ enum PrecedenceLevel : int8_t { BitwiseXor, BitShift, // Type formation. + TypePrefix, TypePostfix, // Sentinel representing a type context. Type, @@ -55,21 +56,26 @@ struct OperatorPriorityTable { // relationships. MarkHigherThan({Highest}, {TermPrefix}); MarkHigherThan({TermPrefix}, {NumericPrefix, BitwisePrefix, LogicalPrefix, - NumericPostfix, TypePostfix}); + NumericPostfix}); MarkHigherThan({NumericPrefix, NumericPostfix}, {Modulo, Multiplicative, BitShift}); MarkHigherThan({Multiplicative}, {Additive}); MarkHigherThan({BitwisePrefix}, {BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}); - MarkHigherThan({TypePostfix}, {Type}); MarkHigherThan( - {Modulo, Additive, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift, Type}, + {Modulo, Additive, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}, {Relational}); MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr}); MarkHigherThan({LogicalAnd, LogicalOr}, {If}); MarkHigherThan({If}, {SimpleAssignment, CompoundAssignment}); MarkHigherThan({SimpleAssignment, CompoundAssignment}, {Lowest}); + // Types are mostly a separate precedence graph. + MarkHigherThan({Highest}, {TypePrefix}); + MarkHigherThan({TypePrefix}, {TypePostfix}); + MarkHigherThan({TypePostfix}, {Type}); + MarkHigherThan({Type}, {If}); + // 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`, // then we will parse `a $ b @ c % d` as `((a $ b) @ c) % d` and should @@ -137,7 +143,9 @@ struct OperatorPriorityTable { // 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 to be repeated. + // 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}) { table[prefix][prefix] = OperatorPriority::RightFirst; @@ -199,6 +207,7 @@ auto PrecedenceGroup::ForLeading(TokenKind kind) -> std::optional { switch (kind) { case TokenKind::Star: + case TokenKind::Amp: return PrecedenceGroup(TermPrefix); case TokenKind::Not: @@ -215,6 +224,9 @@ auto PrecedenceGroup::ForLeading(TokenKind kind) case TokenKind::If: return PrecedenceGroup(If); + case TokenKind::Const: + return PrecedenceGroup(TypePrefix); + default: return std::nullopt; } @@ -288,6 +300,7 @@ auto PrecedenceGroup::ForTrailing(TokenKind kind, bool infix) // Prefix-only operators. case TokenKind::Not: + case TokenKind::Const: break; // Symbolic tokens that might be operators eventually. diff --git a/toolchain/parser/testdata/class/fn_definitions.carbon b/toolchain/parser/testdata/class/fn_definitions.carbon index 1d40fdbcf55e..8893669e0ab5 100644 --- a/toolchain/parser/testdata/class/fn_definitions.carbon +++ b/toolchain/parser/testdata/class/fn_definitions.carbon @@ -41,7 +41,26 @@ // CHECK:STDOUT: {kind: 'MemberAccessExpression', text: '.', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, -// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 38}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'Modify'}, +// CHECK:STDOUT: {kind: 'DeducedParameterListStart', text: '['}, +// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfTypeNameExpression', text: 'Self'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'Address', text: 'addr', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'DeducedParameterList', text: ']', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'Name', text: 'x'}, +// CHECK:STDOUT: {kind: 'PointerMemberAccessExpression', text: '->', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'Literal', text: '1'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '=', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 19}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 57}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] @@ -49,4 +68,6 @@ class Foo { fn Make() -> Self { return {.x = 0}; } fn Baz[self: Self]() -> i32 { return self.x; } + + fn Modify[addr self: Self*]() { self->x = 1; } } diff --git a/toolchain/parser/testdata/namespace/fail_arrow.carbon b/toolchain/parser/testdata/namespace/fail_arrow.carbon new file mode 100644 index 000000000000..bdf8d6183eae --- /dev/null +++ b/toolchain/parser/testdata/namespace/fail_arrow.carbon @@ -0,0 +1,26 @@ +// 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 +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'}, +// CHECK:STDOUT: {kind: 'Name', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'Namespace', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'Bar'}, +// CHECK:STDOUT: {kind: 'CallExpressionStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'CallExpression', text: ')', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', has_error: yes, subtree_size: 7}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +namespace Foo; + +// CHECK:STDERR: fail_arrow.carbon:[[@LINE+3]]:7: `fn` requires a `(` for parameters. +// CHECK:STDERR: fn Foo->Bar() {} +// CHECK:STDERR: ^ +fn Foo->Bar() {} diff --git a/toolchain/parser/testdata/operators/fail_variety.carbon b/toolchain/parser/testdata/operators/fail_variety.carbon index eeae94c21201..c832f7cc7849 100644 --- a/toolchain/parser/testdata/operators/fail_variety.carbon +++ b/toolchain/parser/testdata/operators/fail_variety.carbon @@ -44,7 +44,7 @@ fn F() { // CHECK:STDERR: fail_variety.carbon:[[@LINE+6]]:38: Parentheses are required to disambiguate operator precedence. // CHECK:STDERR: n = a * b + c * d = d * d << e & f - not g; // CHECK:STDERR: ^ - // CHECK:STDERR: fail_variety.carbon:[[@LINE+3]]:40: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: fail_variety.carbon:[[@LINE+3]]:40: Parentheses are required around this unary `not` operator. // CHECK:STDERR: n = a * b + c * d = d * d << e & f - not g; // CHECK:STDERR: ^ n = a * b + c * d = d * d << e & f - not g; diff --git a/toolchain/parser/testdata/operators/prefix_repeat.carbon b/toolchain/parser/testdata/operators/prefix_repeat.carbon index a6bb0e27c5f4..aef8b10e7779 100644 --- a/toolchain/parser/testdata/operators/prefix_repeat.carbon +++ b/toolchain/parser/testdata/operators/prefix_repeat.carbon @@ -9,14 +9,16 @@ // 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: 'PrefixOperator', text: '++', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 3}, -// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '&', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PrefixOperator', 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: ] fn F() { - ++++n; + ***&n; } diff --git a/toolchain/parser/testdata/operators/recover_prefix_repeat.carbon b/toolchain/parser/testdata/operators/recover_prefix_repeat.carbon new file mode 100644 index 000000000000..1d250a9997b8 --- /dev/null +++ b/toolchain/parser/testdata/operators/recover_prefix_repeat.carbon @@ -0,0 +1,25 @@ +// 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 +// 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: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'const', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'const', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +// CHECK:STDERR: recover_prefix_repeat.carbon:[[@LINE+3]]:17: Parentheses are required around this unary `const` operator. +// CHECK:STDERR: fn F() -> const const i32* { +// CHECK:STDERR: ^ +fn F() -> const const i32* { +} diff --git a/toolchain/parser/testdata/pointer/const_pointer.carbon b/toolchain/parser/testdata/pointer/const_pointer.carbon new file mode 100644 index 000000000000..a33f87fb86fb --- /dev/null +++ b/toolchain/parser/testdata/pointer/const_pointer.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 +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'A'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'const', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'A'}, +// CHECK:STDOUT: {kind: 'CallExpressionStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'CallExpression', text: ')', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'B'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ParenExpressionOrTupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'const', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ParenExpression', text: ')', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'B'}, +// CHECK:STDOUT: {kind: 'CallExpressionStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'CallExpression', text: ')', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'C'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ParenExpressionOrTupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ParenExpression', text: ')', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: 'const', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'C'}, +// CHECK:STDOUT: {kind: 'CallExpressionStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'CallExpression', text: ')', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +fn A() -> const i32* { return A(); } +fn B() -> (const i32)* { return B(); } +fn C() -> const (i32*) { return C(); } diff --git a/toolchain/parser/testdata/pointer/fail_pointer_type_in_expression.carbon b/toolchain/parser/testdata/pointer/fail_pointer_type_in_expression.carbon new file mode 100644 index 000000000000..880a7cb14383 --- /dev/null +++ b/toolchain/parser/testdata/pointer/fail_pointer_type_in_expression.carbon @@ -0,0 +1,79 @@ +// 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 +// 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: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'Literal', text: '3'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '*', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'Literal', text: '4'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '*', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'G'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: '4'}, +// CHECK:STDOUT: {kind: 'InfixOperator', text: '+', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'Name', text: 'H'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +fn F() -> i32 { + // TODO: Indicate the locations of both operators involved in the precedence + // error, so that it's clear what ambiguity we're referring to. + // TODO: Improve error recovery so that we recover as `3 * (i32*) * 4`, not + // as `(3 * i32)* * 4`, to suppress the second error here. + // CHECK:STDERR: fail_pointer_type_in_expression.carbon:[[@LINE+6]]:17: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: return 3 * i32* * 4; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_pointer_type_in_expression.carbon:[[@LINE+3]]:19: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: return 3 * i32* * 4; + // CHECK:STDERR: ^ + return 3 * i32* * 4; +} + +fn G() -> i32 { + // CHECK:STDERR: fail_pointer_type_in_expression.carbon:[[@LINE+3]]:15: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: return i32* + 4; + // CHECK:STDERR: ^ + return i32* + 4; +} + +fn H() -> i32 { + // CHECK:STDERR: fail_pointer_type_in_expression.carbon:[[@LINE+3]]:14: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: return *i32*; + // CHECK:STDERR: ^ + return *i32*; +} diff --git a/toolchain/parser/testdata/pointer/pointer_type.carbon b/toolchain/parser/testdata/pointer/pointer_type.carbon new file mode 100644 index 000000000000..614d659601bf --- /dev/null +++ b/toolchain/parser/testdata/pointer/pointer_type.carbon @@ -0,0 +1,44 @@ +// 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 +// 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: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'T'}, +// CHECK:STDOUT: {kind: 'Literal', text: 'type'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'Literal', text: 'true'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'Literal', text: 'f64'}, +// CHECK:STDOUT: {kind: 'PostfixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +fn F(p: i32*) -> i32* { + return p; +} + +var T: type = if true then i32* else f64*; diff --git a/toolchain/parser/testdata/pointer/pointer_value.carbon b/toolchain/parser/testdata/pointer/pointer_value.carbon new file mode 100644 index 000000000000..82835623b0ae --- /dev/null +++ b/toolchain/parser/testdata/pointer/pointer_value.carbon @@ -0,0 +1,54 @@ +// 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 +// 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: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7}, +// 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: '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: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'n'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '&', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'q'}, +// 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: 'PatternBinding', text: ':', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'p'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '&', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'q'}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '*', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 39}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +fn F() -> i32 { + var n: i32 = 0; + var p: i32* = &n; + var q: i32** = &p; + return **q; +} diff --git a/toolchain/semantics/semantics_handle_name.cpp b/toolchain/semantics/semantics_handle_name.cpp index f207ae15e10e..c1b0135bc5ab 100644 --- a/toolchain/semantics/semantics_handle_name.cpp +++ b/toolchain/semantics/semantics_handle_name.cpp @@ -65,6 +65,12 @@ auto SemanticsHandleMemberAccessExpression(SemanticsContext& context, return true; } +auto SemanticsHandlePointerMemberAccessExpression(SemanticsContext& context, + ParseTree::Node parse_node) + -> bool { + return context.TODO(parse_node, "HandlePointerMemberAccessExpression"); +} + auto SemanticsHandleName(SemanticsContext& context, ParseTree::Node parse_node) -> bool { auto name_str = context.parse_tree().GetNodeText(parse_node);