Skip to content

Commit edfefb4

Browse files
committed
Many improvements, based on reviews of both documents
1 parent 227b140 commit edfefb4

File tree

2 files changed

+200
-124
lines changed

2 files changed

+200
-124
lines changed

working/0723-static-extensions/feature-specification-variant1.md

Lines changed: 157 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,27 @@ constructors in the static extension must return an object whose type is
6868
For example:
6969

7070
```dart
71-
// Omit extension type parameters when the static extension only has static
72-
// members. Type parameters are just noise for static members, anyway.
71+
// Omit extension type parameters when the static extension only
72+
// has static members. Type parameters are just noise for static
73+
// members, anyway.
7374
static extension E2 on Map {
7475
static Map<K2, V> castFromKey<K, V, K2>(Map<K, V> source) =>
7576
Map.castFrom<K, V, K2, V>(source);
7677
}
7778
78-
// Declare extension type parameters, to be used by constructors. The
79-
// type parameters can have stronger bounds than the on-declaration.
79+
// Declare extension type parameters, to be used by constructors.
80+
// The type parameters can have stronger bounds than the
81+
// on-declaration.
8082
static extension E3<K extends String, V> on Map<K, V> {
81-
factory Map.fromJson(Map<String, Object?> source) => Map.from(source);
83+
factory Map.fromJson(Map<String, Object?> source) =>
84+
Map.from(source);
8285
}
8386
8487
var jsonMap = <String, Object?>{"key": 42};
8588
var typedMap = Map<String, int>.fromJson(jsonMap);
86-
// `Map<int, int>.fromJson(...)` is an error: Violates the bound of `K`.
89+
90+
// But `Map<int, int>.fromJson(...)` is an error: It violates the
91+
// bound of `K`.
8792
```
8893

8994
Another motivation for this mechanism is that it supports constructors of
@@ -120,14 +125,14 @@ static extension E4<X> on Map<X, List<X>> {
120125
factory Map.listValue(X x) => {x: [x]};
121126
}
122127
123-
var int2intList = Map.listValue(1); // Inferred as `Map<int, List<int>>`.
128+
var int2intList = Map.listValue(1); // `Map<int, List<int>>`.
124129
// `Map<int, double>.listValue(...)` is an error.
125130
126131
static extension E6<Y> on Map<String, Y> {
127132
factory Map.fromString(Y y) => {y.toString(): y};
128133
}
129134
130-
var string2bool = Map.fromString(true); // Inferred as `Map<String, bool>`.
135+
var string2bool = Map.fromString(true); // `Map<String, bool>`.
131136
Map<String, List<bool>> string2listOfBool = Map.fromString([]);
132137
```
133138

@@ -147,7 +152,8 @@ The grammar is modified as follows:
147152
...;
148153
149154
<staticExtensionDeclaration> ::= // New rule.
150-
'static' 'extension' <identifier>? <typeParameters>? 'on' <type>
155+
'static' 'extension' <identifier>? <typeParameters>?
156+
'on' <type>
151157
'{' (<metadata> <staticExtensionMemberDeclaration>)* '}';
152158
153159
<staticExtensionMemberDeclaration> ::= // New rule.
@@ -168,9 +174,6 @@ The grammar is modified as follows:
168174
('final' | 'const') <type>? <staticFinalDeclarationList> |
169175
'late' 'final' <type>? <initializedIdentifierList> |
170176
'late'? <varOrType> <initializedIdentifierList>;
171-
172-
<constructorNameList> ::=
173-
<constructorName> (',' <constructorName>)*;
174177
```
175178

176179
In a static extension of the form `static extension E on C {...}` where `C`
@@ -257,7 +260,9 @@ constructor or a static member with the same basename as a constructor or a
257260
static member in the on-declaration of _D_.
258261

259262
*In other words, a static extension should not have name clashes with its
260-
on-declaration.*
263+
on-declaration. The warning above is aimed at static members and
264+
constructors, but a similar warning would probably be useful for name
265+
clashes with instance members as well.*
261266

262267
#### Static extension scopes
263268

@@ -278,10 +283,10 @@ type parameter scope, if any, and otherwise the library scope.
278283
Static members in a static extension are subject to the same static
279284
analysis as static members in other declarations.
280285

281-
A factory constructor in a static extension introduces scopes in the same
282-
way as other factory constructor declarations. The return type of the
283-
factory constructor is the constructor return type of the static
284-
extension *(that is, the type in the `on` clause)*.
286+
A constructor in a static extension introduces scopes in the same way as
287+
other constructor declarations. The return type of the constructor is the
288+
constructor return type of the static extension *(that is, the type in the
289+
`on` clause)*.
285290

286291
Type variables of a static extension `E` are in scope in static member
287292
declarations in `E`, but any reference to such type variables in a static
@@ -295,46 +300,95 @@ the current library, or if _D_ is imported and not hidden.
295300

296301
*In particular, it is accessible even in the case where there is a name
297302
clash with another locally declared or imported declaration with the same
298-
name.*
299-
300-
#### Invocation of a static member of a static extension
301-
302-
An _explicitly resolved invocation_ of a static member of a static
303-
extension named `E` is an expression of the form `E.m()` (or any other
304-
member access, *e.g., `E.m`, `E.m = e`, etc*), where `m` is a static member
305-
declared by `E`.
303+
name. This is also true if _D_ is imported with a prefix. Similarly, it is
304+
accessible even in the case where _D_ does not have a name, if it is
305+
declared in the current library.*
306+
307+
#### Invocation of a static member
308+
309+
*The language specification defines the notion of a _member invocation_ in
310+
the section [Member Invocations][], which is used below. This concept
311+
includes method invocations like `e.aMethod<int>(24)`, property extractions
312+
like `e.aGetter` or `e.aMethod` (tear-offs), operator invocations like
313+
`e1 + e2` or `aListOrNull?[1] = e`, function invocations like `f()`. Each
314+
of these expressions has a _syntactic receiver_ and an _associated member
315+
name_. With `e.aMethod<int>(24)`, the receiver is `e` and the associated
316+
member name is `aMethod`, with `e1 + e2` the receiver is `e1` and the
317+
member name is `+`, and with `f()` the receiver is `f` and the member name
318+
is `call`. Note that the syntactic receiver is a type literal in the case
319+
where the member invocation invokes a static member. In the following we
320+
will specify invocations of static members using this concept.*
321+
322+
[Member Invocations]: https://github.com/dart-lang/language/blob/94194cee07d7deadf098b1f1e0475cb424f3d4be/specification/dartLangSpec.tex#L13903
323+
324+
Consider an expression `e` which is a member invocation with syntactic
325+
receiver `E` and associated member name `m`, where `E` denotes a static
326+
extension and `m` is a static member declared by `E`. We say that `e` is an
327+
_explicitly resolved invocation_ of said static member of `E`.
306328

307329
*This can be used to invoke a static member of a specific static extension
308-
in order to manually resolve a name clash.*
309-
310-
A static member invocation on a class `C`, of the form `C.m()` (or any
311-
other member access), is resolved by looking up static members in `C` named
312-
`m` and looking up static members of every accessible static extension with
313-
on-declaration `C` and a member named `m`.
314-
315-
If `C` contains such a declaration then the expression is an invocation of
316-
that static member of `C`, with the same static analysis and dynamic
317-
semantics as before the introduction of this feature.
318-
319-
Otherwise, an error occurs if no declarations named `m` or more than one
320-
declaration named `m` were found. *They would necessarily be declared in
321-
static extensions.*
322-
323-
Otherwise, the invocation is resolved to the given static member
324-
declaration in a static extension named `Ej`, and the invocation is treated
325-
as `Ej.m()` *(this is an explicitly resolved invocation, which is specified
326-
above)*.
330+
in order to manually resolve a name clash.
331+
332+
Consider an expression `e` which is a member invocation with syntactic
333+
receiver `C` and an associated member name `m`, where `C` denotes a class
334+
and `m` is a static member declared by `C`. The static analysis and dynamic
335+
semantics of this expression is the same as in Dart before the introduction
336+
of this feature.
337+
338+
When `C` declares a static member whose basename is the basename of `m`,
339+
but `C` does not declare a static member named `m` or a constructor named
340+
`C.m`, a compile-time error occurs. *This is the same behavior as in
341+
pre-feature Dart. It's about "near name clashes" involving a setter.*
342+
343+
In the case where `C` does not declare any static members whose basename is
344+
the basename of `m`, and `C` does not declare any constructors named `C.m2`
345+
where `m2` is the basename of `m`, let _M_ be the set containing each
346+
accessible extension whose on-declaration is `C`, and whose static members
347+
include one with the name `m`, or which declares a constructor named `C.m`.
348+
349+
*If `C` does declare a constructor with such a name `C.m2` then the given
350+
expression is not a static member invocation. This case is described in a
351+
section below.*
352+
353+
Otherwise *(when `C` does not declare such a constructor)*, an error occurs
354+
if _M_ is empty or _M_ contains more than one member.
355+
356+
Otherwise *(when no error occurred)*, assume that _M_ contains exactly one
357+
element which is an extension `E` that declares a static member named
358+
`m`. The invocation is then treated as `E.m()` *(this is an explicitly
359+
resolved invocation, which is specified above)*.
360+
361+
Otherwise *(when `E` does not declare such a static member)*, _M_ will
362+
contain exactly one element which is a constructor named `C.m`. This is not
363+
a static member invocation, and it is specified in a section below.
364+
365+
In addition to these rules for invocations of static members of a static
366+
extension or a class, a corresponding set of rules exist for a static
367+
extension and the following: An enumerated declaration *(`enum ...`)*, a
368+
mixin class, a mixin, and an extension type. They only differ by being
369+
concerned with a different kind of on-declaration.
370+
371+
In addition to the member invocations specified above, it is also possible
372+
to invoke a static member of the enclosing declaration based on lexical
373+
lookup. This case is applicable when an expression in a class, enum, mixin
374+
or extension type resolves to an invocation of a static member of the
375+
enclosing declaration.
376+
377+
*This invocation will never invoke a static member of a static extension
378+
which is not the enclosing declaration. In other words, there is nothing
379+
new in this case.*
327380

328381
#### The instantiated constructor return type of a static extension
329382

330-
We associate a static extension declaration _D_ named `E` with formal type
331-
parameters `X1 extends B1 .. Xs extends Bs` and an actual type argument
332-
list `T1 .. Ts` with a type known as the _instantiated constructor return
333-
type of_ _D_ _with type arguments_ `T1 .. Ts`.
383+
Assume that _D_ is a generic static extension declaration named `E` with
384+
formal type parameters `X1 extends B1, ..., Xs extends Bs` and constructor
385+
return type `C<S1 .. Sk>`. Let `T1, ..., Ts` be a list of types. The
386+
_instantiated constructor return type_ of _D_ _with actual type arguments_
387+
`T1 .. Ts` is then the type `[T1/X1 .. Ts/Xs]C<S1 .. Sk>`.
334388

335-
When a static extension declaration _D_ named `E` has an on-clause which
336-
denotes a non-generic class `C`, the instantiated constructor return type
337-
is `C`, for any list of actual type arguments.
389+
*As a special case, assume that _D_ has an on-type which denotes a
390+
non-generic class `C`. In this case, the instantiated constructor return
391+
type is `C`, for any list of actual type arguments.*
338392

339393
*Note that such type arguments can be useful, in spite of the fact that
340394
they do not occur in the type of the newly created object. For example:*
@@ -352,25 +406,16 @@ static extension E<X> on A {
352406
void main() {
353407
// We can create an `A` "directly".
354408
A a = A(42);
355-
409+
356410
// We can also use a function to compute the `int`.
357411
a = A.computed('Hello!', (s) => s.length);
358412
}
359413
```
360414

361-
When a static extension declaration _D_ has no formal type parameters, and
362-
it has an on-type `C<S1 .. Sk>`, the instantiated constructor return type
363-
of _D_ is `C<S1 .. Sk>`.
364-
365-
*In this case the on-type is a fixed type (also known as a ground type),
366-
e.g., `List<int>`. This implies that the constructor return type of D is
367-
the same for every call site.*
368-
369-
Finally we have the general case: Consider a static extension declaration
370-
_D_ named `E` with formal type parameters `X1 extends B1 .. Xs extends Bs`
371-
and a constructor return type `C<S1 .. Sk>`. With actual type arguments
372-
`T1 .. Ts`, the instantiated constructor return type of _D_ with type
373-
arguments `T1 .. Ts` is `[T1/X1 .. Ts/Xs]C<S1 .. Sk>`.
415+
*As another special case, assume that _D_ has no formal type parameters,
416+
and it has a constructor return type of the form `C<S1 .. Sk>`. In this
417+
case the instantiated constructor return type of _D_ is `C<S1 .. Sk>`,
418+
which is a ground type, and it is the same for all call sites.*
374419

375420
#### Invocation of a constructor in a static extension
376421

@@ -391,7 +436,7 @@ information. In particular, the actual type arguments passed to the
391436
extension determines the actual type arguments passed to the class, which
392437
means that the explicitly resolved invocation typically has quite some
393438
redundancy (but it is very easy to check whether it is consistent, and it
394-
is an error if it is inconsistent). Every other form is reduced to the
439+
is an error if it is inconsistent). Every other form is reduced to this
395440
explicitly resolved form.*
396441

397442
A compile-time error occurs if the type arguments passed to `E` violate the
@@ -415,10 +460,10 @@ normalization occurs. *In other words, the types must be equal, not
415460
just mutual subtypes.*
416461

417462
*Note that explicitly resolved invocations of constructors declared in
418-
static extensions are an exception in real code, usable in the case where a
419-
name clash prevents an implicitly resolved invocation. However, implicitly
420-
resolved invocations are specified in the rest of this section by reducing
421-
them to explicitly resolved ones.*
463+
static extensions are a rare exception in real code, usable in the case
464+
where a name clash prevents an implicitly resolved invocation. However,
465+
implicitly resolved invocations are specified in the rest of this section
466+
by reducing them to explicitly resolved ones.*
422467

423468
A constructor invocation of the form `C<T1 .. Tm>.name(args)` is partially
424469
resolved by looking up a constructor named `C.name` in the class `C` and in
@@ -437,46 +482,65 @@ Otherwise, the invocation is partially resolved to a set of candidate
437482
constructors found in static extensions. Each of the candidates _kj_ is
438483
vetted as follows:
439484

440-
Assume that _kj_ is a constructor declared by a static extension _D_ named
441-
`E` with type parameters `X1 extends B1 .. Xs extends Bs` and on-type
442-
`C<S1 .. Sm>`. Find actual values `U1 .. Us` for `X1 .. Xs` satisfying the
443-
bounds `B1 .. Bs`, such that `([U1/X1 .. Us/Xs]C<S1 .. Sm>) == C<T1 .. Tm>`.
444-
If this fails then remove _kj_ from the set of candidate constructors.
445-
Otherwise note that _kj_ uses actual type arguments `U1 .. Us`.
485+
If `m` is zero and `E` is an accessible extension with on-declaration `C`
486+
that declares a static member whose basename is `name` then the invocation
487+
is a static member invocation *(which is specified in an earlier section)*.
488+
489+
Otherwise, assume that _kj_ is a constructor declared by a static extension
490+
_D_ named `E` with type parameters `X1 extends B1 .. Xs extends Bs`,
491+
on-declaration `C`, and on-type `C<S1 .. Sm>`. Find actual values
492+
`U1 .. Us` for `X1 .. Xs` satisfying the bounds `B1 .. Bs`, such that
493+
`([U1/X1 .. Us/Xs]C<S1 .. Sm>) == C<T1 .. Tm>`. This may determine the
494+
value of some of the actual type arguments `U1 .. Us`, and others may be
495+
unconstrained (because they do not occur in `C<T1 .. Tm>`). Actual type
496+
arguments corresponding to unconstrained type parameters are given as `_`
497+
(and they are subject to inference later on, where the types of the actual
498+
arguments `args` may influence their value). If this inference fails
499+
then remove _kj_ from the set of candidate constructors. Otherwise note
500+
that _kj_ uses actual type arguments `U1 .. Us`.
446501

447502
If all candidate constructors have been removed, or more than one candidate
448503
remains, a compile-time error occurs. Otherwise, the invocation is
449504
henceforth treated as `E<U1 .. Us>.C<T1 .. Tm>.name(args)` (respectively
450-
`E<U1 .. Us>.C<T1 .. Tm>(args)`).
505+
`E<U1 .. Us>.C<T1 .. Tm>(args)`). *This is an explicitly resolved static
506+
extension constructor invocation, which is specified above.*
451507

452508
A constructor invocation of the form `C.name(args)` (respectively
453509
`C(args)`) where `C` denotes a non-generic class is resolved in the
454-
same manner, with `m == 0`.
455-
456-
*In this case, type parameters declared by `E` may be inferred based on the
457-
constructor signature (similarly to the example with `A.computed` above),
458-
or they will be bound to values selected by instantiation to bound.*
510+
same manner, with `m == 0`.
459511

460512
Consider a constructor invocation of the form `C.name(args)` (and similarly
461513
for `C(args)`) where `C` denotes a generic class. As usual, the
462514
invocation is treated as in the pre-feature language when it denotes a
463515
constructor declared by the class `C`.
464516

465-
In the case where the context type schema for this invocation fully
466-
determines the actual type arguments of `C`, the expression is changed to
467-
receive said actual type arguments, `C<T1 .. Tm>.name(args)`, and treated
468-
as described above.
517+
In the case where the context type schema for this invocation
518+
determines some actual type arguments of `C`, the expression is changed to
519+
receive said actual type arguments, `C<T1 .. Tm>.name(args)` (where the
520+
unconstrained actual type arguments are given as `_` and inferred later).
521+
The expression is then treated as described above.
522+
523+
Next, we construct a set _M_ containing all accessible static extensions
524+
with on-declaration `C` that declare a constructor named `C.name`
525+
(respectively `C`).
469526

470-
In the case where the invocation resolves to exactly one constructor
471-
`C.name` (or `C`) declared by a static extension named `E`, the invocation
472-
is treated as `E.C.name(args)` (respectively `E.C(args)`).
527+
In the case where _M_ contains exactly one extension `E` that declares a
528+
constructor named `C.name` (respectively `C`), the invocation is treated as
529+
`E.C.name(args)` (respectively `E.C(args)`).
473530

474-
Otherwise, when there are two or more candidates from static extensions,
475-
an error occurs. *We do not wish to specify an approach whereby `args` is
531+
Otherwise, when there are two or more candidates from static extensions, an
532+
error occurs. *We do not wish to specify an approach whereby `args` is
476533
subject to type inference multiple times, and hence we do not support type
477534
inference for `C.name(args)` in the case where there are multiple distinct
478535
declarations whose signature could be used during the static analysis of
479-
that expression.*
536+
that expression. The workaround is to specify the actual type arguments
537+
explicitly.*
538+
539+
In addition to these rules for invocations of constructors of a static
540+
extension or a class, a corresponding set of rules exist for a static
541+
extension and the following: An enumerated declaration *(`enum ...`)*, a
542+
mixin class, a mixin, and an extension type. They only differ by being
543+
concerned with a different kind of declaration.
480544

481545
### Dynamic Semantics
482546

@@ -495,7 +559,7 @@ This fully determines the dynamic semantics of this feature.
495559

496560
### Changelog
497561

498-
1.1 - August 28, 2024
562+
1.1 - August 30, 2024
499563

500564
* Clarify many parts.
501565

0 commit comments

Comments
 (0)