Open eernstg opened 1 month ago
Yes, this came up recently in another issue as well. But we do generally want to allow top level and static methods with no body (as long as they are augmented with one).
Yes we do.
In a post augmentation world, a function declaration never needs to have a body, since one can be applied by a later augmenting function declaration.
The rules today are based on the declarations uniquely defining the semantic entity, and since a static function cannot be without a body (possibly external
), we didn't allow a declaration without a body.
With augmentations, only the final, fully augmented, function definition needs to have a body, no individual declaration or augmenting declaration needs to contain everything that is required of the final result.
I added some library member cases, they are also currently syntax errors, and the motivation for supporting them is similar to the case for static members with no implementation.
Sounds like we could have support for these generalizations in the language team. We would then need to update the grammar in several locations.
Are there any other places where we need to change something in the grammar, other than allowing ;
instead of a body for any non-local declaration with a body? Including constructors.
We may just need to put a ?
on `external'
for those, or change (`external' `static'?)?
to `external'? `static'?
.
(Do we have grammar that assumes that, fx, that a static final variable must have an initializer?)
Do we have grammar that assumes that, fx, that a static final variable must have an initializer?
Yes, that's currently a property of the specified grammar. The implementations may well treat the situation differently.
Could we restructure the grammar, like, completely? It's mildly annoying that the grammar for a function declaration doesn't have a name, it starts at the (<metadata> <memberDeclaration>)*
of fx <classDeclaration>
, and includes only two of the <memberDeclaration>
options. I could be easier to talk about this if the grammar matched the concepts more directly.
Consider:
<classDeclaration> ::= ... <memberDeclaration>* ...
<memberDeclaration> ::=
<functionDeclaration>
| <getterDeclaration>
| <setterDeclaration>
| <variableDeclaration>
| <constructorDeclaration>
<functionDeclaration> ::=
<metadata> `external`? `static`? <functionSignature> <functionBodyOpt>
<functionBodyOpt> ::= <functionBody> | `;`
<functionSignature> ::=
<type>? <functionName> <formalParameterPart>
<functionName> ::= <identifier> | `operator` <operator>
and then we can use the same <functionDeclaration>
everywhere, including top-level,
just by saying that it's a compile-time error to use static
if not occurring as a member, external
together with a <functionBody>
and an operator
-name if not occurring as a member or together with static
. (And now, not an immediate error if static
and not having a body.)
One declaration that contains all of what a function declaration is, and much easier to talk about, rather than having six different parse traces through a number of declarations that each correspond to a function declaration.
Not intended to change what a valid program is, just restructuring the specification to align with the concepts we need to talk about. (I'll be happy to write it.)
Maybe it's not so bad if I actually have to treat all the members anyway, iterating through all the possible productions, it's talking about a "function declaration" by itself that requires you to extract a sub-grammar from the language grammar for what the (<metadata> <classMember>)
actually matched when it matches a function declaration.
I think we can focus on how to change the grammar and/or specification separately if and when we have a decision about the overall principle.
The overall principle here would be that the grammar supports unimplemented versions of all variable and function declarations, because the ones that can't be abstract can still be unimplemented and not an error now, due to augmentations.
If we do agree on this then I'm sure the generalization that uses the same <functionDeclaration>
everywhere will be an attractive approach.
The overall principle here would be that the grammar supports unimplemented versions of all variable and function declarations, because the ones that can't be abstract can still be unimplemented and not an error now, due to augmentations.
Yes, I agree.
Note that there is a certain overlap with https://github.com/dart-lang/language/issues/4060, which is also about the ability to omit "implementation" parts of a declaration, and providing them in augmentations.
Thanks to @sgrekhov for bringing up this topic! For an instance member we're allowed to use the existing syntax of an abstract declaration to omit the implementation, which is then provided by an augmentation:
A similar approach could be used with static members, except that it is currently a syntax error:
It seems reasonable to allow this. We do not have a terminology for this usage (the first declaration of
B.method
isn't abstract), but it is hardly appropriate to say that the first declaration ofA.method
is "abstract", either. So we'd need to talk about these declarations as unimplemented or something like that, and then we'll only be able to tell whether the effective declaration of a locally unimplemented member is actually abstract or not when we know more (it must be an instance member and it must be true that every augmentation of it is locally unimplemented).Another case which is similar is
final
library variables and library functions:These are currently syntax errors, but they seem to be motivated by the same considerations as the previous cases.
@dart-lang/language-team, do you wish to modify the grammar to allow unimplemented static members?
Edit, Aug 6: Added some library member cases, adjusted the title.