Skip to content

Clarify spec of constructors which are induced implicitly by mixin application #769

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

Open
eernstg opened this issue Jan 6, 2020 · 1 comment
Labels
bug There is a mistake in the language specification or in an active document specification technical-debt Dealing with a part of the language which needs clarification or adjustments

Comments

@eernstg
Copy link
Member

eernstg commented Jan 6, 2020

Section 'Mixin Application' specifies mixin applications, including the mechanism whereby the resulting class contains generative constructors which are induced implicitly. This text has a couple of imprecise elements:

$MC$ is an undefined symbol, it should be $M$. Similarly, $C$ does not declare any instance variables should be $M$ does not declare any instance variables (the members declared in $M$ and in $C$ are the same, so let's stick to $M$ for consistency).

The case for optional positional parameters does not allow for missing default values, and the same problem exists for named parameters (this is a tiny fix, we usually just say something like "where $d_i$ may be omitted"). <fieldFormalParameter> (such as this.x) are never mentioned, but they can't occur, and we should mention that. The word fields is used, but not well-defined; it should be instance variables.

It is assumed that it is possible to write an expression $d'_i$ (which will be located in $L_C$, that is, in the same library where S with M is located) such that $d'_i$ will evaluate to the same object as $d_i$ (which occurs in the the library where $S$ is declared), and this may not be possible in Dart (because of privacy). So we need to deal with the fact that this code is somewhat "magical", in the sense that it is generated by the compiler/analyzer, and they are capable of creating expressions with this property (they can break the encapsulation provided by privacy).

The scoping, and in particular the treatment of private declarations in members, is unspecified. The text suggests that the declarations from $M$ (let's call that library $L_M$) are copied verbatim into $L_C$, in which case private declarations in $M$ will become private to $L_C$, and various scoping problems would arise. E.g., any use of a private identifier declared outside $M$ in the body of a declaration in $M$ would now be reinterpreted, and probably it would be an error.

So we'd need to say that name resolution is not affected by the operation whereby the members of $M$ are copied into $C$. Perhaps we could say that every member of a mixin is transformed into a top level function with a fresh name (not private), and $C$ contains methods whose body is just a forwarding call to such a top-level function. This will not address all issues, however, because it is still a problem that private mixin methods would be reinterpreted as private-in-$L_C$.

@eernstg eernstg added bug There is a mistake in the language specification or in an active document specification labels Jan 6, 2020
@lrhn
Copy link
Member

lrhn commented Jan 7, 2020

Missing default values can be treated as default values with the value null. There is a difference for instance methods, because of overridability, but the distinction should not matter for constructors. So, we can say that an omitted default value is treated as a default value of null (and/or the other way).

The ability to create "expressions" with the same value as a default value of a function in different library also occurs for implicit forwarding functions, so that is not new. We should be able to leverage the same approach here.

Privacy also depends on non-linguistic features that allow a method with a private name from one library to be found in a different library, without changing the name (it's still private to the former library). It's not syntax based, the moving happens at a point where _foo in one library is a different name than _foo in a different library, even if it's the same identifier. (So, post name-mangling).

I would prefer to not transform methods in any way, and especially not to top-level functions.
Transformations always either lose something or (worse) adds something which is unrelated to the actual operation.

So, what I propose we do is:

  • A class (separate from a class declaration) is a container of instance member declarations (and constructors). It also has a superclass (unless it's Object) and a set of implemented interfaces, one of which is its own interface.
  • You can do lookup of a name on a class. This results in a declaration and the class it was found in, or nothing/error. If the method is not declared in the class itself, this iterates the superclass chain until a member is found or we run out of superclasses at Object.
  • When we invoke an instance method, we find the method declaration and class that it's found on, the bind parameters and execute the body of the method in a context where this is bound to the current instance and the "current class" is bound to the class where the method was found. That's the class used to resolve super invocations.
  • Mixin application does not move anything at the source level. Instead it makes the instance method declarations of the mixin declaration findable on the mixin application class. That is, a lookup on the mixin application class checks the mixin decaration for methods instead of itself, before continuing to the superclass.

What happens is then that the method declaration of the mixin can be found by a lookup on the mixin application class, and we invoke that declaration with a this binding of the current object and a lookup-class which is the mixin application class. That is the class is used for super-invocations, which are dynamic invocations in mixin applications.

(Non-mixin instance methods can assume that they know their super statically and do super-lookups statically, mixin methods need to either be specialized or dynamic when doing super invocations).

The mixin method is still a method declared on the mixin declaration, and its lexical scope does not change.

@eernstg eernstg added the technical-debt Dealing with a part of the language which needs clarification or adjustments label Aug 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug There is a mistake in the language specification or in an active document specification technical-debt Dealing with a part of the language which needs clarification or adjustments
Projects
None yet
Development

No branches or pull requests

2 participants