You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: proposals/nnnn-strict-memory-safety.md
+46-6Lines changed: 46 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,7 +34,7 @@ While there are a number of potential definitions for memory safety, the one pro
34
34
35
35
Since its inception, Swift has provided memory safety for the first four dimensions. Lifetime safety is provided for reference types by automatic reference counting and for value types via [memory exclusivity](https://www.swift.org/blog/swift-5-exclusivity/); bounds safety is provided by bounds-checking on `Array` and other collections; type safety is provided by safe features for casting (`as?` , `is` ) and `enum` s; and initialization safety is provided by “definite initialization”, which doesn’t allow a variable to be accessed until it has been defined. Swift 6’s strict concurrency checking extends Swift’s memory safety guarantees to the last dimension.
36
36
37
-
Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safely requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types be non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited.
37
+
Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safety requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types be non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited.
38
38
39
39
## Proposed solution
40
40
@@ -106,7 +106,7 @@ extension Array<Int> {
106
106
107
107
The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `c_library_sum_function` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`.
108
108
109
-
Note that we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe`.
109
+
Note that we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe`. Additionally, the `@unsafe` attribute is available for all Swift code, even if it doesn't itself enable the strict safety checking described in this proposal.
110
110
111
111
### Incremental adoption
112
112
@@ -197,12 +197,12 @@ The following language constructs are always considered to be unsafe:
197
197
198
198
*`unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. Uses of `unowned(unsafe)` entities are not memory-safe.
199
199
*`unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives.
200
-
*`@exclusivity(unchecked)`: Used to remove dynamic exclusivity checks from a particular variable, which can mean that dynamic exclusivity violations go undetected at run time, causing a memory safety violation.
200
+
*`@exclusivity(unchecked)`: Used to remove dynamic exclusivity checks from a particular variable, which can mean that dynamic exclusivity violations go undetected at run time, causing a memory safety violation. Uses of `@exclusivity(unchecked)` entities are not memory-safe.
201
201
202
202
The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode):
203
203
204
204
*`nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. Uses of `nonisolated(unsafe)` entities are not memory-safe.
205
-
*`@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues.
205
+
*`@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. The `@preconcurrency` import will need to be annotated with `@unsafe` in the strict dsafety mode.
206
206
207
207
### Unsafe standard library APIs
208
208
@@ -317,7 +317,7 @@ The `unsafe` keyword in this proposal will be introduced as a contextual keyword
317
317
funcunsafe(_body: () ->Void) { }
318
318
319
319
unsafe {
320
-
// currently calls 'unsafe(_:)', but will become and unsafe expression
320
+
// currently calls 'unsafe(_:)', but will become an unsafe expression
321
321
}
322
322
```
323
323
@@ -408,6 +408,46 @@ There are downsides to this approach. It partially undermines the source compati
408
408
409
409
## Alternatives considered
410
410
411
+
### `@unsafe` implying `unsafe` throughout a function body
412
+
413
+
A function marked `@unsafe` is unsafe to use, so any clients that have enabled strict safety checking will need to put uses of the function into an `unsafe` expression. The implementation of that function is likely to use unsafe code (possibly a lot of it), which could result in a large number of annotations:
414
+
415
+
```swift
416
+
extensionUnsafeMutableBufferPointer {
417
+
@unsafepublicfuncswapAt(_i: Int, _j: Int) {
418
+
guard i != j else { return }
419
+
precondition(i >=0&& j >=0)
420
+
precondition(unsafe i < endIndex && j < endIndex)
421
+
@unsafelet pi =unsafe (_position!+ i)
422
+
@unsafelet pj =unsafe (_position!+ j)
423
+
@unsafelet tmp = unsafe pi.move()
424
+
unsafe pi.moveInitialize(from: pj, count: 1)
425
+
unsafe pj.initialize(to: tmp)
426
+
}
427
+
}
428
+
```
429
+
430
+
Now, it is very likely that an `@unsafe` function is going to make use of other unsafe constructs, so we could choose to make `@unsafe` on a function acknowledge all uses of unsafe code within its definition. For example, this would mean that marking `swapAt` with `@unsafe` means that one need not have any `unsafe` expressions in its body:
431
+
432
+
```swift
433
+
extensionUnsafeMutableBufferPointer {
434
+
@unsafepublicfuncswapAt(_i: Int, _j: Int) {
435
+
guard i != j else { return }
436
+
precondition(i >=0&& j >=0)
437
+
precondition(i < endIndex && j < endIndex)
438
+
let pi = (_position!+ i)
439
+
let pj = (_position!+ j)
440
+
let tmp = pi.move()
441
+
pi.moveInitialize(from: pj, count: 1)
442
+
pj.initialize(to: tmp)
443
+
}
444
+
}
445
+
```
446
+
447
+
This approach reduces the annotation burden in unsafe code, but makes it much harder to tell exactly what aspects of the implementation are unsafe. Indeed, even unsafe functions should still strive to minimize the use of unsafe constructs, and benefit from having the actual unsafe behavior marked in the source. It also conflates the notion of "exposes an unsafe interface" from "has an unsafe implementation".
448
+
449
+
Rust's `unsafe` functions have this behavior, where an `unsafe fn` in Rust implies an `unsafe { ... }` block around the entire function body. [Rust RFC #2585](https://rust-lang.github.io/rfcs/2585-unsafe-block-in-unsafe-fn.html) argues for Rust to remove this behavior; the motivation there generally applies to Swift as well.
450
+
411
451
### `@safe(unchecked)` attribute to allow unsafe code
412
452
413
453
Early iterations of this proposal introduced a `@safe(unchecked)` attribute as an alternative to `unsafe` expressions. The `@safe(unchecked)` attribute would be placed on a function to suppress diagnostics about use of unsafe constructs within its definition. For our `sum` example, this means one would write:
@@ -461,4 +501,4 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh
461
501
462
502
## Acknowledgments
463
503
464
-
This proposal has been greatly improved by the feedback from Félix Cloutier, Gábor Horváth, Frederick Kellison-Linn, Karl Wagner, and Xiaodi Wu.
504
+
This proposal has been greatly improved by the feedback from Félix Cloutier, Geoff Garen, Gábor Horváth, Frederick Kellison-Linn, Karl Wagner, and Xiaodi Wu.
0 commit comments