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
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
-
publicclassDataPacket {
477
-
@unsafepubliclet 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
-
extensionDataPacket {
485
-
publicvar 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
-
extensionDataPacket {
495
-
publicvar 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
-
523
471
## Alternatives considered
524
472
525
473
### `@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
631
579
* 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.
632
580
* 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.
633
581
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
+
publicclassDataPacket {
588
+
@unsafepubliclet 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
+
extensionDataPacket {
596
+
publicvar 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
+
extensionDataPacket {
606
+
publicvar 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
+
634
634
### Optional `message` for the `@unsafe` attribute
635
635
636
636
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