-
Notifications
You must be signed in to change notification settings - Fork 226
Update the declaring constructors feature specification #4524
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
# Declaring and Primary Constructors | ||
# Declaring Constructors | ||
|
||
Author: Erik Ernst | ||
|
||
Status: Accepted | ||
|
||
Version: 1.9 | ||
Version: 1.10 | ||
|
||
Experiment flag: declaring-constructors | ||
|
||
|
@@ -243,9 +243,9 @@ constructor parameter is `final`. | |
|
||
In the case where the declaration is an `extension type`, the modifier | ||
`final` on the representation variable can be specified or omitted. Note | ||
that an extension type declaration is specified to use a primary | ||
constructor (in that case there is no other choice, it is in the grammar | ||
rules): | ||
that an extension type declaration is specified to use a declaring | ||
constructor (it is not supported to declare the representation variable | ||
using a normal instance variable declaration): | ||
|
||
```dart | ||
// Using a primary constructor. | ||
|
@@ -619,6 +619,13 @@ constructors as well. | |
'const' <constructorName> <formalParameterList> | ||
| <declaringConstantConstructorSignature>; | ||
|
||
<constructorName> ::= // Modified rule. | ||
<typeIdentifierOrNew> ('.' identifierOrNew)?; | ||
|
||
<typeIdentifierOrNew> ::= // New rule. | ||
<typeIdentifier> | ||
| 'new'; | ||
|
||
<declaringConstantConstructorSignature> ::= // New rule. | ||
'const' 'this' ('.' <identifierOrNew>)? <declaringParameterList>; | ||
|
||
|
@@ -686,8 +693,8 @@ extension type declaration, together with a declaration in the body that | |
contains a `<declaringConstructorSignature>` *(which does not contain a | ||
`<declaringParameterList>`, because that's an error)*. | ||
|
||
A class declaration whose class body is `;` is treated as a class | ||
declaration whose class body is `{}`. | ||
A class or extension type declaration whose class body is `;` is treated as | ||
a declaration whose body is `{}`. | ||
|
||
Let _D_ be a class, extension type, or enum declaration. | ||
|
||
|
@@ -732,8 +739,19 @@ superclass.* | |
A compile-time error occurs if a `<defaultDeclaringNamedParameter>` has the | ||
modifier `required` as well as a default value. | ||
|
||
*Note that the updated grammar rule for `<constructorName>` allows | ||
non-declaring constructors to use `new` where the current rules require | ||
the class name. This is not a necessary part of the declaring constructors | ||
feature, but it contributes to the overall brevity of Dart programs.* | ||
|
||
|
||
### Static processing | ||
|
||
The ability to use `new` rather than the class name in declarations of | ||
ordinary (non-declaring) constructors is purely syntactic. The static | ||
analysis and meaning of such constructors is identical to the form that | ||
uses the class name. | ||
|
||
The name of a primary constructor of the form | ||
`'const'? id1 <typeParameters>? <declaringParameterList>` is `id1` *(that | ||
is, the same as the name of the class)*. | ||
|
@@ -874,12 +892,13 @@ reason for this error is that the modifier `covariant` must be specified on | |
the declaration of `v` which is known to exist, not on the parameter.* | ||
|
||
A compile-time error occurs if _p_ has both of the modifiers `covariant` | ||
and `final`. *A final instance variable cannot be covariant, because being | ||
covariant is a property of the setter.* | ||
and `final`, also if the latter is implicitly induced *(which can occur in a | ||
primary constructor of an extension type declaration)*. *A final instance | ||
variable cannot be covariant, because being covariant is a property of the | ||
setter.* | ||
|
||
A compile-time error occurs if _p_ has the modifier `covariant`, but | ||
neither `var` nor `final`. *This parameter does not induce an instance | ||
variable, so there is no setter.* | ||
not `var`. *This parameter does not induce a setter.* | ||
|
||
Conversely, it is not an error for the modifier `covariant` to occur on a | ||
declaring formal parameter _p_ of a declaring constructor. This extends the | ||
|
@@ -890,33 +909,36 @@ constructors. | |
|
||
The semantics of the declaring constructor is found in the following steps, | ||
where _D_ is the class, extension type, or enum declaration in the program | ||
that includes a declaring constructor, and _D2_ is the result of the | ||
that includes a declaring constructor _k_, and _D2_ is the result of the | ||
derivation of the semantics of _D_. The derivation step will delete | ||
elements that amount to the declaring constructor; it will add a new | ||
constructor _k_; and it will add zero or more instance variable | ||
elements that amount to the declaring constructor. Semantically, it will | ||
add a new constructor _k2_, and it will add zero or more instance variable | ||
declarations. | ||
|
||
*Adding program elements 'semantically' implies that this is not a source | ||
code transformation, it is a way to obtain semantic program elements that | ||
differ from the ones that are obtained from pre-feature declarations, but | ||
can be specified in terms of pre-feature declarations.* | ||
|
||
Where no processing is mentioned below, _D2_ is identical to _D_. Changes | ||
occur as follows: | ||
|
||
Assume that `p` is an optional formal parameter in _D_ which has the | ||
modifier `var` or the modifier `final` *(that is, `p` is a declaring | ||
parameter)*. | ||
|
||
Assume that the combined member signature for a getter with the same name | ||
as `p` from the superinterfaces of _D_ exists, and has return type `T`. In | ||
that case the parameter `p` has declared type `T` as well. | ||
|
||
*In other words, an instance variable introduced by a declaring parameter | ||
is subject to override inference, just like an explicitly declared instance | ||
variable.* | ||
|
||
Otherwise, assume that `p` does not have a declared type, but it does have | ||
a default value whose static type in the empty context is a type (not a | ||
type schema) `T` which is not `Null`. In that case `p` is considered to | ||
have the declared type `T`. When `T` is `Null`, `p` is considered to have | ||
the declared type `Object?`. If `p` does not have a declared type nor a | ||
default value then `p` is considered to have the declared type `Object?`. | ||
Let `p` be a formal parameter in _k_ which has the modifier `var` or the | ||
modifier `final` *(that is, `p` is a declaring parameter)*. | ||
|
||
Consider the situation where `p` has no type annotation: | ||
- if combined member signature for a getter with the same name as `p` from | ||
the superinterfaces of _D_ exists and has return type `T`, the parameter | ||
`p` has declared type `T`. If no such getter exists, but a setter with | ||
the same basename exists, with a formal parameter whose type is `T`, the | ||
parameter `p` has declared type `T`. *In other words, an instance | ||
variable introduced by a declaring parameter is subject to override | ||
inference, just like an explicitly declared instance variable.* | ||
- otherwise, if `p` is optional and has a default value whose static type | ||
in the empty context is a type `T` which is not `Null` then `p` has | ||
declared type `T`. When `T` is `Null`, `p` has declared type `Object?`. | ||
- otherwise, if `p` does not have a default value then `p` has declared | ||
type `Object?`. | ||
|
||
*Dart has traditionally assumed the type `dynamic` in such situations. We | ||
have chosen the more strictly checked type `Object?` instead, in order to | ||
|
@@ -932,25 +954,25 @@ this by specifying the current scope explicitly as the body scope, in spite | |
of the fact that the declaring constructor is actually placed outside the | ||
braces that delimit the class body.* | ||
|
||
Next, _k_ has the modifier `const` iff the keyword `const` occurs just | ||
Next, _k2_ has the modifier `const` iff the keyword `const` occurs just | ||
before the name of _D_ or before `this`, or if _D_ is an `enum` | ||
declaration. | ||
|
||
Consider the case where _D_ is a declaring header constructor. If the name | ||
Consider the case where _k_ is a declaring header constructor. If the name | ||
`C` in _D_ and the type parameter list, if any, is followed by `.id` where | ||
`id` is an identifier then _k_ has the name `C.id`. If it is followed by | ||
`.new` then _k_ has the name `C`. If it is not followed by `.` then _k_ | ||
has the name `C`. If it exists, _D2_ omits the part derived from | ||
`'.' <identifierOrNew>` that follows the name and type parameter list, if | ||
any, in _D_. Moreover, _D2_ omits the formal parameter list _L_ that | ||
follows the name, type parameter list, if any, and `.id`, if any. | ||
`id` is an identifier then _k2_ has the name `C.id`. If it is followed by | ||
`.new` then _k2_ has the name `C`. If it is not followed by `.` then _k2_ | ||
has the name `C`. _D2_ omits the part derived from `'.' <identifierOrNew>` | ||
that follows the name and type parameter list in _D_, if said part exists. | ||
Moreover, _D2_ omits the formal parameter list _L_ that follows the name, | ||
type parameter list, if any, and `.id`, if any. | ||
|
||
Otherwise, _D_ is a declaring body constructor. If the reserved word `this` | ||
is followed by `.id` where `id` is an identifier then _k_ has the name | ||
`C.id`. If it is followed by `.new` then _k_ has the name `C`. If it is not | ||
followed by `.` then _k_ has the name `C`. | ||
is followed by `.id` where `id` is an identifier then _k2_ has the name | ||
`C.id`. If it is followed by `.new` then _k2_ has the name `C`. If it is not | ||
followed by `.` then _k2_ has the name `C`. | ||
|
||
The formal parameter list _L2_ of _k_ is identical to _L_, except that each | ||
The formal parameter list _L2_ of _k2_ is identical to _L_, except that each | ||
formal parameter is processed as follows. | ||
|
||
The formal parameters in _L_ and _L2_ occur in the same order, and | ||
|
@@ -968,22 +990,19 @@ positional or named parameter remains optional; if it has a default value | |
unchanged from _L_ to _L2_ *(this is a plain, non-declaring parameter)*. | ||
- Otherwise, a formal parameter (named or positional) of the form `var T p` | ||
or `final T p` where `T` is a type and `p` is an identifier is replaced | ||
in _L2_ by `this.p`, along with its default value, if any. Next, an | ||
instance variable declaration of the form `T p;` or `final T p;` is added | ||
to _D2_. The instance variable has the modifier `final` if the parameter | ||
in _L_ has the modifier `final`, or _D_ is an `extension type` | ||
declaration, or _D_ is an `enum` declaration. In all cases, if `p` has | ||
the modifier `covariant` then this modifier is removed from the parameter | ||
in _L2_, and it is added to the instance variable declaration named `p`. | ||
|
||
In every case, any DartDoc comments are copied along with the formal | ||
parameter, and in the case where an instance variable is implicitly | ||
induced, the DartDoc comment is also added to that instance variable. | ||
in _L2_ by `this.p`, along with its default value, if any. Next, a | ||
semantic instance variable declaration corresponding to the syntax `T p;` | ||
or `final T p;` is added to _D2_. It includes the modifier `final` if the | ||
parameter in _L_ has the modifier `final`, or _D_ is an `extension type` | ||
declaration and _k_ is a declaring header constructor. In all cases, if | ||
`p` has the modifier `covariant` then this modifier is removed from the | ||
parameter in _L2_, and it is added to the instance variable declaration | ||
named `p`. | ||
|
||
If there is an initializer list following the formal parameter list _L_ | ||
then _k_ has an initializer list with the same elements in the same order. | ||
then _k2_ has an initializer list with the same elements in the same order. | ||
|
||
Finally, _k_ is added to _D2_, and _D_ is replaced by _D2_. | ||
Finally, _k2_ is added to _D2_, and _D_ is replaced by _D2_. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replaced in what? ("Everywhere", probably. Any and every reference to D is now a reference to D2 instead, there is no D.) When does this replacement happen, precisely?
That means that some parts of the language semantics see the original syntax, and some parts of the langauge semantics see the rewritten syntax. |
||
|
||
### Discussion | ||
|
||
|
@@ -1017,6 +1036,10 @@ of declaration, and the constructor might be non-const). | |
|
||
### Changelog | ||
|
||
1.10 - October 3, 2025 | ||
|
||
* Rename the feature to 'declaring constructors'. Fix several small errors. | ||
|
||
1.9 - August 8, 2025 | ||
|
||
* Change the scoping such that non-late initializing expressions have the | ||
|
Uh oh!
There was an error while loading. Please reload this page.