From 93612109af520d565ea969fbe2810bff79afba94 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 25 Nov 2025 17:25:11 +0100 Subject: [PATCH 01/10] Specify that primary parameters are immutable in initializer expressions (but a primary parameter can still be modified in the body of a primary constructor) --- .../feature-specification.md | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index f047f85c40..8eb55c59df 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -4,7 +4,7 @@ Author: Erik Ernst Status: Accepted -Version: 1.12 +Version: 1.13 Experiment flag: declaring-constructors @@ -191,10 +191,12 @@ that the instance variable declaration which is induced by this declaring 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 (it is not supported to declare the representation variable -using a normal instance variable declaration): +`final` on the representation variable can be specified or omitted. It is +an error to specify the modifier `var` on the representation variable. + +An extension type declaration is specified to use a primary constructor (it +is not supported to declare the representation variable using a normal +instance variable declaration): ```dart // Using a primary constructor. @@ -745,6 +747,10 @@ main() { } ``` +It is a compile-time error if an assignment to a primary parameter occurs +in the initializing expression of a non-late instance variable or in the +initializer list of the body part of a primary constructor. + The following errors apply to formal parameters of a primary constructor. Let _p_ be a formal parameter of a primary constructor in a class, mixin class, enum, or extension type declaration _D_ named `C`: @@ -840,15 +846,16 @@ 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, 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` and _D_ is not an `extension - type` decaration; if _D_ is an `extension type` declaration then the name - of `p` specifies the name of the representation variable. 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 _L2_ by `this.p`, along with its default value, if any. If the + parameter has the modifier `var` and _D_ is an extension type declaration + then a compile-time error occurs. Otherwise, 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` and _D_ is not an `extension type` decaration; if _D_ is + an `extension type` declaration then the name of `p` specifies the name + of the representation variable. 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 _k2_ has an initializer list with the same elements in the same order. @@ -899,6 +906,10 @@ of declaration, and the constructor might be non-const). ### Changelog +1.13 - November 25, 2025 + +* TODO + 1.12 - November 6, 2025 * Eliminate in-body declaring constructors. Revert to the terminology where From 8cc61476c2669a9e0d4c5fe58637a0c26227ee8f Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 25 Nov 2025 17:26:52 +0100 Subject: [PATCH 02/10] Specify that primary parameters are immutable in initializer expressions (but a primary parameter can still be modified in the body of a primary constructor) --- .../primary-constructors/feature-specification.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 8eb55c59df..a8867f7bd3 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -748,9 +748,11 @@ main() { ``` It is a compile-time error if an assignment to a primary parameter occurs -in the initializing expression of a non-late instance variable or in the +in the initializing expression of a non-late instance variable, or in the initializer list of the body part of a primary constructor. +*This includes expressions like `p++` where the assignment is implicit.* + The following errors apply to formal parameters of a primary constructor. Let _p_ be a formal parameter of a primary constructor in a class, mixin class, enum, or extension type declaration _D_ named `C`: From 30ebd6fc87d606bcab93a0ffd37c7feb29fc7629 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 25 Nov 2025 17:40:47 +0100 Subject: [PATCH 03/10] Add version info --- .../primary-constructors/feature-specification.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index a8867f7bd3..973b92b91e 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -910,7 +910,8 @@ of declaration, and the constructor might be non-const). 1.13 - November 25, 2025 -* TODO +* Specify that assignments to primary parameters in initialization code is + an error. 1.12 - November 6, 2025 From c282a5e3bd0e1a392b516cc79128df84aa2b389d Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 26 Nov 2025 10:18:00 +0100 Subject: [PATCH 04/10] Correct the update to apply to declaring/initializing/super parameters only, as decided by the language team --- .../primary-constructors/feature-specification.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 973b92b91e..179fb710e7 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -747,11 +747,15 @@ main() { } ``` -It is a compile-time error if an assignment to a primary parameter occurs -in the initializing expression of a non-late instance variable, or in the -initializer list of the body part of a primary constructor. - -*This includes expressions like `p++` where the assignment is implicit.* +Consider an assignment to a primary parameter which occurs in the +initializing expression of a non-late instance variable, or in the +initializer list of the body part of a primary constructor. In this +situation, it is a compile-time error if the parameter is an initializing +formal, a super parameter, or a declaring parameter. + +*This includes expressions like `p++` where the assignment is implicit. +The rule only applies for non-late variables because the primary parameters +are not in scope in the initializing expression of a late variable.* The following errors apply to formal parameters of a primary constructor. Let _p_ be a formal parameter of a primary constructor in a class, mixin From bba400b7c2591201a7daae429d854c9d50d737f5 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 2 Dec 2025 11:58:09 +0100 Subject: [PATCH 05/10] Clarify version item --- .../primary-constructors/feature-specification.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 179fb710e7..5cec510c06 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -914,8 +914,8 @@ of declaration, and the constructor might be non-const). 1.13 - November 25, 2025 -* Specify that assignments to primary parameters in initialization code is - an error. +* Specify that an assignment to a specialized primary parameter (that is, + an initializing, declaring, or super parameter) is an error. 1.12 - November 6, 2025 From 1a96004dfbabee3f7a2bec17b69c06f2e44f6895 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 2 Dec 2025 12:26:57 +0100 Subject: [PATCH 06/10] Correct the error caused by assignments to primary parameters in initialization code --- .../primary-constructors/feature-specification.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 5cec510c06..f7d5559e4e 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -750,8 +750,7 @@ main() { Consider an assignment to a primary parameter which occurs in the initializing expression of a non-late instance variable, or in the initializer list of the body part of a primary constructor. In this -situation, it is a compile-time error if the parameter is an initializing -formal, a super parameter, or a declaring parameter. +situation, a compile-time error occurs. *This includes expressions like `p++` where the assignment is implicit. The rule only applies for non-late variables because the primary parameters @@ -914,8 +913,8 @@ of declaration, and the constructor might be non-const). 1.13 - November 25, 2025 -* Specify that an assignment to a specialized primary parameter (that is, - an initializing, declaring, or super parameter) is an error. +* Specify that an assignment to a primary parameter in initialization code + is an error. 1.12 - November 6, 2025 From 126080c47c54f7b155391f9396e63db5a40f1981 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 2 Dec 2025 12:45:16 +0100 Subject: [PATCH 07/10] Simplified the wording about the assignment-to-primary-parameter error --- .../primary-constructors/feature-specification.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index f7d5559e4e..5082716f5d 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -747,10 +747,9 @@ main() { } ``` -Consider an assignment to a primary parameter which occurs in the -initializing expression of a non-late instance variable, or in the -initializer list of the body part of a primary constructor. In this -situation, a compile-time error occurs. +A compile-time error occurs if an assignment to a primary parameter occurs +in the initializing expression of a non-late instance variable, or in the +initializer list of the body part of a primary constructor. *This includes expressions like `p++` where the assignment is implicit. The rule only applies for non-late variables because the primary parameters From f646f9a285fb97cd23e8a69b48e083a35481a54d Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 2 Dec 2025 13:00:34 +0100 Subject: [PATCH 08/10] Add an error for double initialization of an instance variable --- .../feature-specification.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 5082716f5d..225ce0459d 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -755,6 +755,19 @@ initializer list of the body part of a primary constructor. The rule only applies for non-late variables because the primary parameters are not in scope in the initializing expression of a late variable.* +Consider a class with a primary constructor that also has a body part with +an initializer list. A compile-time error occurs if an instance variable +declaration has an initializing expression, and it is also initialized by +an element in the initializer list of the body port. + +*This is already an error when the instance variable is final, but no such +error is raised when the instance variable is mutable and the initializer +list is part of a non-primary constructor. However, with a primary +constructor this situation will always cause the value of the initializing +expression in the variable declaration to be overwritten by the value in +the initializer list, which makes the situation more confusing than +useful.* + The following errors apply to formal parameters of a primary constructor. Let _p_ be a formal parameter of a primary constructor in a class, mixin class, enum, or extension type declaration _D_ named `C`: @@ -913,7 +926,9 @@ of declaration, and the constructor might be non-const). 1.13 - November 25, 2025 * Specify that an assignment to a primary parameter in initialization code - is an error. + is an error. Specify an error for double initialization of a mutable + instance variable in the declaration and in a primary constructor + initializer list. 1.12 - November 6, 2025 From ef482f2e9cd135c8d2df2efdabc6161dd426626c Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 3 Dec 2025 11:41:46 +0100 Subject: [PATCH 09/10] Fix typo --- .../feature-specification.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 225ce0459d..7ae26eac05 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -222,7 +222,7 @@ We can omit the type of an optional parameter with a default value, in which case the type is inferred from the default value: ```dart -// Infer the declared type from default value. +// Infer the declared type from the default value. class Point(var int x, [var y = 0]); ``` @@ -805,13 +805,13 @@ 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.* +- if the 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` instead has declared type From 05c02fd1248a7706bed0784c96e0e17da4bcf279 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 3 Dec 2025 16:12:12 +0100 Subject: [PATCH 10/10] Review response --- .../feature-specification.md | 77 ++++++++++--------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/accepted/future-releases/primary-constructors/feature-specification.md b/accepted/future-releases/primary-constructors/feature-specification.md index 7ae26eac05..5f46fc4538 100644 --- a/accepted/future-releases/primary-constructors/feature-specification.md +++ b/accepted/future-releases/primary-constructors/feature-specification.md @@ -194,9 +194,9 @@ In the case where the declaration is an `extension type`, the modifier `final` on the representation variable can be specified or omitted. It is an error to specify the modifier `var` on the representation variable. -An extension type declaration is specified to use a primary constructor (it -is not supported to declare the representation variable using a normal -instance variable declaration): +An extension type declaration must have a primary constructor and its +single parameter is always declaring. The representation variable cannot be +declared using a normal instance variable declaration: ```dart // Using a primary constructor. @@ -222,7 +222,7 @@ We can omit the type of an optional parameter with a default value, in which case the type is inferred from the default value: ```dart -// Infer the declared type from the default value. +// Infers the declared type from the default value. class Point(var int x, [var y = 0]); ``` @@ -697,10 +697,10 @@ latter is the current scope for the initializing expressions of all non-late instance variable declarations, in addition to the initializer list of the body part of the constructor.* -*The point is that the body part of the primary constructor should have -access to the "regular" parameters, but it should have access to the -instance variables rather than the declaring or initializing parameters -with the same names. For example:* +*The point is that the function body of the body part of the primary +constructor should have access to the "regular" parameters, but it should +have access to the instance variables rather than the declaring or +initializing parameters with the same names. For example:* ```dart class C(var String x) { @@ -752,13 +752,15 @@ in the initializing expression of a non-late instance variable, or in the initializer list of the body part of a primary constructor. *This includes expressions like `p++` where the assignment is implicit. -The rule only applies for non-late variables because the primary parameters -are not in scope in the initializing expression of a late variable.* +The rule does not apply to late instance variables or (late or non-late) +static variables. The primary constructor parameters are not in scope for +initializer expressions of those variables.* Consider a class with a primary constructor that also has a body part with an initializer list. A compile-time error occurs if an instance variable declaration has an initializing expression, and it is also initialized by -an element in the initializer list of the body port. +an element in the initializer list of the body part, or by an initializing +formal parameter of the primary constructor. *This is already an error when the instance variable is final, but no such error is raised when the instance variable is mutable and the initializer @@ -772,12 +774,6 @@ The following errors apply to formal parameters of a primary constructor. Let _p_ be a formal parameter of a primary constructor in a class, mixin class, enum, or extension type declaration _D_ named `C`: -A compile-time error occurs if _p_ contains a term of the form `this.v`, or -`super.v` where `v` is an identifier, and _p_ has the modifier -`covariant`. *For example, `required covariant int this.v` is an error. The -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 the modifier `covariant`, but not `var`. *This parameter does not induce a setter.* @@ -833,8 +829,10 @@ specifying the current scope explicitly as the body scope, in spite of the fact that the primary constructor is actually placed outside the braces that delimit the class body.* -Next, _k2_ has the modifier `const` iff the keyword `const` occurs just -before the name of _D_, or _D_ is an `enum` declaration. +Next, _k2_ has the modifier `const` if and only if the keyword `const` +occurs just before the name of _D_ or _D_ is an `enum` declaration. In any +case, such an occurrence of `const` in the header of _D_ is omitted in +_D2_. Consider the case where _k_ is a primary constructor. If the name `C` in _D_ and the type parameter list, if any, is followed by `.id` where `id` is @@ -855,27 +853,32 @@ positional or named parameter remains optional; if it has a default value `d` in _L_ then it has the default value `d` in _L2_ as well. - An initializing formal parameter *(e.g., `T this.x`)* is copied from _L_ - to _L2_, along with the default value, if any, and is otherwise unchanged. -- A super parameter is copied from _L_ to _L2_ along with the default - value, if any, and is otherwise unchanged. + to _L2_, with no changes. +- A super parameter is copied from _L_ to _L2_ any, with no changes. - A formal parameter which is not covered by the previous two cases and which does not have the modifier `var` or the modifier `final` is copied 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. If the - parameter has the modifier `var` and _D_ is an extension type declaration - then a compile-time error occurs. Otherwise, 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` and _D_ is not an `extension type` decaration; if _D_ is - an `extension type` declaration then the name of `p` specifies the name - of the representation variable. 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 _k2_ has an initializer list with the same elements in the same order. +- Otherwise, it is a declaring parameter. 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. The same is done in the case where the formal + parameter has the form `var p` or `final p`, and `T` is the declared type + of `p` which was obtained by inference. If the parameter has the modifier + `var` and _D_ is an extension type declaration then a compile-time error + occurs. Otherwise, if _D_ is not an extension type declaration, 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 and + only if the parameter in _L_ has the modifier `final` and _D_ is not an + `extension type` decaration. Otherwise, if _D_ is an `extension type` + declaration then the name of `p` specifies the name of the representation + variable. 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 a primary constructor body part that contains an initializer +list then _k2_ has an initializer list with the same elements in the same +order. If that body part has a function body then _k2_ has the same +function body. Finally, _k2_ is added to _D2_, and _D_ is replaced by _D2_.