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

Spec allows function literals in initializer list #11509

Closed
lrhn opened this issue Jun 25, 2013 · 14 comments
Closed

Spec allows function literals in initializer list #11509

lrhn opened this issue Jun 25, 2013 · 14 comments
Labels
area-specification (deprecated) Deprecated: use area-language and a language- label. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@lrhn
Copy link
Member

lrhn commented Jun 25, 2013

Example code:

class C {
 final int x;
 C() : x = (){ return 42; }();
}

main() {
  print(new C().x);
}

This errs with the message:
'file:..../initcall.dart': Error: line 3 pos 13: unexpected token ')'
 C() : x = (){ return 42; }();

The grammar allows the syntax.

@DartBot
Copy link

DartBot commented Jun 25, 2013

This comment was originally written by @mhausner


The grammar does allow it, you are right. Two years ago we discussed the same issue because having function literals in field initializer expressions leads to ambiguous code. Consider this:

class C {
  C(a) : x = (a) => a;
  ...

Is field x initialized to a function literal and the constructor has no body, or is field x initialized to (a) and => a; is the body of the constructor?

The old ANTLR grammar used to have a tie-braker rule that made it impossible to have function literals in initializer expressions (unless nested in parenthesis). I don't see such a rule in the spec now.

Gilad, have we made the conscious decision to allow function literals in initializer expressions, or is this an oversight in the current spec?


Set owner to @gbracha.

@gbracha
Copy link
Contributor

gbracha commented Jun 25, 2013

The grammar needs to be reviewed for such issues. This is a bug in the grammar.

@DartBot
Copy link

DartBot commented Jun 25, 2013

This comment was originally written by @mhausner


Issue #11508 has been merged into this issue.


cc @gbracha.

@peter-ahe-google
Copy link
Contributor

Is this "Area-Language"?

@peter-ahe-google
Copy link
Contributor

Issue #11508 has been merged into this issue.

@DartBot
Copy link

DartBot commented Jun 25, 2013

This comment was originally written by @mhausner


Maybe more Area-Spec it that existed.


Removed Area-VM label.
Added Area-Language label.
Changed the title to: "Spec allows function literals in initializer list".

@bwilkerson
Copy link
Member

Marked this as blocking #11828.

@lrhn
Copy link
Member Author

lrhn commented Dec 13, 2013

Issue #15626 has been merged into this issue.

@lrhn
Copy link
Member Author

lrhn commented Dec 13, 2013

If we disallow '=>' for the body of generative constructors, I think the grammar would no longer be ambiguous.
It's still hard to parse because you need arbitrary lookahead, e.g., for:
  C : x = (y) { long thing that could be a body } ; // ';' <- the real body.
but it's probably easier to specify than recursively disallowing undelimited function expressions in a conditionalExpression.

@kevmoo kevmoo removed the Type-Defect label Mar 1, 2016
@munificent munificent added area-specification (deprecated) Deprecated: use area-language and a language- label. and removed area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). labels Dec 16, 2016
@eernstg
Copy link
Member

eernstg commented Feb 6, 2018

No milestone now: This correction will be considered as part of a larger update of grammar related parts of the specification, based on Dart.g.

@eernstg
Copy link
Member

eernstg commented Jan 22, 2019

Cf. also #35674, which has just been closed as a duplicate of this one.

@modulovalue
Copy link
Contributor

Since it's been a while since the last comment, I just wanted to mention that this behavior can still be observed:

class C {
 final int x;
 C() : x = (){ return 42; }();
}

Analyzer and ANTLR parse trees:

 === Analyzer ===
Scan errors: 0
Parse errors: 1
file(51..51): Expected an identifier.
<CompilationUnitImpl> [0-56]
┗━ <ClassDeclarationImpl> [0-56]
  ┣━ 'class' [0-5]
  ┣━ 'C' [6-7]
  ┣━ '{' [8-9]
  ┣━ <FieldDeclarationImpl> [11-23]
  ┃  ┣━ <VariableDeclarationListImpl> [11-22]
  ┃  ┃  ┣━ 'final' [11-16]
  ┃  ┃  ┣━ <NamedTypeImpl> [17-20]
  ┃  ┃  ┃  ┗━ 'int' [17-20]
  ┃  ┃  ┗━ <VariableDeclarationImpl> [21-22]
  ┃  ┃    ┗━ 'x' [21-22]
  ┃  ┗━ ';' [22-23]
  ┣━ <ConstructorDeclarationImpl> [25-51]
  ┃  ┣━ <SimpleIdentifierImpl> [25-26]
  ┃  ┃  ┗━ 'C' [25-26]
  ┃  ┣━ <FormalParameterListImpl> [26-28]
  ┃  ┃  ┣━ '(' [26-27]
  ┃  ┃  ┗━ ')' [27-28]
  ┃  ┣━ ':' [29-30]
  ┃  ┣━ <ConstructorFieldInitializerImpl> [31-37]
  ┃  ┃  ┣━ <SimpleIdentifierImpl> [31-32]
  ┃  ┃  ┃  ┗━ 'x' [31-32]
  ┃  ┃  ┣━ '=' [33-34]
  ┃  ┃  ┗━ <RecordLiteralImpl> [35-37]
  ┃  ┃    ┣━ '(' [35-36]
  ┃  ┃    ┗━ ')' [36-37]
  ┃  ┗━ <BlockFunctionBodyImpl> [37-51]
  ┃    ┗━ <BlockImpl> [37-51]
  ┃      ┣━ '{' [37-38]
  ┃      ┣━ <ReturnStatementImpl> [39-49]
  ┃      ┃  ┣━ 'return' [39-45]
  ┃      ┃  ┣━ <IntegerLiteralImpl> [46-48]
  ┃      ┃  ┃  ┗━ '42' [46-48]
  ┃      ┃  ┗━ ';' [48-49]
  ┃      ┗━ '}' [50-51]
  ┣━ <MethodDeclarationImpl> [51-54]
  ┃  ┣━ '' [51-51]
  ┃  ┣━ <FormalParameterListImpl> [51-53]
  ┃  ┃  ┣━ '(' [51-52]
  ┃  ┃  ┗━ ')' [52-53]
  ┃  ┗━ <EmptyFunctionBodyImpl> [53-54]
  ┃    ┗━ ';' [53-54]
  ┗━ '}' [55-56]
--------------------------------------------------------------------------------
 === ANTLR ===
Errors of type 1: []
Errors of type 2: []
<startSymbol>
┗━ <libraryDefinition>
  ┣━ <metadata>
  ┣━ <topLevelDefinition>
  ┃  ┗━ <classDeclaration>
  ┃    ┣━ <classModifiers>
  ┃    ┣━ 'class'
  ┃    ┣━ <typeWithParameters>
  ┃    ┃  ┗━ <typeIdentifier>
  ┃    ┃    ┗━ 'C'
  ┃    ┣━ '{'
  ┃    ┣━ <metadata>
  ┃    ┣━ <classMemberDeclaration>
  ┃    ┃  ┣━ <declaration>
  ┃    ┃  ┃  ┣━ 'final'
  ┃    ┃  ┃  ┣━ <type>
  ┃    ┃  ┃  ┃  ┗━ <typeNotFunction>
  ┃    ┃  ┃  ┃    ┗━ <typeNotVoidNotFunction>
  ┃    ┃  ┃  ┃      ┗━ <typeName>
  ┃    ┃  ┃  ┃        ┗━ <typeIdentifier>
  ┃    ┃  ┃  ┃          ┗━ 'int'
  ┃    ┃  ┃  ┗━ <initializedIdentifierList>
  ┃    ┃  ┃    ┗━ <initializedIdentifier>
  ┃    ┃  ┃      ┗━ <identifier>
  ┃    ┃  ┃        ┗━ 'x'
  ┃    ┃  ┗━ ';'
  ┃    ┣━ <metadata>
  ┃    ┣━ <classMemberDeclaration>
  ┃    ┃  ┣━ <declaration>
  ┃    ┃  ┃  ┣━ <constructorSignature>
  ┃    ┃  ┃  ┃  ┣━ <constructorName>
  ┃    ┃  ┃  ┃  ┃  ┗━ <typeIdentifier>
  ┃    ┃  ┃  ┃  ┃    ┗━ 'C'
  ┃    ┃  ┃  ┃  ┗━ <formalParameterList>
  ┃    ┃  ┃  ┃    ┣━ '('
  ┃    ┃  ┃  ┃    ┗━ ')'
  ┃    ┃  ┃  ┗━ <initializers>
  ┃    ┃  ┃    ┣━ ':'
  ┃    ┃  ┃    ┗━ <initializerListEntry>
  ┃    ┃  ┃      ┗━ <fieldInitializer>
  ┃    ┃  ┃        ┣━ <identifier>
  ┃    ┃  ┃        ┃  ┗━ 'x'
  ┃    ┃  ┃        ┣━ '='
  ┃    ┃  ┃        ┗━ <initializerExpression>
  ┃    ┃  ┃          ┗━ <conditionalExpression>
  ┃    ┃  ┃            ┗━ <ifNullExpression>
  ┃    ┃  ┃              ┗━ <logicalOrExpression>
  ┃    ┃  ┃                ┗━ <logicalAndExpression>
  ┃    ┃  ┃                  ┗━ <equalityExpression>
  ┃    ┃  ┃                    ┗━ <relationalExpression>
  ┃    ┃  ┃                      ┗━ <bitwiseOrExpression>
  ┃    ┃  ┃                        ┗━ <bitwiseXorExpression>
  ┃    ┃  ┃                          ┗━ <bitwiseAndExpression>
  ┃    ┃  ┃                            ┗━ <shiftExpression>
  ┃    ┃  ┃                              ┗━ <additiveExpression>
  ┃    ┃  ┃                                ┗━ <multiplicativeExpression>
  ┃    ┃  ┃                                  ┗━ <unaryExpression>
  ┃    ┃  ┃                                    ┗━ <postfixExpression>
  ┃    ┃  ┃                                      ┣━ <primary>
  ┃    ┃  ┃                                      ┃  ┗━ <functionPrimary>
  ┃    ┃  ┃                                      ┃    ┣━ <formalParameterPart>
  ┃    ┃  ┃                                      ┃    ┃  ┗━ <formalParameterList>
  ┃    ┃  ┃                                      ┃    ┃    ┣━ '('
  ┃    ┃  ┃                                      ┃    ┃    ┗━ ')'
  ┃    ┃  ┃                                      ┃    ┗━ <functionPrimaryBody>
  ┃    ┃  ┃                                      ┃      ┗━ <block>
  ┃    ┃  ┃                                      ┃        ┣━ '{'
  ┃    ┃  ┃                                      ┃        ┣━ <statements>
  ┃    ┃  ┃                                      ┃        ┃  ┗━ <statement>
  ┃    ┃  ┃                                      ┃        ┃    ┗━ <nonLabelledStatement>
  ┃    ┃  ┃                                      ┃        ┃      ┗━ <returnStatement>
  ┃    ┃  ┃                                      ┃        ┃        ┣━ 'return'
  ┃    ┃  ┃                                      ┃        ┃        ┣━ <expression>
  ┃    ┃  ┃                                      ┃        ┃        ┃  ┗━ <conditionalExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃    ┗━ <ifNullExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃      ┗━ <logicalOrExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃        ┗━ <logicalAndExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃          ┗━ <equalityExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃            ┗━ <relationalExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃              ┗━ <bitwiseOrExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃                ┗━ <bitwiseXorExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃                  ┗━ <bitwiseAndExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃                    ┗━ <shiftExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃                      ┗━ <additiveExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃                        ┗━ <multiplicativeExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃                          ┗━ <unaryExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃                            ┗━ <postfixExpression>
  ┃    ┃  ┃                                      ┃        ┃        ┃                              ┗━ <primary>
  ┃    ┃  ┃                                      ┃        ┃        ┃                                ┗━ <literal>
  ┃    ┃  ┃                                      ┃        ┃        ┃                                  ┗━ <numericLiteral>
  ┃    ┃  ┃                                      ┃        ┃        ┃                                    ┗━ '42'
  ┃    ┃  ┃                                      ┃        ┃        ┗━ ';'
  ┃    ┃  ┃                                      ┃        ┗━ '}'
  ┃    ┃  ┃                                      ┗━ <selector>
  ┃    ┃  ┃                                        ┗━ <argumentPart>
  ┃    ┃  ┃                                          ┗━ <arguments>
  ┃    ┃  ┃                                            ┣━ '('
  ┃    ┃  ┃                                            ┗━ ')'
  ┃    ┃  ┗━ ';'
  ┃    ┗━ '}'
  ┗━ '<EOF>'

@modulovalue
Copy link
Contributor

Note: once this is resolved, it would probably make sense to also update the switch expression grammar since there's a similar ambiguity there, too. See: https://github.com/dart-lang/language/blob/main/accepted/3.0/patterns/feature-specification.md#function-expression-in-guard-ambiguity

@eernstg
Copy link
Member

eernstg commented Dec 11, 2023

Thanks for the heads-up!

The error which is reported for the original example today (DartPad, master) is a type error:

The initializer type '()' can't be assigned to the field type 'int'.

This implies that the text () { return 42; } is being parsed as an expression () (the empty record) followed by a constructor body, and the (); that follows is then parsed as the next class member declaration (that has its own syntax errors).

This is all in line with the expected behavior today. There is a rule that prevents function literals from being present in initializer elements. It has never been specified precisely, but that is being clarified now in #54284.

In any case, that rule was (or at least could have been) introduced in response to this issue. The initial message of this issue indicates that the grammar allowed this kind of construct, and it doesn't mention any special exceptions for initializer list elements. In other words, this issue was relevant for the language before that rule was introduced, which means Dart as of 2014 or so.

So we can close this one now, and rely on #54284 to clarify the rule that makes this issue obsolete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-specification (deprecated) Deprecated: use area-language and a language- label. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
Development

No branches or pull requests

9 participants