Skip to content

Commit 8abde81

Browse files
authored
Specify that non-plain primary parameters are immutable (#4575)
Specify that primary parameters are immutable in initializer expressions (but a primary parameter can still be modified in the function body of a primary constructor). Also specify that double initialization is an error with a primary constructor (initializing formal + initializing expression in instance variable declaration + initializer list, or two of those).
1 parent a293312 commit 8abde81

File tree

1 file changed

+75
-41
lines changed

1 file changed

+75
-41
lines changed

accepted/future-releases/primary-constructors/feature-specification.md

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Author: Erik Ernst
44

55
Status: Accepted
66

7-
Version: 1.12
7+
Version: 1.13
88

99
Experiment flag: declaring-constructors
1010

@@ -191,10 +191,12 @@ that the instance variable declaration which is induced by this declaring
191191
constructor parameter is `final`.
192192

193193
In the case where the declaration is an `extension type`, the modifier
194-
`final` on the representation variable can be specified or omitted. Note
195-
that an extension type declaration is specified to use a primary
196-
constructor (it is not supported to declare the representation variable
197-
using a normal instance variable declaration):
194+
`final` on the representation variable can be specified or omitted. It is
195+
an error to specify the modifier `var` on the representation variable.
196+
197+
An extension type declaration must have a primary constructor and its
198+
single parameter is always declaring. The representation variable cannot be
199+
declared using a normal instance variable declaration:
198200

199201
```dart
200202
// Using a primary constructor.
@@ -220,7 +222,7 @@ We can omit the type of an optional parameter with a default value,
220222
in which case the type is inferred from the default value:
221223

222224
```dart
223-
// Infer the declared type from default value.
225+
// Infers the declared type from the default value.
224226
class Point(var int x, [var y = 0]);
225227
```
226228

@@ -695,10 +697,10 @@ latter is the current scope for the initializing expressions of all
695697
non-late instance variable declarations, in addition to the initializer
696698
list of the body part of the constructor.*
697699

698-
*The point is that the body part of the primary constructor should have
699-
access to the "regular" parameters, but it should have access to the
700-
instance variables rather than the declaring or initializing parameters
701-
with the same names. For example:*
700+
*The point is that the function body of the body part of the primary
701+
constructor should have access to the "regular" parameters, but it should
702+
have access to the instance variables rather than the declaring or
703+
initializing parameters with the same names. For example:*
702704

703705
```dart
704706
class C(var String x) {
@@ -745,16 +747,33 @@ main() {
745747
}
746748
```
747749

750+
A compile-time error occurs if an assignment to a primary parameter occurs
751+
in the initializing expression of a non-late instance variable, or in the
752+
initializer list of the body part of a primary constructor.
753+
754+
*This includes expressions like `p++` where the assignment is implicit.
755+
The rule does not apply to late instance variables or (late or non-late)
756+
static variables. The primary constructor parameters are not in scope for
757+
initializer expressions of those variables.*
758+
759+
Consider a class with a primary constructor that also has a body part with
760+
an initializer list. A compile-time error occurs if an instance variable
761+
declaration has an initializing expression, and it is also initialized by
762+
an element in the initializer list of the body part, or by an initializing
763+
formal parameter of the primary constructor.
764+
765+
*This is already an error when the instance variable is final, but no such
766+
error is raised when the instance variable is mutable and the initializer
767+
list is part of a non-primary constructor. However, with a primary
768+
constructor this situation will always cause the value of the initializing
769+
expression in the variable declaration to be overwritten by the value in
770+
the initializer list, which makes the situation more confusing than
771+
useful.*
772+
748773
The following errors apply to formal parameters of a primary constructor.
749774
Let _p_ be a formal parameter of a primary constructor in a class, mixin
750775
class, enum, or extension type declaration _D_ named `C`:
751776

752-
A compile-time error occurs if _p_ contains a term of the form `this.v`, or
753-
`super.v` where `v` is an identifier, and _p_ has the modifier
754-
`covariant`. *For example, `required covariant int this.v` is an error. The
755-
reason for this error is that the modifier `covariant` must be specified on
756-
the declaration of `v` which is known to exist, not on the parameter.*
757-
758777
A compile-time error occurs if _p_ has the modifier `covariant`, but
759778
not `var`. *This parameter does not induce a setter.*
760779

@@ -782,13 +801,13 @@ Let `p` be a formal parameter in _k_ which has the modifier `var` or the
782801
modifier `final` *(that is, `p` is a declaring parameter)*.
783802

784803
Consider the situation where `p` has no type annotation:
785-
- if combined member signature for a getter with the same name as `p` from
786-
the superinterfaces of _D_ exists and has return type `T`, the parameter
787-
`p` has declared type `T`. If no such getter exists, but a setter with
788-
the same basename exists, with a formal parameter whose type is `T`, the
789-
parameter `p` has declared type `T`. *In other words, an instance
790-
variable introduced by a declaring parameter is subject to override
791-
inference, just like an explicitly declared instance variable.*
804+
- if the combined member signature for a getter with the same name as `p`
805+
from the superinterfaces of _D_ exists and has return type `T`, the
806+
parameter `p` has declared type `T`. If no such getter exists, but a
807+
setter with the same basename exists, with a formal parameter whose type
808+
is `T`, the parameter `p` has declared type `T`. *In other words, an
809+
instance variable introduced by a declaring parameter is subject to
810+
override inference, just like an explicitly declared instance variable.*
792811
- otherwise, if `p` is optional and has a default value whose static type
793812
in the empty context is a type `T` which is not `Null` then `p` has
794813
declared type `T`. When `T` is `Null`, `p` instead has declared type
@@ -810,8 +829,10 @@ specifying the current scope explicitly as the body scope, in spite of the
810829
fact that the primary constructor is actually placed outside the braces
811830
that delimit the class body.*
812831

813-
Next, _k2_ has the modifier `const` iff the keyword `const` occurs just
814-
before the name of _D_, or _D_ is an `enum` declaration.
832+
Next, _k2_ has the modifier `const` if and only if the keyword `const`
833+
occurs just before the name of _D_ or _D_ is an `enum` declaration. In any
834+
case, such an occurrence of `const` in the header of _D_ is omitted in
835+
_D2_.
815836

816837
Consider the case where _k_ is a primary constructor. If the name `C` in
817838
_D_ and the type parameter list, if any, is followed by `.id` where `id` is
@@ -832,26 +853,32 @@ positional or named parameter remains optional; if it has a default value
832853
`d` in _L_ then it has the default value `d` in _L2_ as well.
833854

834855
- An initializing formal parameter *(e.g., `T this.x`)* is copied from _L_
835-
to _L2_, along with the default value, if any, and is otherwise unchanged.
836-
- A super parameter is copied from _L_ to _L2_ along with the default
837-
value, if any, and is otherwise unchanged.
856+
to _L2_, with no changes.
857+
- A super parameter is copied from _L_ to _L2_ any, with no changes.
838858
- A formal parameter which is not covered by the previous two cases and
839859
which does not have the modifier `var` or the modifier `final` is copied
840860
unchanged from _L_ to _L2_ *(this is a plain, non-declaring parameter)*.
841-
- Otherwise, a formal parameter (named or positional) of the form `var T p`
842-
or `final T p` where `T` is a type and `p` is an identifier is replaced
843-
in _L2_ by `this.p`, along with its default value, if any. Next, a
861+
- Otherwise, it is a declaring parameter. A formal parameter (named or
862+
positional) of the form `var T p` or `final T p` where `T` is a type and
863+
`p` is an identifier is replaced in _L2_ by `this.p`, along with its
864+
default value, if any. The same is done in the case where the formal
865+
parameter has the form `var p` or `final p`, and `T` is the declared type
866+
of `p` which was obtained by inference. If the parameter has the modifier
867+
`var` and _D_ is an extension type declaration then a compile-time error
868+
occurs. Otherwise, if _D_ is not an extension type declaration, a
844869
semantic instance variable declaration corresponding to the syntax `T p;`
845-
or `final T p;` is added to _D2_. It includes the modifier `final` if the
846-
parameter in _L_ has the modifier `final` and _D_ is not an `extension
847-
type` decaration; if _D_ is an `extension type` declaration then the name
848-
of `p` specifies the name of the representation variable. In all cases, if
849-
`p` has the modifier `covariant` then this modifier is removed from the
850-
parameter in _L2_, and it is added to the instance variable declaration
851-
named `p`.
852-
853-
If there is an initializer list following the formal parameter list _L_
854-
then _k2_ has an initializer list with the same elements in the same order.
870+
or `final T p;` is added to _D2_. It includes the modifier `final` if and
871+
only if the parameter in _L_ has the modifier `final` and _D_ is not an
872+
`extension type` decaration. Otherwise, if _D_ is an `extension type`
873+
declaration then the name of `p` specifies the name of the representation
874+
variable. In all cases, if `p` has the modifier `covariant` then this
875+
modifier is removed from the parameter in _L2_, and it is added to the
876+
instance variable declaration named `p`.
877+
878+
If there is a primary constructor body part that contains an initializer
879+
list then _k2_ has an initializer list with the same elements in the same
880+
order. If that body part has a function body then _k2_ has the same
881+
function body.
855882

856883
Finally, _k2_ is added to _D2_, and _D_ is replaced by _D2_.
857884

@@ -899,6 +926,13 @@ of declaration, and the constructor might be non-const).
899926

900927
### Changelog
901928

929+
1.13 - November 25, 2025
930+
931+
* Specify that an assignment to a primary parameter in initialization code
932+
is an error. Specify an error for double initialization of a mutable
933+
instance variable in the declaration and in a primary constructor
934+
initializer list.
935+
902936
1.12 - November 6, 2025
903937

904938
* Eliminate in-body declaring constructors. Revert to the terminology where

0 commit comments

Comments
 (0)