From 2087f6180055da4ad401768234c57bd58225e43d Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 2 Oct 2025 16:11:51 +0200 Subject: [PATCH 1/4] Small fixes --- .../feature-specification.md | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 99ce253657..89a6a93f57 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -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. @@ -874,8 +874,9 @@ 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. *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 @@ -890,30 +891,31 @@ 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 +constructor _k2_; and it will add zero or more instance variable 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 +Assume that `p` is an optional formal parameter in _k_ 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. +Assume that `p` has no type annotation, and 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 +Otherwise, assume that `p` does not have a type annotation, 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?`. @@ -932,25 +934,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_ +`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`. If it exists, _D2_ omits the part derived from `'.' ` 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. 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 @@ -972,18 +974,19 @@ positional or named parameter remains optional; if it has a default value 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`. + 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`. 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. 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_. ### Discussion From d6377f40f2bfbf6101c0ec736c98d74669e0c99f Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 2 Oct 2025 16:20:10 +0200 Subject: [PATCH 2/4] Add support for constructors using `new` --- .../feature-specification.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 89a6a93f57..6b95622f56 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -619,6 +619,13 @@ constructors as well. 'const' | ; + ::= // Modified rule. + ('.' identifierOrNew)?; + + ::= // New rule. + + | 'new'; + ::= // New rule. 'const' 'this' ('.' )? ; @@ -732,8 +739,19 @@ superclass.* A compile-time error occurs if a `` has the modifier `required` as well as a default value. +*Note that the updated grammar rule for `` 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 ? ` is `id1` *(that is, the same as the name of the class)*. From 7bfa61d6194b863fae2a16f6ce2ca8097d501aa5 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 3 Oct 2025 10:12:05 +0200 Subject: [PATCH 3/4] Rename feature and change title accordingly. Add change log entry --- .../primary-constructors/feature-specification.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 6b95622f56..be7ff6d41c 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -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 @@ -1038,6 +1038,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 From 5e132fada396258a0b8161081a95369ba16086ba Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 3 Oct 2025 17:15:18 +0200 Subject: [PATCH 4/4] Review response --- .../feature-specification.md | 76 +++++++++---------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index be7ff6d41c..7231193120 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -620,7 +620,7 @@ constructors as well. | ; ::= // Modified rule. - ('.' identifierOrNew)?; + ('.' identifierOrNew)?; ::= // New rule. @@ -693,8 +693,8 @@ extension type declaration, together with a declaration in the body that contains a `` *(which does not contain a ``, 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. @@ -892,13 +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`, also if the latter is implicitly induced. *A final instance +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 @@ -911,32 +911,34 @@ 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 _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 _k2_; 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 _k_ which has the -modifier `var` or the modifier `final` *(that is, `p` is a declaring -parameter)*. - -Assume that `p` has no type annotation, and 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 type annotation, 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 @@ -960,10 +962,10 @@ 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 _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`. If it exists, _D2_ omits the part derived from -`'.' ` 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. +has the name `C`. _D2_ omits the part derived from `'.' ` +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 _k2_ has the name @@ -988,19 +990,15 @@ 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` + 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`. -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. - If there is an initializer list following the formal parameter list _L_ then _k2_ has an initializer list with the same elements in the same order.