Skip to content

Commit 290abf2

Browse files
authored
Proposal for super-parameters
Initial strawman proposal, for #1855.
1 parent 292b9cc commit 290abf2

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Dart Super-Initializer Parameters
2+
3+
Author: [email protected]<br>Version: 1.0
4+
5+
## Background and Motivation
6+
7+
This document specifies a language feature which allows concise propagation of parameters of a non-redirecting generative constructor to the superclass constructor it invokes.
8+
9+
Currently a “forwarding constructor”, one which does nothing except forward parameters to its superclass constructor (like the constructors introduced by mixin application), has to repeat the names of parameters when passing them to the superclass constructor. This becomes extra egregious when the parameter is named.
10+
11+
Example:
12+
13+
```dart
14+
class C extends D {
15+
C(int someMeaningfulName, {int? anotherName})
16+
: super(someMeaningfulName, anotherName: anotherName);
17+
}
18+
```
19+
20+
This example repeats `someMeaningfulName` once and `anotherName` twice.
21+
22+
We’ll introduce a short-hand syntax, similar to the `this.name` initializing formal parameter, which implicitly forwards the parameter directly to the superclass constructor.
23+
24+
## Feature specification
25+
26+
### Grammar
27+
28+
Like we currently allow <code>this.*id*</code> as an initializing formal in a non-redirecting generative constructor, with an implicit type derived from the *id* variable declaration, and introducing a final variable in the initializer list scope, we will also allow <code>super.*id*</code>.
29+
30+
We extend the grammar to:
31+
32+
```ebnf
33+
<normalFormalParameterNoMetadata> ::= <functionFormalParameter>
34+
\alt <fieldFormalParameter>
35+
\alt <simpleFormalParameter>
36+
\alt <superFormalParameter> ## new
37+
38+
<fieldFormalParameter> ::= \gnewline{}
39+
<finalConstVarOrType>? \THIS{} `.' <identifier> (<formalParameterPart> `?'?)?
40+
41+
<superFormalParameter> ::= \gnewline{} ## new
42+
<finalConstVarOrType>? \SUPER{} `.' <identifier> (<formalParameterPart> `?'?)? ## new
43+
```
44+
45+
_That is, exactly the same grammar as initializing formals, but with `super` instead of `this`._
46+
47+
## Semantics
48+
49+
It’s a compile-time error if a super-parameter in any declaration other than a non-redirecting generative constructor.
50+
51+
It’s a compile-time error if `var` occurs as the first token of a `<superFormalParameter>` production. (It’s generally a compile-time error if `const` or `late` occurs in a parameter declaration, this also applies to super-parameters).
52+
53+
We then treat each positional super-parameter as if it was an implicit positional argument to the super-constructor invocation at the end of the initializer list (which is `super()` if not explicitly specified), appended to any existing positional arguments in source order, and each named super-parameter as if it was an implicit named argument to the super-constructor invocation with the same name and value. If this would be invalid, the constructor is invalid.
54+
Further, the super-parameter also introduces a final variable with the same name and value into the initializer-list scope, just like initializing formals do.
55+
56+
#### More formally
57+
58+
##### Definitions
59+
60+
We define the *name* of a parameter declaration as the identifier naming it for normal parameters, and the identifier after `this.` or `super.` for initializing formals and super parameters. _The obvious definition, just stating it._ It’s a compile-time error if a function has two parameter declarations with the same name.
61+
62+
Let *C* be a non-redirecting generative constructor with super-constructor invocation *s* at the end of its initializer list (if none is written, it’s implicitly `super()`). Let *D* be the superclass constructor targeted by *s*.
63+
64+
We define the _associated super-constructor parameter_ for each super-parameter *p* of *C* as follows:
65+
66+
- If *p* is a positional parameter, let *k* be the number of positional arguments of *s* and let *j* be the number of positional super-parameters of *C* up to and including *p* in source order. The associated super-constructor parameter of *p* is the *k*+*j*‘th positional parameter of *D*, if *D* has that many positional parameters
67+
- If *p* is a named parameter with name *n*, the associated super-constructor parameter is the named parameter of *D* with name *n*, if *D* has a named parameter with that name.
68+
69+
It’s a **compile-time error** if a non-redirecting generative constructor has a super-parameter with no associated super-constructor parameter.
70+
71+
_All we need to for this definition is the ability to resolve the superclass constructor and see its argument structure._
72+
73+
##### Type inference
74+
75+
We define the *type* of a parameter declaration, *p*, of a non-redirecting generative constructor, *C*, as:
76+
77+
- If the parameter has a type in its `<finalConstVarOrType>`, that’s the type of the parameter.
78+
- If the parameter is an initializing formal (`this.name`) the type of the parameter is the declared/inferred type of the instance variable named `name` of the surrounding class (which must exist, otherwise it’s a compile-time error.)
79+
- If the parameter is a positional super parameter (`super.name`), the type of the parameter is the associated super-constructor parameter (which must exist, otherwise it’s a compile-time error).
80+
81+
Each super-parameter introduces a final variable with the same name and type into the initializer list scope (just like initializing formals).
82+
83+
When inferring the super-constructor invocation, *s*, targeting the super constructor *D*, we include the implicit super-parameters from the constructor parameter list:
84+
85+
- Let *k* be the number of positional arguments of *s*.
86+
- Let *j* be the number of positional super-parameters of *C*.
87+
- It’s a compile-time error if *D* has fewer than *k*+*j* positional parameters. _(Redundant with “all super parameters must have associated super-constructor parameter”.)_
88+
- It’s a compile-time error if *D* has more than *k*+*j* *required* positional parameters.
89+
- For 0 &le; *i* < *k*, it’s a compile-time error if the static type of the *k*’th positional argument of *s* is not assignable to the *k*‘th positional parameter of *D*. _(We use *assignable* here, which means that we do allow implicit coercions like downcast or `.call` method tear-off)_.
90+
- For 1 &le; i &le; j, it’s a compile-time error if the type of the (1-based) *i*‘th positional super-parameter of *C* is not assignable to the *k*+*i*‘th positional parameter of *D*.
91+
- It’s a compile-time error if *D* has a required named parameter named *n*, *s* does not have a named argument named *n* and *C* does not have a named super-parameter named *n*.
92+
- It’s a compile-time error *s* has a named argument *a* named *n* and *D* does not have a named parameter named *n* *or* *D* does have a named parameter *q* named *n* and the type of *a* is not assignable to the type of *q*.
93+
- It’s a compile-time error if *C* has a named super-parameter *p* with name *n* and the type of *p* is not assignable to the type if its corresponding super-constructor parameter of *D*. (We know it exists.)
94+
95+
##### Invocation
96+
97+
When invoking a non-redirecting generative constructor *C*, parameter binding occurs as follows:
98+
99+
- As usual for non-super-parameters.
100+
- Binding a value *v* to a super-parameter *p* with name *n*:
101+
- Binds the final variable *n* to *v* in the run-time initializer list scope.
102+
- Binds a fresh variable <code>_$*n*</code> to *v* in the run-time initializer list scope as well.
103+
104+
When reaching the super-constructor invocation, *s*, targeting the super-constructor *D*, the argument list passed to *D* is:
105+
106+
- The positional arguments of *s*,
107+
- followed by the values of each of the positional super-parameters of *C* as positional arguments, in source order (can be referenced without risk of being shadowed as <code>_$*n*</code>),
108+
- followed by the named arguments of *s*,
109+
- followed by named arguments corresponding to each named super-parameter of *C* with the same name as the parameter and the value of that parameter (a super-parameter named *n* has the associated argument <Code>*n*: _$*n*</code>).
110+
111+
##### Desugaring
112+
113+
This was specified without trying to desugar into existing valid Dart code.
114+
115+
We *can* desugar the desired behavior into existing Dart code, but that requires some amount of rewriting in the constructor body to enforce the “initializer-only” scope of variables introduced by initializing formals and super parameters. Example:
116+
117+
```dart
118+
C(super.x, this.y, {required super.z}) : super.foo() {
119+
something(x, y, z);
120+
}
121+
```
122+
123+
*could* be (re)written as:
124+
125+
```dart
126+
C(final TypeOfX x, final TypeOfY y, {final required TypeOfZ z})
127+
: this.y = y, super(x, z: z) {
128+
something(this.x, this,y, this.z);
129+
}
130+
```
131+
132+
The change of `x, y, z` to `this.x, this.y, this.z` is necessary to ensure the body code still references the instance variables, not the newly-introduced “normal” parameters which are visible in the body unlike the variables introduced by initializing formals and, now, super-parameters.
133+
134+
We so far prefer to avoid doing that kind of non-local rewriting, which means that we will need to treat this as a feature by itself, not something that can easily be “lowered” to existing code.
135+
136+
## Examples
137+
138+
```dart
139+
class B {
140+
final int foo;
141+
final int bar;
142+
final int baz;
143+
B(this.foo, this.bar, [this.baz = 4]);
144+
}
145+
class C extends B {
146+
C(super.foo, super.bar, [super.baz = 4]);
147+
}
148+
```
149+
150+
This shows that you still can’t just forward *every* parameter, you have to write each parameter out. You avoid having to write it *again* in the super-invocation, but have to write and look at the `super.` instead.
151+
152+
## Revisions
153+
154+
1.0: Initial version

0 commit comments

Comments
 (0)