Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[parser/spec] ~,- and await inconsistency on function expression literals. #3750

Open
modulovalue opened this issue Mar 17, 2024 · 2 comments

Comments

@modulovalue
Copy link

There appears to be an inconsistency wrt unary prefix -, ~ and await operators on function expression literals between the implementation and DSP.

Consider:

void main() {
  -() => 0;
}
 === pkg:analyzer (https://pub.dev/packages/analyzer) ===
Parsing succeeded with no errors.
Scan errors: 0
Parse errors: 0
<CompilationUnitImpl> [0-27]
┗━ <FunctionDeclarationImpl> [0-27]
  ┣━ <NamedTypeImpl> [0-4]
  ┃  ┗━ 'void' [0-4]
  ┣━ 'main' [5-9]
  ┗━ <FunctionExpressionImpl> [9-27]
    ┣━ <FormalParameterListImpl> [9-11]
    ┃  ┣━ '(' [9-10]
    ┃  ┗━ ')' [10-11]
    ┗━ <BlockFunctionBodyImpl> [12-27]
      ┗━ <BlockImpl> [12-27]
        ┣━ '{' [12-13]
        ┣━ <ExpressionStatementImpl> [16-25]
        ┃  ┣━ <PrefixExpressionImpl> [16-24]
        ┃  ┃  ┣━ '-' [16-17]
        ┃  ┃  ┗━ <FunctionExpressionImpl> [17-24]
        ┃  ┃    ┣━ <FormalParameterListImpl> [17-19]
        ┃  ┃    ┃  ┣━ '(' [17-18]
        ┃  ┃    ┃  ┗━ ')' [18-19]
        ┃  ┃    ┗━ <ExpressionFunctionBodyImpl> [20-24]
        ┃  ┃      ┣━ '=>' [20-22]
        ┃  ┃      ┗━ <IntegerLiteralImpl> [23-24]
        ┃  ┃        ┗━ '0' [23-24]
        ┃  ┗━ ';' [24-25]
        ┗━ '}' [26-27]
--------------------------------------------------------------------------------
 === DSP (https://github.com/dart-lang/sdk/blob/master/tools/spec_parser/dart_spec_parser/Dart.g4) dspVersion v0.41 ===
Errors of type 1: [[@8,20:21='=>',<4>,2:6] Bad state: ]
Errors of type 2: []
<startSymbol>
┗━ <libraryDefinition>
  ┣━ <metadata>
  ┣━ <topLevelDefinition>
  ┃  ┣━ <functionSignature>
  ┃  ┃  ┣━ <type>
  ┃  ┃  ┃  ┗━ <typeNotFunction>
  ┃  ┃  ┃    ┗━ 'void'
  ┃  ┃  ┣━ <identifier>
  ┃  ┃  ┃  ┗━ 'main'
  ┃  ┃  ┗━ <formalParameterPart>
  ┃  ┃    ┗━ <formalParameterList>
  ┃  ┃      ┣━ '('
  ┃  ┃      ┗━ ')'
  ┃  ┗━ <functionBody>
  ┃    ┗━ <block>
  ┃      ┣━ '{'
  ┃      ┣━ <statements>
  ┃      ┃  ┣━ <statement>
  ┃      ┃  ┃  ┗━ <nonLabelledStatement>
  ┃      ┃  ┃    ┗━ <expressionStatement>
  ┃      ┃  ┃      ┣━ <expression>
  ┃      ┃  ┃      ┃  ┗━ <conditionalExpression>
  ┃      ┃  ┃      ┃    ┗━ <ifNullExpression>
  ┃      ┃  ┃      ┃      ┗━ <logicalOrExpression>
  ┃      ┃  ┃      ┃        ┗━ <logicalAndExpression>
  ┃      ┃  ┃      ┃          ┗━ <equalityExpression>
  ┃      ┃  ┃      ┃            ┗━ <relationalExpression>
  ┃      ┃  ┃      ┃              ┗━ <bitwiseOrExpression>
  ┃      ┃  ┃      ┃                ┗━ <bitwiseXorExpression>
  ┃      ┃  ┃      ┃                  ┗━ <bitwiseAndExpression>
  ┃      ┃  ┃      ┃                    ┗━ <shiftExpression>
  ┃      ┃  ┃      ┃                      ┗━ <additiveExpression>
  ┃      ┃  ┃      ┃                        ┗━ <multiplicativeExpression>
  ┃      ┃  ┃      ┃                          ┗━ <unaryExpression>
  ┃      ┃  ┃      ┃                            ┣━ <prefixOperator>
  ┃      ┃  ┃      ┃                            ┃  ┗━ <minusOperator>
  ┃      ┃  ┃      ┃                            ┃    ┗━ '-'
  ┃      ┃  ┃      ┃                            ┗━ <unaryExpression>
  ┃      ┃  ┃      ┃                              ┗━ <postfixExpression>
  ┃      ┃  ┃      ┃                                ┗━ <primary>
  ┃      ┃  ┃      ┃                                  ┗━ <literal>
  ┃      ┃  ┃      ┃                                    ┗━ <recordLiteral>
  ┃      ┃  ┃      ┃                                      ┗━ <recordLiteralNoConst>
  ┃      ┃  ┃      ┃                                        ┣━ '('
  ┃      ┃  ┃      ┃                                        ┗━ ')'
  ┃      ┃  ┃      ┗━ '=>'
  ┃      ┃  ┗━ <statement>
  ┃      ┃    ┗━ <nonLabelledStatement>
  ┃      ┃      ┗━ <expressionStatement>
  ┃      ┃        ┣━ <expression>
  ┃      ┃        ┃  ┗━ <conditionalExpression>
  ┃      ┃        ┃    ┗━ <ifNullExpression>
  ┃      ┃        ┃      ┗━ <logicalOrExpression>
  ┃      ┃        ┃        ┗━ <logicalAndExpression>
  ┃      ┃        ┃          ┗━ <equalityExpression>
  ┃      ┃        ┃            ┗━ <relationalExpression>
  ┃      ┃        ┃              ┗━ <bitwiseOrExpression>
  ┃      ┃        ┃                ┗━ <bitwiseXorExpression>
  ┃      ┃        ┃                  ┗━ <bitwiseAndExpression>
  ┃      ┃        ┃                    ┗━ <shiftExpression>
  ┃      ┃        ┃                      ┗━ <additiveExpression>
  ┃      ┃        ┃                        ┗━ <multiplicativeExpression>
  ┃      ┃        ┃                          ┗━ <unaryExpression>
  ┃      ┃        ┃                            ┗━ <postfixExpression>
  ┃      ┃        ┃                              ┗━ <primary>
  ┃      ┃        ┃                                ┗━ <literal>
  ┃      ┃        ┃                                  ┗━ <numericLiteral>
  ┃      ┃        ┃                                    ┗━ '0'
  ┃      ┃        ┗━ ';'
  ┃      ┗━ '}'
  ┗━ '<EOF>'

and

final a = ~() => 0;
 === pkg:analyzer (https://pub.dev/packages/analyzer) ===
Parsing succeeded with no errors.
Scan errors: 0
Parse errors: 0
<CompilationUnitImpl> [0-19]
┗━ <TopLevelVariableDeclarationImpl> [0-19]
  ┣━ <VariableDeclarationListImpl> [0-18]
  ┃  ┣━ 'final' [0-5]
  ┃  ┗━ <VariableDeclarationImpl> [6-18]
  ┃    ┣━ 'a' [6-7]
  ┃    ┣━ '=' [8-9]
  ┃    ┗━ <PrefixExpressionImpl> [10-18]
  ┃      ┣━ '~' [10-11]
  ┃      ┗━ <FunctionExpressionImpl> [11-18]
  ┃        ┣━ <FormalParameterListImpl> [11-13]
  ┃        ┃  ┣━ '(' [11-12]
  ┃        ┃  ┗━ ')' [12-13]
  ┃        ┗━ <ExpressionFunctionBodyImpl> [14-18]
  ┃          ┣━ '=>' [14-16]
  ┃          ┗━ <IntegerLiteralImpl> [17-18]
  ┃            ┗━ '0' [17-18]
  ┗━ ';' [18-19]
--------------------------------------------------------------------------------
 === DSP (https://github.com/dart-lang/sdk/blob/master/tools/spec_parser/dart_spec_parser/Dart.g4) dspVersion v0.41 ===
Errors of type 1: [[@6,14:15='=>',<4>,1:14] Bad state: ]
Errors of type 2: []
<startSymbol>
┗━ <libraryDefinition>
  ┣━ <metadata>
  ┣━ <topLevelDefinition>
  ┃  ┣━ 'final'
  ┃  ┣━ <staticFinalDeclarationList>
  ┃  ┃  ┗━ <staticFinalDeclaration>
  ┃  ┃    ┣━ <identifier>
  ┃  ┃    ┃  ┗━ 'a'
  ┃  ┃    ┣━ '='
  ┃  ┃    ┗━ <expression>
  ┃  ┃      ┗━ <conditionalExpression>
  ┃  ┃        ┗━ <ifNullExpression>
  ┃  ┃          ┗━ <logicalOrExpression>
  ┃  ┃            ┗━ <logicalAndExpression>
  ┃  ┃              ┗━ <equalityExpression>
  ┃  ┃                ┗━ <relationalExpression>
  ┃  ┃                  ┗━ <bitwiseOrExpression>
  ┃  ┃                    ┗━ <bitwiseXorExpression>
  ┃  ┃                      ┗━ <bitwiseAndExpression>
  ┃  ┃                        ┗━ <shiftExpression>
  ┃  ┃                          ┗━ <additiveExpression>
  ┃  ┃                            ┗━ <multiplicativeExpression>
  ┃  ┃                              ┗━ <unaryExpression>
  ┃  ┃                                ┣━ <prefixOperator>
  ┃  ┃                                ┃  ┗━ <tildeOperator>
  ┃  ┃                                ┃    ┗━ '~'
  ┃  ┃                                ┗━ <unaryExpression>
  ┃  ┃                                  ┗━ <postfixExpression>
  ┃  ┃                                    ┗━ <primary>
  ┃  ┃                                      ┗━ <literal>
  ┃  ┃                                        ┗━ <recordLiteral>
  ┃  ┃                                          ┗━ <recordLiteralNoConst>
  ┃  ┃                                            ┣━ '('
  ┃  ┃                                            ┗━ ')'
  ┃  ┣━ '=>'
  ┃  ┣━ '0'
  ┃  ┗━ ';'
  ┗━ '<EOF>'

and

void a() async {
  await () => 0;    
}
 === pkg:analyzer (https://pub.dev/packages/analyzer) ===
Parsing succeeded with no errors.
Scan errors: 0
Parse errors: 0
<CompilationUnitImpl> [0-39]
┗━ <FunctionDeclarationImpl> [0-39]
  ┣━ <NamedTypeImpl> [0-4]
  ┃  ┗━ 'void' [0-4]
  ┣━ 'a' [5-6]
  ┗━ <FunctionExpressionImpl> [6-39]
    ┣━ <FormalParameterListImpl> [6-8]
    ┃  ┣━ '(' [6-7]
    ┃  ┗━ ')' [7-8]
    ┗━ <BlockFunctionBodyImpl> [9-39]
      ┣━ 'async' [9-14]
      ┗━ <BlockImpl> [15-39]
        ┣━ '{' [15-16]
        ┣━ <ExpressionStatementImpl> [19-33]
        ┃  ┣━ <AwaitExpressionImpl> [19-32]
        ┃  ┃  ┣━ 'await' [19-24]
        ┃  ┃  ┗━ <FunctionExpressionImpl> [25-32]
        ┃  ┃    ┣━ <FormalParameterListImpl> [25-27]
        ┃  ┃    ┃  ┣━ '(' [25-26]
        ┃  ┃    ┃  ┗━ ')' [26-27]
        ┃  ┃    ┗━ <ExpressionFunctionBodyImpl> [28-32]
        ┃  ┃      ┣━ '=>' [28-30]
        ┃  ┃      ┗━ <IntegerLiteralImpl> [31-32]
        ┃  ┃        ┗━ '0' [31-32]
        ┃  ┗━ ';' [32-33]
        ┗━ '}' [38-39]
--------------------------------------------------------------------------------
 === DSP (https://github.com/dart-lang/sdk/blob/master/tools/spec_parser/dart_spec_parser/Dart.g4) dspVersion v0.41 ===
Errors of type 1: [[@6,19:23='await',<108>,2:2] Bad state: , [@6,19:23='await',<108>,2:2] Bad state: ]
Errors of type 2: []
<startSymbol>
┗━ <libraryDefinition>
  ┣━ <metadata>
  ┣━ <topLevelDefinition>
  ┃  ┣━ <functionSignature>
  ┃  ┃  ┣━ <type>
  ┃  ┃  ┃  ┗━ <typeNotFunction>
  ┃  ┃  ┃    ┗━ 'void'
  ┃  ┃  ┣━ <identifier>
  ┃  ┃  ┃  ┗━ 'a'
  ┃  ┃  ┗━ <formalParameterPart>
  ┃  ┃    ┗━ <formalParameterList>
  ┃  ┃      ┣━ '('
  ┃  ┃      ┗━ ')'
  ┃  ┗━ <functionBody>
  ┃    ┣━ 'async'
  ┃    ┗━ <block>
  ┃      ┣━ '{'
  ┃      ┣━ <statements>
  ┃      ┃  ┣━ <statement>
  ┃      ┃  ┃  ┗━ <nonLabelledStatement>
  ┃      ┃  ┣━ <statement>
  ┃      ┃  ┃  ┗━ <nonLabelledStatement>
  ┃      ┃  ┃    ┗━ 'await'
  ┃      ┃  ┗━ <statement>
  ┃      ┃    ┗━ <nonLabelledStatement>
  ┃      ┃      ┗━ <expressionStatement>
  ┃      ┃        ┣━ <expression>
  ┃      ┃        ┃  ┗━ <functionExpression>
  ┃      ┃        ┃    ┣━ <formalParameterPart>
  ┃      ┃        ┃    ┃  ┗━ <formalParameterList>
  ┃      ┃        ┃    ┃    ┣━ '('
  ┃      ┃        ┃    ┃    ┗━ ')'
  ┃      ┃        ┃    ┗━ <functionExpressionBody>
  ┃      ┃        ┃      ┣━ '=>'
  ┃      ┃        ┃      ┗━ <expression>
  ┃      ┃        ┃        ┗━ <conditionalExpression>
  ┃      ┃        ┃          ┗━ <ifNullExpression>
  ┃      ┃        ┃            ┗━ <logicalOrExpression>
  ┃      ┃        ┃              ┗━ <logicalAndExpression>
  ┃      ┃        ┃                ┗━ <equalityExpression>
  ┃      ┃        ┃                  ┗━ <relationalExpression>
  ┃      ┃        ┃                    ┗━ <bitwiseOrExpression>
  ┃      ┃        ┃                      ┗━ <bitwiseXorExpression>
  ┃      ┃        ┃                        ┗━ <bitwiseAndExpression>
  ┃      ┃        ┃                          ┗━ <shiftExpression>
  ┃      ┃        ┃                            ┗━ <additiveExpression>
  ┃      ┃        ┃                              ┗━ <multiplicativeExpression>
  ┃      ┃        ┃                                ┗━ <unaryExpression>
  ┃      ┃        ┃                                  ┗━ <postfixExpression>
  ┃      ┃        ┃                                    ┗━ <primary>
  ┃      ┃        ┃                                      ┗━ <literal>
  ┃      ┃        ┃                                        ┗━ <numericLiteral>
  ┃      ┃        ┃                                          ┗━ '0'
  ┃      ┃        ┗━ ';'
  ┃      ┗━ '}'
  ┗━ '<EOF>'

DSP rejects those operators in that position, but those operators are supported by the implementation:

void main() {
  -() => 0;
}

extension on Function {
  void operator -() {
    print("valid program");
  }
}
void main() {
  ~() => 0;
}

extension on Function {
  void operator ~() {
    print("valid program");
  }
}
void main() async {
  await () => 0;
  print("valid program");
}

Note: something similar can be observed with !, i.e. final a = !() => 0;, but, AFAIK, ! is never supported as an operator on function expression literals.

@eernstg
Copy link
Member

eernstg commented Mar 18, 2024

There appears to be an inconsistency wrt unary prefix -, ~ and await operators on function expression literals between the implementation and DSP.

This is presumably a consequence of the fact that the grammar in the language specification includes <functionExpression> as one of the alternatives of <primary>, and the grammar in Dart.g splits <functionExpression> into <functionPrimary> (which is only able to derive function literals whose body is a block: { ... }) and <functionExpression> (which is only able to derive function literals whose body starts with an arrow: =>), and derives the former from <primary> and the latter from <expression>. Presumably, the implementation uses the same approach as the language specification.

However, the rules in the language specification are highly ambiguous. In particular, an expression of the form (...) => e can typically be parsed in many different ways, because e may end in terms that may be applicable to the body of the function as well as to the function as a whole. For example: () => a.b.c may mean () => (a.b.c), (() => a.b).c, or (() => a).b.c.

I changed the approach in Dart.g when I created Dart.g (because the blatant ambiguity had to be handled, and also just because we ought to fix that issue), but it hasn't yet been accepted into the language specification.

With the approach in Dart.g, ~() => 0 is a syntax error (because <unaryExpression> cannot derive () => 0), and similarly for unary - and await. However, ~() { return 0; } would be fine, and so would -() { return 0; } and await () { return 0; }, as well as parenthesized versions of the => variants (~(() => 0), etc).

So I'd prefer to say that this issue will be resolved by fixing the ambiguity in the implementation and specification based on the approach in Dart.g.

@modulovalue
Copy link
Author

Related: #3508 (comment)

@eernstg eernstg transferred this issue from dart-lang/sdk May 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants