@@ -166,26 +166,22 @@ extension MemoryLayout {
166
166
/// Returns the offset of an inline stored property of `T` within the
167
167
/// in-memory representation of `T`.
168
168
///
169
- /// If the given `key` refers to inline storage within the
170
- /// in-memory representation of `T`, and the storage is directly
171
- /// addressable (meaning that accessing it does not need to trigger any
172
- /// `didSet` or `willSet` accessors, perform any representation changes
173
- /// such as bridging or closure reabstraction, or mask the value out of
174
- /// overlapping storage as for packed bitfields), then the return value
175
- /// is a distance in bytes that can be added to a pointer of type `T` to
176
- /// get a pointer to the storage accessed by `key`. If the return value is
177
- /// non-nil, then these formulations are equivalent:
178
- ///
179
- /// var root: T, value: U
180
- /// var key: WritableKeyPath<T, U>
181
- /// // Mutation through the key path...
182
- /// root[keyPath: \.key] = value
183
- /// // ...is exactly equivalent to mutation through the offset pointer...
184
- /// withUnsafePointer(to: &root) {
185
- /// (UnsafeMutableRawPointer($0) + MemoryLayout<T>.offset(of: \.key))
186
- /// // ...which can be assumed to be bound to the target type
187
- /// .assumingMemoryBound(to: U.self).pointee = value
188
- /// }
169
+ /// If the given `key` refers to inline, directly addressable storage within
170
+ /// the in-memory representation of `T`, then the return value is a distance
171
+ /// in bytes that can be added to a pointer of type `T` to get a pointer to
172
+ /// the storage accessed by `key`. If the return value is non-nil, then these
173
+ /// formulations are equivalent:
174
+ ///
175
+ /// var root: T, value: U
176
+ /// var key: WritableKeyPath<T, U>
177
+ /// // Mutation through the key path...
178
+ /// root[keyPath: key] = value
179
+ /// // ...is exactly equivalent to mutation through the offset pointer...
180
+ /// withUnsafeMutablePointer(to: &root) {
181
+ /// (UnsafeMutableRawPointer($0) + MemoryLayout<T>.offset(of: key))
182
+ /// // ...which can be assumed to be bound to the target type
183
+ /// .assumingMemoryBound(to: U.self).pointee = value
184
+ /// }
189
185
///
190
186
/// - Parameter key: A key path referring to storage that can be accessed
191
187
/// through a value of type `T`.
@@ -194,6 +190,28 @@ extension MemoryLayout {
194
190
/// such offset is available for the storage referenced by `key`, such as
195
191
/// because `key` is computed, has observers, requires reabstraction, or
196
192
/// overlaps storage with other properties.
193
+ ///
194
+ /// A property has inline, directly addressable storage when it is a stored
195
+ /// property for which no additional work is required to extract or set the
196
+ /// value. For example:
197
+ ///
198
+ /// struct ProductCategory {
199
+ /// var name: String // inline, directly-addressable
200
+ /// var updateCounter: Int // inline, directly-addressable
201
+ /// var productCount: Int { // computed properties are not directly addressable
202
+ /// return products.count
203
+ /// }
204
+ /// var products: [Product] { // didSet/willSet properties are not directly addressable
205
+ /// didSet { updateCounter += 1 }
206
+ /// }
207
+ /// }
208
+ ///
209
+ /// When using `offset(of:)` with a type imported from a library, don't assume
210
+ /// that future versions of the library will have the same behavior. If a
211
+ /// property is converted from a stored property to a computed property, the
212
+ /// result of `offset(of:)` changes to `nil`. That kind of conversion is
213
+ /// non-breaking in other contexts, but would trigger a runtime error if the
214
+ /// result of `offset(of:)` is force-unwrapped.
197
215
@_inlineable // FIXME(sil-serialize-all)
198
216
@_transparent
199
217
public static func offset( of key: PartialKeyPath < T > ) -> Int ? {
0 commit comments