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

Do we allow static or library members whose implementation is omitted? #4008

Closed
eernstg opened this issue Aug 5, 2024 · 11 comments
Closed
Labels
augmentation-libraries question Further information is requested

Comments

@eernstg
Copy link
Member

eernstg commented Aug 5, 2024

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:

class A {
  void method(); // OK, implementation provided below.
  int i; // OK, "implementation" provided below.
}

augment class A {
  augment method() {}
  augment i = 1;
}

A similar approach could be used with static members, except that it is currently a syntax error:

class B {
  static void method(); // Syntax error!
  static final int i; // Syntax error!
}

augment class B {
  augment static void method() {}
  augment static final i = 1;
}

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 of A.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:

final int i; // OK because of the augmentation.
augment final i = 1;

void foo(int i); // OK because of the augmentation.
augment foo(i) => print(i);

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.

@jakemac53
Copy link
Contributor

jakemac53 commented Aug 5, 2024

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).

@lrhn
Copy link
Member

lrhn commented Aug 5, 2024

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.

@jakemac53 jakemac53 moved this from Todo to In Progress in Static Metaprogramming - design/prototype Aug 5, 2024
@eernstg eernstg changed the title Do we allow static members whose implementation is omitted? Do we allow static or library members whose implementation is omitted? Aug 6, 2024
@eernstg
Copy link
Member Author

eernstg commented Aug 6, 2024

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.

@eernstg
Copy link
Member Author

eernstg commented Aug 6, 2024

Sounds like we could have support for these generalizations in the language team. We would then need to update the grammar in several locations.

@lrhn
Copy link
Member

lrhn commented Aug 6, 2024

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?)

@eernstg
Copy link
Member Author

eernstg commented Aug 6, 2024

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.

@lrhn
Copy link
Member

lrhn commented Aug 6, 2024

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.

@eernstg
Copy link
Member Author

eernstg commented Aug 6, 2024

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.

@jakemac53
Copy link
Contributor

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.

@eernstg
Copy link
Member Author

eernstg commented Aug 27, 2024

Note that there is a certain overlap with #4060, which is also about the ability to omit "implementation" parts of a declaration, and providing them in augmentations.

@eernstg
Copy link
Member Author

eernstg commented Sep 28, 2024

Done in #4106 and #4109.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
augmentation-libraries question Further information is requested
Projects
Development

No branches or pull requests

3 participants