dart-lang / language

Design of the Dart language
Other
2.67k stars 205 forks source link

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

Open modulovalue opened 8 months ago

modulovalue commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

Related: https://github.com/dart-lang/language/issues/3508#issuecomment-1850340366