Skip to content

Commit a3a7c1d

Browse files
authored
clarify some edge cases around constructors (#3737)
Closes #3555 and #3554 Specifies a bit more precisely how augmenting constructors are handled: - specifies what it means if there is no explicit body - specifies that all the same parameter variables are in scope in the augmented body - specifies clearly that no arguments are passed to the augmented body cc @polina-c
1 parent 0cae902 commit a3a7c1d

File tree

1 file changed

+118
-27
lines changed

1 file changed

+118
-27
lines changed

working/augmentation-libraries/feature-specification.md

Lines changed: 118 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ have one or more other capabilities:
567567
additional updates and/or checks.)
568568

569569
Variable declarations may be marked `abstract` or `external` and, if so,
570-
those are mapped over to the corresponding getter and setter functions.
570+
those are mapped over to the corresponding getter and setter functions.
571571

572572
An `abstract` variable declaration is equivalent to an abstract getter
573573
declaration, and if not `final`, also an abstract setter declaration. An
@@ -784,39 +784,38 @@ It is a compile-time error if:
784784

785785
### Augmenting constructors
786786

787-
Constructors are (as always) more complex. A constructor marked `augment`
788-
replaces the body of the existing constructor with its body, if present. If the
789-
augmenting constructor has any initializers, they are appended to the augmented
790-
constructor's initializers, but before any super initializer or redirecting
791-
initializer if there is one.
792-
793-
In the augmenting constructor's body, an `augmented()` call invokes the
794-
augmented constructor's body. The expression has type `void` and evaluates to
795-
`null`. **(TODO: This is slightly under-specified. We can use the current
796-
bindings of the parameters of the augmenting constructor as the initial binding
797-
of parameter variables in the augmented body, or we can execute the body in the
798-
current *scope*, using the same variables as the current body. The latter is
799-
not what we do with functions elsewhere, and allows the `augmented` expression
800-
to modify local variables, but the former introduces different variables than
801-
the ones that existed when evaluating the initializer list. If the initializer
802-
list captures variables in closures, that body may not work.)**
787+
Constructors are (as always) more complex. We have many kinds of constructors,
788+
and what it means to augment each is different. For the purposes of this section
789+
we will call out three specific kinds of constructors:
790+
791+
**Non-redirecting generative constructors**: These always produce a new
792+
instance, and have an optional initializer list and optional body.
793+
**Non-redirecting factory constructors**: These are much like static methods,
794+
and might return a subtype of the current type. They also may not create a new
795+
instance, but return an existing one. They must have a body.
796+
**Redirecting constructors**: Both generative and factory constructors can be
797+
redirecting, although the syntax looks slightly different for each.
798+
799+
It may not always be apparent whether a constructor is redirecting or not based
800+
on a given declaration (if there is no body, initializer list, or redirecting
801+
constructor invocation). These constructors are considered to be "potentially
802+
redirecting" or "potentially non-redirecting". An augmentation may alter this
803+
property by augmenting a constructor in a way that makes it concretely
804+
redirecting or not.
803805

804806
It is a compile-time error if:
805807

806-
* The function signature of the augmenting constructor does not match the
807-
signature of the augmented constructor. This means that the parameters must
808-
be the same (just as for augmenting functions, except here there is no
809-
return type and no type parameters on the constructor itself). Any
810-
initializing formals must be the same in both constructors. Any super
811-
parameters must be the same in both constructors.
812-
813-
**TODO: Is this the right way to handle initializing formals?**
808+
* The signature of the constructor augmentation does not match the original
809+
constructor. It must have the same number of positional parameters, the same
810+
named parameters, and matching parameters must have the same type,
811+
optionality, and any `required` modifiers must match. Any initializing
812+
formals and super parameters must also be the same in both constructors.
814813

815814
* The augmenting constructor parameters specify any default values.
816815
*Default values are defined solely by the introductory constructor.*
817816

818817
* The introductory constructor is `const` and the augmenting constructor
819-
is not, or vice versa.
818+
is not or vice versa.
820819

821820
* The introductory constructor is marked `factory` and the augmenting
822821
constructor is not, or vice versa.
@@ -828,7 +827,99 @@ It is a compile-time error if:
828827
replace a declared super constructor._ **(TODO: Why not? We allow
829828
"replacing implementation", and this is *something* like that.)**
830829

831-
**TODO: What about redirecting constructors?**
830+
* The resulting constructor is not valid (has a redirecting initializer and
831+
other initializers, multiple `super` initializers, etc).
832+
833+
* A non-redirecting constructor augments a constructor which is not
834+
potentially non-redirecting.
835+
836+
* A redirecting constructor augments a constructor which is not potentially
837+
redirecting.
838+
839+
#### Non-redirecting generative constructors
840+
841+
These are probably the most complex constructor, but also the most common.
842+
843+
A non-redirecting generative constructor marked `augment` may:
844+
845+
* Add or replace the body of the augmented constructor with a new body.
846+
847+
* If the augmenting constructor has an explicit block body, then that body
848+
replaces any existing constructor body.
849+
850+
* In the augmenting constructor's body, an `augmented()` call executes the
851+
augmented constructor's body in the same parameter scope that the
852+
augmenting body is executing in. The expression has type `void` and
853+
evaluates to `null`. **(TODO: This is slightly under-specified. We can
854+
use the current bindings of the parameters of the augmenting constructor
855+
as the initial binding of parameter variables in the augmented body, or
856+
we can execute the body in the current *scope*, using the same variables
857+
as the current body. The latter is not what we do with functions
858+
elsewhere, and allows the `augmented()` expression to modify local
859+
variables, but the former introduces different variables than the ones
860+
that existed when evaluating the initializer list. If the initializer
861+
list captures variables in closures, that body may not work.)**
862+
863+
* Initializer lists _are not_ re-run, they have already executed and
864+
shouldn't be executed twice. The same goes for initializing formals and
865+
super parameters.
866+
867+
* If a parameter variable is overwritten prior to calling `augmented()`,
868+
the augmented body will see the updated value, because the parameter
869+
scope is identical.
870+
871+
* Local variables in scope where augmented is evaluated are not in scope
872+
for the execution of the augmented constructor's body.
873+
874+
* Add initializers to the initializer list. If the augmenting constructor has
875+
an initializer list then:
876+
877+
* It's a compile-time error if the augmented constructor has
878+
super-initializer, and the augmenting constructor's initializer list
879+
also contains a super-initializer.
880+
881+
* Otherwise the result of applying the augmenting constructor has an
882+
initializer list containing first the assertions and field initializers
883+
of the augmented constructor, if any, then the assertions and field
884+
initializers of the augmenting constructor, and finally any
885+
super-initializer of either the augmeted or augmenting constructor.
886+
887+
#### Non-redirecting factory constructors
888+
889+
A non-redirecting factory constructor marked `augment` works in the same way as
890+
a normal function augmentation.
891+
892+
If it has a body, it replaces the body of the augmented constructor (if
893+
present), and it may invoke the augmented body by calling
894+
`augmented(arguments)`.
895+
896+
#### Redirecting generative constructors
897+
898+
A redirecting generative constructor marked `augment` adds its redirecting
899+
initializer to the augmented constructors initializer list.
900+
901+
This converts it into a redirecting generative constructor, removing the
902+
potentially non-redirecting property of the constructor.
903+
904+
It is a compile-time error if:
905+
906+
* The augmented constructor has any initializers or a body.
907+
908+
#### Redirecting factory constructors
909+
910+
A redirecting factory constructor marked `augment` adds its factory redirection
911+
to the augmented constructor.
912+
913+
The result of applying the augmenting constructor is a redirecting factory
914+
constructor with the same target constructor designation as the augmenting
915+
constructor. This removes the potentially non-redirecting property of the
916+
constructor.
917+
918+
It is a compile-time error if:
919+
920+
* The augmented constructor has a body.
921+
922+
#### Extension types
832923

833924
When augmenting an extension type declaration, the parenthesized clause where
834925
the representation type is specified is treated as a constructor that has a

0 commit comments

Comments
 (0)