Skip to content

Commit 4a2813d

Browse files
DougGregorhborla
andauthored
Init accessors suggestions (#2)
* Expand description of accesses and give a "dictionary storage" example * Make the name of the `newValue` parameter configurable Be consistent with set/willSet/didSet in making the parameter name optional and overridable. * A few clarifications regarding init accessor signatures * Clarify the rules for definite initialization Expand the examples and show the case where virtual and stored-property initialization collide. --------- Co-authored-by: Holly Borla <[email protected]>
1 parent 03af503 commit 4a2813d

File tree

1 file changed

+60
-19
lines changed

1 file changed

+60
-19
lines changed

proposals/NNNN-init-accessors.md

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,42 @@ struct Angle {
7575
}
7676
```
7777

78-
The signature of an `init` accessor specifies the property's access dependencies and the set of stored properties that are initialized by the accessor. Access dependencies must be initialized before the computed property's `init` accessor is invoked, and the `init` accessor must initialize the specified stored properties on all control flow paths.
78+
The signature of an `init` accessor specifies up to two sets of stored properties: the access dependencies (via `accesses`) and the initialized properties (via `initializes`). Access dependencies specify the other stored properties that can be accessed from within the `init` accessor (no other uses of `self` are allowed), and therefore must be initialized before the computed property's `init` accessor is invoked. The `init` accessor must initialize each of the initialized stored properties on all control flow paths. The `radians` property in the example above specifies no access dependencies, but initializes the `degrees` property, so it specifies only `initializes: degrees`.
79+
80+
Access dependencies allow a computed property to be initialized by placing its contents into another stored property:
81+
82+
```swift
83+
struct ProposalViaDictionary {
84+
private var dictionary: [String: String] = [:]
85+
86+
var title: String {
87+
init(newValue, accesses: dictionary) {
88+
dictionary["title"] = newValue
89+
}
90+
91+
get { dictionary["title"]! }
92+
set { dictionary["title"] = newValue }
93+
}
94+
95+
var text: String {
96+
init(newValue, accesses: dictionary) {
97+
dictionary["text"] = newValue
98+
}
99+
100+
get { dictionary["text"]! }
101+
set { dictionary["text"] = newValue }
102+
}
103+
104+
init(title: String, text: String) {
105+
self.title = title // calls init accessor to insert title into the dictionary
106+
self.text = text // calls init accessor to insert text into the dictionary
107+
108+
// it is an error to omit either initialization above
109+
}
110+
}
111+
```
112+
113+
Both `init` accessors document that they access `dictionary`, which allows them to insert the new values into the dictionary with the appropriate key as part of initialization. This allows one to fully abstract away the storage mechanism used in the type.
79114

80115
With this proposal, property wrappers have no bespoke definite initialization support. Instead, the desugaring includes an `init` accessor for wrapped properties. The property wrapper code in the Motivation section will desugar to the following code:
81116

@@ -116,16 +151,17 @@ This proposal adds new syntax for `init` accessor blocks, which can be written i
116151
```
117152
init-accessor -> 'init' init-accessor-signature[opt] function-body
118153
119-
init-accessor-signature -> '(' init-dependency-clause ')'
154+
init-accessor-signature -> '(' init-dependency-clause [opt] ')'
120155
121-
init-dependency-clause -> 'newValue'
122-
init-dependency-clause -> 'newValue' ',' init-dependencies
156+
init-dependency-clause -> identifier
157+
init-dependency-clause -> identifier ',' init-dependencies
158+
init-dependency-clause -> init-dependencies
123159
124-
init-dependencies -> subsumes-list
125-
init-dependencies -> subsumes-list ',' accesses-list
160+
init-dependencies -> initializes-list
161+
init-dependencies -> initializes-list ',' accesses-list
126162
init-dependences -> access-list
127163
128-
subsumes-list -> 'initializes' ':' identifier-list
164+
initializes-list -> 'initializes' ':' identifier-list
129165
130166
accesses-list -> 'accesses' ':' identifier-list
131167
@@ -136,9 +172,11 @@ identifier-list -> identifier ',' identifier-list
136172
accessor-block -> init-accessor
137173
```
138174

175+
The `identifier` in an `init-dependency-clause`, if provided, is the name of the parameter that contains the initial value. If not provided, a parameter with the name `newValue` is automatically created.
176+
139177
### `init` accessor signatures
140178

141-
`init` accessor declarations can optionally specify a signature. An `init` accessor signature is composed of the `newValue` parameter, a list of stored properties that are initialized by this accessor specified with the `initializes:` label, and a list of stored properties that are accessed by this accessor specified with the `accesses:` label:
179+
`init` accessor declarations can optionally specify a signature. An `init` accessor signature is composed of a parameter for the initial value, a list of stored properties that are initialized by this accessor specified with the `initializes:` label, and a list of stored properties that are accessed by this accessor specified with the `accesses:` labe, all of which are optional:
142180

143181
```swift
144182
struct S {
@@ -158,21 +196,21 @@ struct S {
158196
}
159197
```
160198

161-
If the accessor only uses `newValue`, the signature is not required.
199+
If the accessor uses the default parameter name `newValue` and neither initializes nor accesses any stored property, the signature is not required.
162200

163-
Init accessors can subsume the initialization of a set of stored properties. Subsumed stored properties are specified through the `initializes:` clause of the accessor signature. The body of an `init` accessor is required to initialize the subsumed stored properties on all paths.
201+
Init accessors can subsume the initialization of a set of stored properties. Subsumed stored properties are specified through the `initializes:` clause of the accessor signature. The body of an `init` accessor is required to initialize the subsumed stored properties on all control flow paths.
164202

165-
Init accessors can also require a set of stored properties to already be initialized when the body is evaluated, which are specified through the `accesses:` cause of the signature. These stored properties can be accessed in the accessor body; no other properties or methods on `self` are available inside the accessor body.
203+
Init accessors can also require a set of stored properties to already be initialized when the body is evaluated, which are specified through the `accesses:` cause of the signature. These stored properties can be accessed in the accessor body; no other properties or methods on `self` are available inside the accessor body, nor is `self` available as a whole object (i.e., to call methods on it).
166204

167205
### Definite initialization of properties on `self`
168206

169-
The semantics of an assignment inside of a type's initializer depend on whether or not all of `self` is initialized on all paths at the point of assignment. Before `self` is initialized, assignment to a wrapped property is re-written to initialization of the backing property wrapper storage. After `self` is initialized, assignment to a wrapped property is re-written to a call to the wrapped property's setter. For computed properties with `init` accessors, assignment is re-written to an `init` accessor call before `self` is initialized, and assignment is re-written to a setter call after `self` is initialized.
207+
The semantics of an assignment inside of a type's initializer depend on whether or not all of `self` is initialized on all paths at the point of assignment. Before all of `self` is initialized, assignment to a computed property with an `init` accessor is re-written to an `init` accessor call; after `self` has been initialized, assignment to a computed property is re-written to a setter call.
170208

171209
With this proposal, all of `self` is initialized if:
172-
* All stored properties are initialized on all paths.
210+
* All stored properties are initialized on all paths, and
173211
* All computed properties with `init` accessors are virtually initialized on all paths.
174212

175-
An assignment to a computed property with an `init` accessor before all of `self` is initialized covers the computed property and all stored properties specified in the `initializes` clause:
213+
An assignment to a computed property with an `init` accessor before all of `self` is initialized will virtually initialize the computed property and initialize all of the stored properties specified in its `initializes` clause:
176214

177215
```swift
178216
struct S {
@@ -183,28 +221,31 @@ struct S {
183221
}
184222

185223
init() {
186-
self.computed = 1 // initializes 'computed', 'x1', and 'x2'
224+
self.computed = 1 // initializes 'computed', 'x1', and 'x2'; 'self' is now fully initialized
187225
}
188226
}
189227
```
190228

191-
An assignment to a stored property before all of `self` is initialized covers the initialization of computed properties with `init` accessors that specify that stored property if the other `initializes:` dependencies are already initialized:
229+
An assignment to a stored property before all of `self` is initialized will initialize that stored property. When all of the stored properties listed in the `initializes:` clause of a computed property with an `init` accessor have been initialized, that computed property is virtually initialized:
192230

193231
```swift
194232
struct S {
195233
var x1: Int
196234
var x2: Int
235+
var x3: Int
197236
var computed: Int {
198237
init(newValue, initializes: x1, x2) { ... }
199238
}
200239

201240
init() {
202-
self.computed = 1 // initializes 'computed', 'x1', and 'x2'
241+
self.x1 = 1 // initializes 'x1'; neither 'x2' or 'computed' is initialized
242+
self.x2 = 1 // initializes 'x2' and 'computed'
243+
self.x3 = 1 // initializes 'x3'; 'self' is now fully initialized
203244
}
204245
}
205246
```
206247

207-
A stored property is considered initialized if it is assigned a value directly, or if a computed property that subsumes its initialization is assigned a value directly:
248+
An assignment to a computed property where at least one of the stored properties listed in `initializes:` is initialized, but `self` is not initialized, is an error. This prevents double-initialization of the underlying stored properties:
208249

209250
```swift
210251
struct S {
@@ -220,7 +261,7 @@ struct S {
220261

221262
init(x: Int, y: Int) {
222263
self.x = x // Only initializes 'x'
223-
self.y = y // Initializes 'y' and 'point'
264+
self.point = (x, y) // error: neither the `init` accessor nor the setter can be called here
224265
}
225266
}
226267
```

0 commit comments

Comments
 (0)