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/0458-strict-memory-safety.md
+58-6Lines changed: 58 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -217,7 +217,7 @@ There are a few exemptions to the rule that any unsafe constructs within the sig
217
217
218
218
### `@safe` attribute
219
219
220
-
Like the `@unsafe` attribute, the `@safe` attribute is used on declarations whose signatures involve unsafe types. However, the `@safe` attribute means that the declaration is consider safe to use even though its signature includes unsafe types. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those particular operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`:
220
+
Like the `@unsafe` attribute, the `@safe` attribute is used on declarations whose signatures involve unsafe types. However, the `@safe` attribute means that the declaration is considered safe to use even though its signature includes unsafe types. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those particular operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`:
221
221
222
222
```swift
223
223
extensionUnsafeBufferPointer {
@@ -430,11 +430,6 @@ Other than the source break above, the introduction of this strict safety checki
430
430
431
431
The attributes, `unsafe` expression, and strict memory-safety checking model proposed here have no impact on ABI.
432
432
433
-
## Revision history
434
-
435
-
***Revision 2 (following first review)**
436
-
* Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe.
437
-
438
433
## Future Directions
439
434
440
435
### The `SerialExecutor` and `Actor` protocols
@@ -489,6 +484,57 @@ We have several options here:
489
484
490
485
## Alternatives considered
491
486
487
+
### Prohibiting unsafe conformances and overrides entirely
488
+
489
+
This proposal introduces two places where polymorphism interacts with unsafety: protocol conformances and overrides. In both cases, a safe abstraction (e.g., a superclass or protocol) has a specific implementation that is unsafe, and there is a way to note the unsafety:
490
+
491
+
* When overriding a safe declaration with an unsafe one, the overriding subclass must be marked `@unsafe`.
492
+
* When implementing a safe protocol requirement with an unsafe declaration, the corresponding conformance must be marked `@unsafe`.
493
+
494
+
In both cases, the current proposal will consider uses of the type (in the overriding case) or conformance (for that case) as unsafe, respectively. However, that unsafety is not localized, because code that's generally safe can now cause safety problems when calling through polymorphic operations. For example, consider a function that operates on a general collection:
Calling this function with an unsafe buffer pointer will produce a diagnostic due to the use of the unsafe conformance of `UnsafeBufferPointer` to `Collection`:
501
+
502
+
```swift
503
+
let result =parse(unsafeBufferPointer) // warning: use of unsafe conformance
504
+
```
505
+
506
+
Marking the call as `unsafe` will address the diagnostic. However, because `UnsafeBufferPointer` doesn't perform bounds checking, the `parse` function itself can introduce a memory safety problem if it subscripts into the collection with an invalid index. There isn't a way to communicate how the code that is `unsafe` is addressing memory safety issues within the context of the call.
507
+
508
+
This proposal could prohibit use of unsafe conformances and overrides entirely, for example by making it impossible to suppress the diagnostics associated with their definition and use. This would require the `parse(unsafeBufferPointer)` call to be refactored to avoid the unsafe conformance, for example by introducing a wrapper type:
let wrapper = unsafe ImmortalBufferWrapper(withImmortalBuffer: buffer)
531
+
let result =parse(wrapper)
532
+
```
533
+
534
+
This approach is better than the prior one: it improves bounds safety by introducing bounds checking. It clearly documents the assumptions made around lifetime safety. It is both functionally safer (due to bounds checks) and makes it easier to reason that the `unsafe` is correctly used. It does require a lot more code, and the code itself requires careful reasoning about safety (e.g., the right preconditions for bounds checking; the right naming to capture the lifetime implications).
535
+
536
+
Unsafe conformances and overrides remain part of this proposal because prohibiting them doesn't fundamentally change the safety model. Rather, it requires the introduction of more abstractions that could be safer--or could just be boilerplate. Swift has a number of constructs that are functionally similar to unsafe conformances, where safety checking can be disabled locally despite that having wide-ranging consequences: `@unchecked Sendable`, `nonisolated(unsafe)`, `unowned(unsafe)`, and `@preconcurrency` all fall into this category.
537
+
492
538
### `@unsafe` implying `unsafe` throughout a function body
493
539
494
540
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:
@@ -654,6 +700,12 @@ There are downsides to this approach. It partially undermines the source compati
654
700
655
701
We could introduce an optional `message` argument to the `@unsafe` attribute, which would allow programmers to indicate *why* the use of a particular declaration is unsafe and, more importantly, how to safely write code that uses it. However, this argument isn't strictly necessary: a comment could provide the same information, and there is established tooling to expose comments to programmers that wouldn't be present for this attribute's message, so we have omitted this feature.
656
702
703
+
## Revision history
704
+
705
+
***Revision 2 (following first review)**
706
+
* Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe.
707
+
* Add an Alternatives Considered section on prohibiting unsafe conformances and overrides.
708
+
657
709
## Acknowledgments
658
710
659
711
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