-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Initial draft of simple non-Copyable protocols proposal #3022
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+177
−0
Merged
Changes from 1 commit
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
34ce90d
Initial draft of simple non-Copyable protocols proposal
airspeedswift 9e0fc26
Apply suggestions from code review
airspeedswift ebb19e5
Update proposals/NNNN-make-stdlib-protocols-escapable.md
airspeedswift 1d23422
Rename NNNN-make-stdlib-protocols-escapable.md to NNNN-support-simple…
airspeedswift 0d34829
Update NNNN-support-non-copyable-simple-protocols.md
airspeedswift 8ab0287
Update NNNN-support-non-copyable-simple-protocols.md
airspeedswift b21920b
Assign SE-0499 and put into review.
hborla File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| # Support ~Copyable, ~Escapable in simple standard library protocols | ||
|
|
||
| * Proposal: [SE-NNNN](NNNN-make-stdlib-protocols-escapable.md) | ||
| * Authors: [Ben Cohen](https://github.com/airspeedswift) | ||
| * Review Manager: TBD | ||
| * Status: **Awaiting Review** | ||
| * Implementation: [swiftlang/swift#85079](https://github.com/swiftlang/swift/pull/85079) | ||
|
|
||
| ## Introduction | ||
|
|
||
| The following protocols will be marked as refining `~Copyable` and `~Escapable`: | ||
|
|
||
| - `Equatable`, `Comparable`, and `Hashable` | ||
| - `CustomStringConvertible` and `CustomDebugStringConvertible` | ||
| - `TextOutputStream` and `TextOutputStreamable` | ||
| `LosslessStringConvertible` will be marked as refining `~Copyable`. | ||
|
|
||
| Additionally, `Optional` and `Result` will have their `Equatable` and `Hashable` conformances updated to support `~Copyable` and `~Escapable` elements. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Several standard library protocols have simple requirements not involving associated types or elaborate generic implementations. | ||
|
|
||
| - `Equatable` and `Comparable` tend only to need to borrow the left- and right-hand side | ||
| of their one essential operator in order to equate or compare values; | ||
| - `Hashable` only needs operations to fold hash values produced by a borrowed value | ||
| into a `Hasher`; | ||
| - The various `String` producing or consuming operations only | ||
| need to borrow their operand to turn it into a string (as `CustomStringConvertible.description` | ||
| does), or can create a non-`Copyable` values (as `LosslessStringConvertible.init?` could). | ||
|
|
||
| Use of these protocols is ubiquitous in Swift code, and this can be a major impediment to introducing non-`Copyable` types into a codebase. For example, it might be desirable to drop in a [`UniqueArray`](https://swiftpackageindex.com/apple/swift-collections/1.3.0/documentation/basiccontainers/uniquearray) to replace an `Array` in some code where the copy-on-write checks are proving prohibitively expensive. But this cannot be done if that code is relying on that array type being `Hashable`. | ||
|
|
||
| ## Proposed solution | ||
|
|
||
| The following signatures in the standard library will be changed. None of these | ||
| changes effect the existing semantics, just allow them to be applied to non-`Copyable`/`Escapable` types. | ||
|
|
||
| ``` | ||
airspeedswift marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| protocol Equatable: ~Copyable, ~Escapable { | ||
| static func == (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| } | ||
|
|
||
| extension Equatable where Self: ~Copyable & ~Escapable { | ||
| static func != (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| } | ||
|
|
||
| protocol Comparable: Equatable, ~Copyable, ~Escapable { | ||
| static func < (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| static func <= (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| static func >= (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| static func > (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| } | ||
|
|
||
| extension Comparable where Self: ~Copyable & ~Escapable { | ||
| static func <= (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| static func >= (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| static func > (lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| } | ||
|
|
||
| protocol Hashable: Equatable & ~Copyable & ~Escapable { } | ||
|
|
||
| struct Hasher { | ||
| mutating func combine<H: Hashable & ~Copyable & ~Escapable>(_ value: borrowing H) | ||
| } | ||
|
|
||
| extension Optional: Equatable where Wrapped: Equatable & ~Copyable & ~Escapable { | ||
| public static func ==(lhs: borrowing Wrapped?, rhs: borrowing Wrapped?) -> Bool { | ||
| } | ||
|
|
||
| extension Optional: Hashable where Wrapped: Hashable & ~Copyable & ~Escapable { | ||
| func hash(into hasher: inout Hasher) | ||
| var hashValue: Int | ||
| } | ||
|
|
||
| protocol LosslessStringConvertible: CustomStringConvertible, ~Copyable { } | ||
|
|
||
| protocol TextOutputStream: ~Copyable, ~Escapable { } | ||
| protocol TextOutputStreamable: ~Copyable & ~Escapable { } | ||
|
|
||
| protocol CustomStringConvertible: ~Copyable, ~Escapable { } | ||
| protocol CustomDebugStringConvertible: ~Copyable, ~Escapable { } | ||
| protocol LosslessStringConvertible: CustomStringConvertible, ~Copyable { } | ||
airspeedswift marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| extension Result: Equatable where Success: Equatable & ~Copyable, Failure: Equatable { | ||
| public static func ==(lhs: borrowing Self, rhs: borrowing Self) -> Bool | ||
| } | ||
|
|
||
| extension DefaultStringInterpolation | ||
| mutating func appendInterpolation<T>( | ||
| _ value: borrowing T | ||
| ) where T: TextOutputStreamable & ~Copyable & ~Escapable { } | ||
|
|
||
| mutating func appendInterpolation<T>( | ||
| _ value: borrowing T | ||
| ) where T: CustomStringConvertible & ~Copyable & ~Escapable { } | ||
| } | ||
| ``` | ||
|
|
||
| `LosslessStringConvertible` explicitly does not conform to `~Escapable` since this | ||
| would require a lifetime for the created value, something that requires | ||
| further language features to express. | ||
|
|
||
| Note that underscored protocol requirements and methods in extensions are omitted | ||
| but will be updated as necessary. | ||
|
|
||
| ## Source compatibility | ||
|
|
||
| The design of `~Copyable` and `~Escapable` explicitly allows for source compatibility with | ||
| retroactive adoption, as extensions that do not restate these restrictions assume compatability. | ||
airspeedswift marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| So no clients of the standard library should need to alter their existing source except | ||
| with the goal of extending it to work with more types. | ||
|
|
||
| ## ABI compatibility | ||
|
|
||
| As with previous retroactive adoption, the existing pre-inverse-generics features used in the | ||
| standard library will be applied to preserve the same symbols as existed before. | ||
|
|
||
| The ABI implications of back deployment of these protocols is being investigated. It is hoped | ||
| this can be made to work – if not, these new features may need to be gated under a minimum | ||
| deployment target on ABI-stable platforms. | ||
|
|
||
| ## Future directions | ||
|
|
||
| There are many other protocols that would benefit from this approach that are not included. | ||
|
|
||
| Most of these are due to the presence of associated types (for example, `RangeExpression.Bound`), which is not yet supported. Once that is a supported feature, these protocols can be similarly refined with a follow-on proposal. | ||
|
|
||
| `Codable` and `Decodable` do not have associated types – but their implementation is heavily | ||
| generic, may not generalize to noncopyable types, and is out of scope for this proposal. | ||
|
|
||
| Now that these protocols support them, types such as `InlineArray` and `Span` could be made | ||
| to conditionally conform to `Hashable`, as `Array` does. There is some debate to be had about | ||
| the semantics of `Equatable` conformance for `Span` (though probably not for `InlineArray`), | ||
| and this should be the subject of a future proposal. | ||
|
|
||
| Allowing more types to be `Custom*StringConvertible where Self: ~Copyable & ~Escapable`, such as `Optional`, requires further work on the `print` infrastructure to be able to hand such types, so is out of scope for this proposal. | ||
airspeedswift marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## Alternatives considered | ||
|
|
||
| It can be argued that non-`Copyable` types have identity, and therefore should not be `Equatable` | ||
|
|
||
airspeedswift marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| in the current sense of the protocol. In particular: | ||
|
|
||
| > Equality implies substitutability—any two instances that compare equally | ||
| > can be used interchangeably in any code that depends on their values. | ||
|
|
||
| One might say that a noncopyable string type (one that does not require reference counting | ||
| or copy-on-write-checking overhead) should not be considered "substitutable" for another. | ||
|
|
||
| However, the definition also states: | ||
|
|
||
| > **Equality is Separate From Identity.** The identity of a class instance is not part of an | ||
| > instance's value. | ||
|
|
||
| Authors of non-`Copyable` types will need to decide for themselves whether their type should | ||
| be `Equatable` and what it means. The standard library should allow it to be possible, though. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.