Skip to content

Commit d52a345

Browse files
committed
Move the "overloading to stage in safe APIs" section to Alternatives Considered
We probably don't want to ever do this, so move it out of Future Directions
1 parent 11f5ff9 commit d52a345

File tree

1 file changed

+52
-52
lines changed

1 file changed

+52
-52
lines changed

proposals/nnnn-strict-memory-safety.md

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -468,58 +468,6 @@ We have several options here:
468468
if case unsafe .rawOffsetIntoGlobalArray(let offset) = weirdAddress { ... }
469469
```
470470

471-
### Overloading to stage in safe APIs
472-
473-
When adopting the strict memory safety mode, it's likely that a Swift module will want to replace existing APIs that traffic in unsafe types (such as `UnsafeMutablePointer`) with safer equivalents (such as `Span`). To retain compatibility for older clients, the existing APIs will need to be left in place. Unfortunately, this might mean that the best name for the API is already taken. For example, perhaps we have a data packet that exposes its bytes via a property:
474-
475-
```swift
476-
public class DataPacket {
477-
@unsafe public let bytes: UnsafeRawBufferPointer
478-
}
479-
```
480-
481-
The `bytes` property is necessarily unsafe. Far better would be to produce a `RawSpan`, which we can easily do with another property:
482-
483-
```swift
484-
extension DataPacket {
485-
public var byteSpan: RawSpan
486-
}
487-
```
488-
489-
Clients using the existing `bytes` will continue to work, and those that care about memory safety can choose to move to `byteSpan`. All of this works, but is somewhat annoying because the good name, `bytes`, has been taken for the API we no longer want to use.
490-
491-
Swift does allow type-based overloading, including on the type of properties, so one could introduce an overloaded `bytes` property, like this:
492-
493-
```swift
494-
extension DataPacket {
495-
public var bytes: RawSpan
496-
}
497-
```
498-
499-
This works for code that accesses `bytes` and then uses it in a context where type inference can figure out whether we need an `UnsafeRawBufferPointer` or a `RawSpan`, but fails if that context does not exist:
500-
501-
```swift
502-
let unsafeButGoodBytes: UnsafeRawBufferPointer = dataPacket.bytes // ok, uses @unsafe bytes
503-
let goodBytes: RawSpan = dataPacket.bytes // ok, uses safe bytes
504-
let badBytes = dataPacket.bytes // error: ambiguous!
505-
```
506-
507-
We could consider extending Swift's overloading rules to make this kind of evolution possible. For example, one could introduce a pair of rules into the language:
508-
509-
1. When strict memory safety checking is enabled, `@unsafe` declarations are dis-favored vs. safe ones, so the unsafe `bytes: UnsafeRawBufferPointer` would be a worse solution for type inference to pick than the safe alternative, `bytes: RawSpan`.
510-
511-
2. Overloads that were introduced to replace unsafe declarations could be marked with a new attribute `@safe(unsafeDisfavored)` so that they would be disfavored only when building with strict memory safety checking disabled.
512-
513-
Assuming these rules, and that the safe `bytes: RawSpan` had the `@safe(unsafeDisfavored)` attribute, the example uses of `DataPacket` would resolve as follows:
514-
515-
* `unsafeButGoodBytes` would always be initialized with the unsafe `bytes`. If strict memory safety were enabled, this use would produce a warning.
516-
* `goodBytes` would always be initialized with the safe `bytes`.
517-
* `badBytes` would be initialized differently based on whether strict memory safety was enabled:
518-
* If enabled, `badBytes` would choose the safe version of `bytes` to produce the safest code, because the unsafe one is disfavored (rule #1).
519-
* If disabled, `badBytes` would choose the unsafe version of `bytes` to provide source compatibility with existing code, because the safe one is disfavored (rule #2).
520-
521-
There are downsides to this approach. It partially undermines the source compatibility story for the strict safety mode, because type inference now behaves differently when the mode is enabled. That means, for example, there might be errors---not warnings---because some code like `badBytes` above would change behavior, causing additional failures. Changing the behavior of type inference is also risky in an of itself, because it is not always easy to reason about all of the effects of such a change. That said, the benefit of being able to move toward a more memory-safe future might be worth it.
522-
523471
## Alternatives considered
524472

525473
### `@unsafe` implying `unsafe` throughout a function body
@@ -631,6 +579,58 @@ This proposal introduced strict safety checking as an opt in mode and not an [*u
631579
* Interoperability with the C family of languages is an important feature for Swift. Most C(++) APIs are unlikely to ever adopt the safety-related attributes described above, which means that enabling strict safety checking by default would undermine the usability of C(++) interoperability.
632580
* Swift's current (non-strict) memory safety by default is likely to be good enough for the vast majority of users of Swift, so the benefit of enabling stricter checking by default is unlikely to be worth the disruption it would cause.
633581

582+
### Overloading to stage in safe APIs
583+
584+
When adopting the strict memory safety mode, it's likely that a Swift module will want to replace existing APIs that traffic in unsafe types (such as `UnsafeMutablePointer`) with safer equivalents (such as `Span`). To retain compatibility for older clients, the existing APIs will need to be left in place. Unfortunately, this might mean that the best name for the API is already taken. For example, perhaps we have a data packet that exposes its bytes via a property:
585+
586+
```swift
587+
public class DataPacket {
588+
@unsafe public let bytes: UnsafeRawBufferPointer
589+
}
590+
```
591+
592+
The `bytes` property is necessarily unsafe. Far better would be to produce a `RawSpan`, which we can easily do with another property:
593+
594+
```swift
595+
extension DataPacket {
596+
public var byteSpan: RawSpan
597+
}
598+
```
599+
600+
Clients using the existing `bytes` will continue to work, and those that care about memory safety can choose to move to `byteSpan`. All of this works, but is somewhat annoying because the good name, `bytes`, has been taken for the API we no longer want to use.
601+
602+
Swift does allow type-based overloading, including on the type of properties, so one could introduce an overloaded `bytes` property, like this:
603+
604+
```swift
605+
extension DataPacket {
606+
public var bytes: RawSpan
607+
}
608+
```
609+
610+
This works for code that accesses `bytes` and then uses it in a context where type inference can figure out whether we need an `UnsafeRawBufferPointer` or a `RawSpan`, but fails if that context does not exist:
611+
612+
```swift
613+
let unsafeButGoodBytes: UnsafeRawBufferPointer = dataPacket.bytes // ok, uses @unsafe bytes
614+
let goodBytes: RawSpan = dataPacket.bytes // ok, uses safe bytes
615+
let badBytes = dataPacket.bytes // error: ambiguous!
616+
```
617+
618+
We could consider extending Swift's overloading rules to make this kind of evolution possible. For example, one could introduce a pair of rules into the language:
619+
620+
1. When strict memory safety checking is enabled, `@unsafe` declarations are dis-favored vs. safe ones, so the unsafe `bytes: UnsafeRawBufferPointer` would be a worse solution for type inference to pick than the safe alternative, `bytes: RawSpan`.
621+
622+
2. Overloads that were introduced to replace unsafe declarations could be marked with a new attribute `@safe(unsafeDisfavored)` so that they would be disfavored only when building with strict memory safety checking disabled.
623+
624+
Assuming these rules, and that the safe `bytes: RawSpan` had the `@safe(unsafeDisfavored)` attribute, the example uses of `DataPacket` would resolve as follows:
625+
626+
* `unsafeButGoodBytes` would always be initialized with the unsafe `bytes`. If strict memory safety were enabled, this use would produce a warning.
627+
* `goodBytes` would always be initialized with the safe `bytes`.
628+
* `badBytes` would be initialized differently based on whether strict memory safety was enabled:
629+
* If enabled, `badBytes` would choose the safe version of `bytes` to produce the safest code, because the unsafe one is disfavored (rule #1).
630+
* If disabled, `badBytes` would choose the unsafe version of `bytes` to provide source compatibility with existing code, because the safe one is disfavored (rule #2).
631+
632+
There are downsides to this approach. It partially undermines the source compatibility story for the strict safety mode, because type inference now behaves differently when the mode is enabled. That means, for example, there might be errors---not warnings---because some code like `badBytes` above would change behavior, causing additional failures. Changing the behavior of type inference is also risky in an of itself, because it is not always easy to reason about all of the effects of such a change. That said, the benefit of being able to move toward a more memory-safe future might be worth it.
633+
634634
### Optional `message` for the `@unsafe` attribute
635635

636636
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.

0 commit comments

Comments
 (0)