dart-lang / dart-lang/language

[specification] Implicitly induced private members from a different library

Open
#2,286 2 comments 0 reactions 0 assignees View on GitHub
question specification
Dominant language
TeX
Stars
2.9k
Forks
239
Avg merge
2d 18h
Merged PRs (30d)
14

Description

We specify that a mixin application has a semantics that corresponds to the creation of an [implicitly induced class](https://github.com/dart-lang/language/blob/812f939ec0130da119ad2cbe5f9a4121e31b0831/specification/dartLangSpec.tex#L5555) (let's call it `MixinApplication` here) whose members are the instance members of the mixin (and similarly for the case where we're using a class as a mixin).

We can do this in the case where the given member has a public name.

However, we can't just copy the entire declaration from the mixin/class, because any identifiers which are used in the declaration (including the type annotations in the header as well as the entire body) may refer to a different declaration than the one at the declaration of the mixin, or they may be undefined in the scope where the mixin application occurs.

Basically, we'd need to transform the mixin members into top-level functions or static members, similarly to the way we can desugar extension members, such that the name resolution for identifiers in those declarations is preserved. We would then implicitly induce some forwarding members in the scope where the mixin application occurs.

For the specification of the semantics of extension instance methods, we avoid mentioning the desugaring, they are specified to be executed with a specific binding of `this`, and there's no mention of moving code at all. For a mixin we do imply that the code is copied from the mixin to the class `MixinApplication` (with an adjustment concerned with `super`), so we'd at least need to spell out the semantics of mixin methods explicitly (in a way that is similar to the one we used with extension methods), in order to avoid the need to talk about moving code.

However, that isn't enough in the case where the mixin member has a private name.

We're actually relying on the "magic" ability of the compiler to generate code (or whatever it means to 'implicitly induce' a member) that contains identifiers which are taken to be private to a different library, and this of course implies that it cannot be regular source code.

```dart
// Library 'lib.dart'.

mixin M {
void _m() { print('_m running!'); }
}

void f(M m) {
m._m();
}

// Library 'main.dart'.
import 'lib.dart';

class C extends Object with M {}
/* Which means:
class OM extends Object implements M {
// 'private_to_lib_dart_m' stands for that magic identifier which is private to 'lib.dart'.
void private_to_lib_dart_m() => ...; // A forwarding call.
}
class C extends OM {}
*/

void main() {
f(C());
}
```

So far we've left this "magic" ability of the compiler/analyzer implicit (but implied). It is implemented, however, and we should specify the actual semantics more precisely.

A similar issue exists for noSuchMethod forwarding/throwing methods that are implicitly induced because they are 'forced by privacy'. In this case the header of the function could contain types or default values involving names that are private to a different library.

Yet another case is the copying of default values that we've specified for super parameters: They could include identifiers that are private to another library.

So how do we properly introduce this kind of magic into the language specification? We could introduce the notion of an identifier scheme which will serve this particular purpose, and simply state that a compiler/analyzer can create those, but they cannot occur in Dart source code. For instance, an identifier whose first character is a space would do (the lexer won't produce any of those), if it also contains enough information to be free of name clashes with other names that are private to other libraries. This would basically amount to the same thing as specifying Dart privacy in terms of name mangling, and I know that there's no particular enthusiasm for that in the language team. But I do think we need to clarify this topic area at some point, e.g., now. ;-)

@stereotype441, @natebosch, @lrhn, @munificent, @leafpetersen, @jakemac53, WDYT?

Contributor guide

Open the contributing guide

Research direction

Start with specification/dartLangSpec.tex at the linked implicitly induced class section around line 5555, then compare the specification of mixin members with extension instance methods. Trace the cases described for private members, noSuchMethod forwarding, and super-parameter default values. Done means the specification precisely defines how these implicitly induced declarations preserve private-name bindings without requiring impossible Dart source code.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.