-
Notifications
You must be signed in to change notification settings - Fork 208
Generalize ContiguousBytes to be noncopyable and nonescapable for spans #1565
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
base: main
Are you sure you want to change the base?
Changes from all commits
9760ccc
fd55250
6f4d2b9
d71aef0
892f17b
ec458d8
d6e88d2
f8b3fbe
d5e426f
708c11c
5fc7a2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,16 +13,57 @@ | |
| //===--- ContiguousBytes --------------------------------------------------===// | ||
|
|
||
| /// Indicates that the conforming type is a contiguous collection of raw bytes | ||
| /// whose underlying storage is directly accessible by withUnsafeBytes. | ||
| /// whose underlying storage is directly accessible by withBytes. | ||
| @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) | ||
| public protocol ContiguousBytes { | ||
| public protocol ContiguousBytes: ~Escapable, ~Copyable { | ||
| #if !hasFeature(Embedded) | ||
| /// Calls the given closure with the contents of underlying storage. | ||
| /// | ||
| /// - note: Calling `withUnsafeBytes` multiple times does not guarantee that | ||
| /// the same buffer pointer will be passed in every time. | ||
| /// - warning: The buffer argument to the body should not be stored or used | ||
| /// outside of the lifetime of the call to the closure. | ||
| func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R | ||
| #else | ||
| /// Calls the given closure with the contents of underlying storage. | ||
| /// | ||
| /// - note: Calling `withUnsafeBytes` multiple times does not guarantee that | ||
| /// the same buffer pointer will be passed in every time. | ||
| /// - warning: The buffer argument to the body should not be stored or used | ||
| /// outside of the lifetime of the call to the closure. | ||
| func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R | ||
| #endif | ||
|
|
||
| /// Calls the given closure with the contents of underlying storage. | ||
| /// | ||
| /// - note: Calling `withBytes` multiple times does not guarantee that | ||
| /// the same span will be passed in every time. | ||
| @available(FoundationPreview 6.3, *) | ||
| func withBytes<R, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R | ||
| } | ||
|
|
||
| extension ContiguousBytes where Self: ~Escapable, Self: ~Copyable { | ||
| /// Calls the given closure with the contents of underlying storage. | ||
| /// | ||
| /// - note: Calling `withBytes` multiple times does not guarantee that | ||
| /// the same span will be passed in every time. | ||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| #if !hasFeature(Embedded) | ||
| do { | ||
| return try withUnsafeBytes { (buffer) in | ||
| try body(buffer.bytes) | ||
| } | ||
| } catch let error { | ||
| // Note: withUnsafeBytes is rethrowing, so we have an "any Error" here that needs casting. | ||
| throw error as! E | ||
| } | ||
| #else | ||
| return try withUnsafeBytes { (buffer) throws(E) in | ||
| try body(buffer.bytes) | ||
| } | ||
| #endif | ||
| } | ||
| } | ||
|
|
||
| //===--- Collection Conformances ------------------------------------------===// | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the array types implement |
||
|
|
@@ -43,69 +84,274 @@ extension ContiguousArray : ContiguousBytes where Element == UInt8 { } | |
|
|
||
| @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) | ||
| extension UnsafeRawBufferPointer : ContiguousBytes { | ||
| @inlinable | ||
| public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| #if !hasFeature(Embedded) | ||
| // Historical ABI | ||
| @usableFromInline | ||
| @abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) | ||
| func __abi__withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| return try body(self) | ||
| } | ||
| #endif | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| return try body(self) | ||
| } | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| return try body(bytes) | ||
| } | ||
| } | ||
|
|
||
| @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) | ||
| extension UnsafeMutableRawBufferPointer : ContiguousBytes { | ||
| @inlinable | ||
| public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| #if !hasFeature(Embedded) | ||
| // Historical ABI | ||
| @usableFromInline | ||
| @abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) | ||
| func __abi__withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| return try body(UnsafeRawBufferPointer(self)) | ||
| } | ||
| #endif | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| return try body(UnsafeRawBufferPointer(self)) | ||
| } | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| return try body(bytes) | ||
| } | ||
| } | ||
|
|
||
| // FIXME: When possible, expand conformance to `where Element : Trivial`. | ||
| @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) | ||
| extension UnsafeBufferPointer : ContiguousBytes where Element == UInt8 { | ||
| @inlinable | ||
| public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| #if !hasFeature(Embedded) | ||
| @usableFromInline | ||
| @abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) | ||
| func __abi__withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| return try body(UnsafeRawBufferPointer(self)) | ||
| } | ||
| #endif | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| return try body(UnsafeRawBufferPointer(self)) | ||
| } | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| return try body(span.bytes) | ||
| } | ||
| } | ||
|
|
||
| // FIXME: When possible, expand conformance to `where Element : Trivial`. | ||
| @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) | ||
| extension UnsafeMutableBufferPointer : ContiguousBytes where Element == UInt8 { | ||
| @inlinable | ||
| public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| #if !hasFeature(Embedded) | ||
| @usableFromInline | ||
| @abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) | ||
| func __abi__withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| return try body(UnsafeRawBufferPointer(self)) | ||
| } | ||
| #endif | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| return try body(UnsafeRawBufferPointer(self)) | ||
| } | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| return try body(span.bytes) | ||
| } | ||
| } | ||
|
|
||
| // FIXME: When possible, expand conformance to `where Element : Trivial`. | ||
| @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) | ||
| extension EmptyCollection : ContiguousBytes where Element == UInt8 { | ||
| @inlinable | ||
| public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| #if !hasFeature(Embedded) | ||
| @usableFromInline | ||
| @abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) | ||
| func __abi__withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| return try body(UnsafeRawBufferPointer(start: nil, count: 0)) | ||
| } | ||
| #endif | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| return try body(UnsafeRawBufferPointer(start: nil, count: 0)) | ||
| } | ||
| } | ||
|
|
||
| // FIXME: When possible, expand conformance to `where Element : Trivial`. | ||
| @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) | ||
| extension CollectionOfOne : ContiguousBytes where Element == UInt8 { | ||
| @inlinable | ||
| public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| #if !hasFeature(Embedded) | ||
| @usableFromInline | ||
| @abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) | ||
| func __abi__withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { | ||
| let element = self.first! | ||
| return try Swift.withUnsafeBytes(of: element) { | ||
| return try body($0) | ||
| } | ||
| } | ||
| #endif | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| let element = self.first! | ||
| return try Swift.withUnsafeBytes(of: element) { (buffer) throws(E) in | ||
| return try body(buffer) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| //===--- Conditional Conformances -----------------------------------------===// | ||
|
|
||
| @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) | ||
| extension Slice : ContiguousBytes where Base : ContiguousBytes { | ||
| public func withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { | ||
| #if !hasFeature(Embedded) | ||
| @usableFromInline | ||
| @abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) | ||
| func __abi__withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { | ||
| let offset = base.distance(from: base.startIndex, to: self.startIndex) | ||
| return try base.withUnsafeBytes { ptr in | ||
| let slicePtr = ptr.baseAddress?.advanced(by: offset) | ||
| let sliceBuffer = UnsafeRawBufferPointer(start: slicePtr, count: self.count) | ||
| return try body(sliceBuffer) | ||
| } | ||
| } | ||
| #endif | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<ResultType, ErrorType>(_ body: (UnsafeRawBufferPointer) throws(ErrorType) -> ResultType) throws(ErrorType) -> ResultType { | ||
| let offset = base.distance(from: base.startIndex, to: self.startIndex) | ||
|
|
||
| #if !hasFeature(Embedded) | ||
| do { | ||
| return try base.withUnsafeBytes { (ptr) in | ||
| let slicePtr = ptr.baseAddress?.advanced(by: offset) | ||
| let sliceBuffer = UnsafeRawBufferPointer(start: slicePtr, count: self.count) | ||
| return try body(sliceBuffer) | ||
| } | ||
| } catch let error { | ||
| // Note: withUnsafeBytes is rethrowing, so we have an "any Error" here that needs casting. | ||
| throw error as! ErrorType | ||
| } | ||
| #else | ||
| return try base.withUnsafeBytes { (ptr) throws(ErrorType) in | ||
| let slicePtr = ptr.baseAddress?.advanced(by: offset) | ||
| let sliceBuffer = UnsafeRawBufferPointer(start: slicePtr, count: self.count) | ||
| return try body(sliceBuffer) | ||
| } | ||
| #endif | ||
| } | ||
| } | ||
|
|
||
| //===--- Span Conformances -----------------------------------------===// | ||
|
|
||
| @available(FoundationPreview 6.3, *) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a public conformance, right? We would need an API review.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Here's the proposal PR: #1582 |
||
| extension RawSpan: ContiguousBytes { } | ||
|
|
||
| extension RawSpan { | ||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| return try body(self) | ||
| } | ||
| } | ||
|
|
||
| @available(FoundationPreview 6.3, *) | ||
| extension MutableRawSpan: ContiguousBytes { } | ||
|
|
||
| extension MutableRawSpan { | ||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| return try body(bytes) | ||
| } | ||
| } | ||
|
|
||
| @available(FoundationPreview 6.3, *) | ||
| extension OutputRawSpan: ContiguousBytes { } | ||
|
|
||
| extension OutputRawSpan { | ||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| try bytes.withUnsafeBytes(body) | ||
| } | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| try body(bytes) | ||
| } | ||
| } | ||
|
|
||
| @available(FoundationInlineArray 6.3, *) | ||
| extension UTF8Span: ContiguousBytes { } | ||
|
|
||
| @available(FoundationInlineArray 6.2, *) | ||
| extension UTF8Span { | ||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| try span.withUnsafeBytes(body) | ||
| } | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| return try body(span.bytes) | ||
| } | ||
| } | ||
|
|
||
| @available(FoundationPreview 6.3, *) | ||
| extension Span: ContiguousBytes where Element == UInt8 { } | ||
|
|
||
| extension Span where Element == UInt8 { | ||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| try body(bytes) | ||
| } | ||
| } | ||
|
|
||
| @available(FoundationPreview 6.3, *) | ||
| extension MutableSpan: ContiguousBytes where Element == UInt8 { } | ||
|
|
||
| extension MutableSpan where Element == UInt8 { | ||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| try body(bytes) | ||
| } | ||
| } | ||
|
|
||
| @available(FoundationPreview 6.3, *) | ||
| extension OutputSpan: ContiguousBytes where Element == UInt8 { } | ||
|
|
||
| extension OutputSpan where Element == UInt8 { | ||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| try span.withUnsafeBytes(body) | ||
| } | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| try body(span.bytes) | ||
| } | ||
| } | ||
|
|
||
| @available(FoundationInlineArray 6.3, *) | ||
| extension InlineArray: ContiguousBytes where Element == UInt8 { } | ||
|
|
||
| @available(FoundationInlineArray 6.2, *) | ||
| extension InlineArray where Element == UInt8 { | ||
| @_alwaysEmitIntoClient | ||
| public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R { | ||
| return try span.withUnsafeBytes(body) | ||
| } | ||
|
|
||
| @_alwaysEmitIntoClient | ||
| public func withBytes<R: ~Copyable, E>(_ body: (RawSpan) throws(E) -> R) throws(E) -> R { | ||
| try body(span.bytes) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just confirming, are there any ABI implications on this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it is not ABI-breaking (or source-breaking) to make this change.