Skip to content

Commit 576b2fa

Browse files
authored
Generalize concrete implementations of withUnsafeBytes to typed throws for ContiguousBytes conformers (#1929)
1 parent 6c4c14e commit 576b2fa

2 files changed

Lines changed: 184 additions & 23 deletions

File tree

Sources/FoundationEssentials/Data/ContiguousBytes.swift

Lines changed: 113 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,20 @@ extension Data : ContiguousBytes { }
137137

138138
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
139139
extension UnsafeRawBufferPointer : ContiguousBytes {
140-
// TODO: Generalize for typed throws
141-
@inlinable
142-
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
140+
// TODO: This should be limited to DATA_LEGACY_ABI once clients are rebuilt
141+
@available(macOS, obsoleted: 1.0)
142+
@available(iOS, obsoleted: 1.0)
143+
@available(watchOS, obsoleted: 1.0)
144+
@available(tvOS, obsoleted: 1.0)
145+
@available(visionOS, obsoleted: 1.0)
146+
@usableFromInline
147+
@abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R)
148+
func __legacy_withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
149+
return try withUnsafeBytes(body)
150+
}
151+
152+
@_alwaysEmitIntoClient
153+
public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R {
143154
return try body(self)
144155
}
145156

@@ -152,9 +163,20 @@ extension UnsafeRawBufferPointer : ContiguousBytes {
152163

153164
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
154165
extension UnsafeMutableRawBufferPointer : ContiguousBytes {
155-
// TODO: Generalize for typed throws
156-
@inlinable
157-
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
166+
// TODO: This should be limited to DATA_LEGACY_ABI once clients are rebuilt
167+
@available(macOS, obsoleted: 1.0)
168+
@available(iOS, obsoleted: 1.0)
169+
@available(watchOS, obsoleted: 1.0)
170+
@available(tvOS, obsoleted: 1.0)
171+
@available(visionOS, obsoleted: 1.0)
172+
@usableFromInline
173+
@abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R)
174+
func __legacy_withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
175+
return try withUnsafeBytes(body)
176+
}
177+
178+
@_alwaysEmitIntoClient
179+
public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R {
158180
return try body(UnsafeRawBufferPointer(self))
159181
}
160182

@@ -168,9 +190,20 @@ extension UnsafeMutableRawBufferPointer : ContiguousBytes {
168190
// FIXME: When possible, expand conformance to `where Element : Trivial`.
169191
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
170192
extension UnsafeBufferPointer : ContiguousBytes where Element == UInt8 {
171-
// TODO: Generalize for typed throws
172-
@inlinable
173-
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
193+
// TODO: This should be limited to DATA_LEGACY_ABI once clients are rebuilt
194+
@available(macOS, obsoleted: 1.0)
195+
@available(iOS, obsoleted: 1.0)
196+
@available(watchOS, obsoleted: 1.0)
197+
@available(tvOS, obsoleted: 1.0)
198+
@available(visionOS, obsoleted: 1.0)
199+
@usableFromInline
200+
@abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R)
201+
func __legacy_withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
202+
return try withUnsafeBytes(body)
203+
}
204+
205+
@_alwaysEmitIntoClient
206+
public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R {
174207
return try body(UnsafeRawBufferPointer(self))
175208
}
176209

@@ -184,9 +217,20 @@ extension UnsafeBufferPointer : ContiguousBytes where Element == UInt8 {
184217
// FIXME: When possible, expand conformance to `where Element : Trivial`.
185218
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
186219
extension UnsafeMutableBufferPointer : ContiguousBytes where Element == UInt8 {
187-
// TODO: Generalize for typed throws
188-
@inlinable
189-
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
220+
// TODO: This should be limited to DATA_LEGACY_ABI once clients are rebuilt
221+
@available(macOS, obsoleted: 1.0)
222+
@available(iOS, obsoleted: 1.0)
223+
@available(watchOS, obsoleted: 1.0)
224+
@available(tvOS, obsoleted: 1.0)
225+
@available(visionOS, obsoleted: 1.0)
226+
@usableFromInline
227+
@abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R)
228+
func __legacy_withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
229+
return try withUnsafeBytes(body)
230+
}
231+
232+
@_alwaysEmitIntoClient
233+
public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R {
190234
return try body(UnsafeRawBufferPointer(self))
191235
}
192236

@@ -200,9 +244,20 @@ extension UnsafeMutableBufferPointer : ContiguousBytes where Element == UInt8 {
200244
// FIXME: When possible, expand conformance to `where Element : Trivial`.
201245
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
202246
extension EmptyCollection : ContiguousBytes where Element == UInt8 {
203-
// TODO: Generalize for typed throws
204-
@inlinable
205-
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
247+
// TODO: This should be limited to DATA_LEGACY_ABI once clients are rebuilt
248+
@available(macOS, obsoleted: 1.0)
249+
@available(iOS, obsoleted: 1.0)
250+
@available(watchOS, obsoleted: 1.0)
251+
@available(tvOS, obsoleted: 1.0)
252+
@available(visionOS, obsoleted: 1.0)
253+
@usableFromInline
254+
@abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R)
255+
func __legacy_withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
256+
return try withUnsafeBytes(body)
257+
}
258+
259+
@_alwaysEmitIntoClient
260+
public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R {
206261
return try body(UnsafeRawBufferPointer(start: nil, count: 0))
207262
}
208263

@@ -216,11 +271,22 @@ extension EmptyCollection : ContiguousBytes where Element == UInt8 {
216271
// FIXME: When possible, expand conformance to `where Element : Trivial`.
217272
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
218273
extension CollectionOfOne : ContiguousBytes where Element == UInt8 {
219-
// TODO: Generalize for typed throws
220-
@inlinable
221-
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
274+
// TODO: This should be limited to DATA_LEGACY_ABI once clients are rebuilt
275+
@available(macOS, obsoleted: 1.0)
276+
@available(iOS, obsoleted: 1.0)
277+
@available(watchOS, obsoleted: 1.0)
278+
@available(tvOS, obsoleted: 1.0)
279+
@available(visionOS, obsoleted: 1.0)
280+
@usableFromInline
281+
@abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R)
282+
func __legacy_withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
283+
return try withUnsafeBytes(body)
284+
}
285+
286+
@_alwaysEmitIntoClient
287+
public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R {
222288
let element = self.first!
223-
return try Swift.withUnsafeBytes(of: element) { (buffer) in
289+
return try Swift.withUnsafeBytes(of: element) { (buffer) throws(E) in
224290
return try body(buffer)
225291
}
226292
}
@@ -240,15 +306,39 @@ extension CollectionOfOne : ContiguousBytes where Element == UInt8 {
240306

241307
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
242308
extension Slice : ContiguousBytes where Base : ContiguousBytes {
243-
// TODO: Generalize for typed throws
244-
@inlinable
245-
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
309+
// TODO: This should be limited to DATA_LEGACY_ABI once clients are rebuilt
310+
@available(macOS, obsoleted: 1.0)
311+
@available(iOS, obsoleted: 1.0)
312+
@available(watchOS, obsoleted: 1.0)
313+
@available(tvOS, obsoleted: 1.0)
314+
@available(visionOS, obsoleted: 1.0)
315+
@usableFromInline
316+
@abi(func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R)
317+
func __legacy_withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
318+
return try withUnsafeBytes(body)
319+
}
320+
321+
@_alwaysEmitIntoClient
322+
public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R {
246323
let offset = base.distance(from: base.startIndex, to: self.startIndex)
247-
return try base.withUnsafeBytes { ptr in
324+
#if !hasFeature(Embedded)
325+
do {
326+
return try base.withUnsafeBytes { (ptr) in
327+
let slicePtr = ptr.baseAddress?.advanced(by: offset)
328+
let sliceBuffer = UnsafeRawBufferPointer(start: slicePtr, count: self.count)
329+
return try body(sliceBuffer)
330+
}
331+
} catch let error {
332+
// Note: withUnsafeBytes is rethrowing, so we have an "any Error" here that needs casting.
333+
throw error as! E
334+
}
335+
#else
336+
return try base.withUnsafeBytes { (ptr) throws(E) in
248337
let slicePtr = ptr.baseAddress?.advanced(by: offset)
249338
let sliceBuffer = UnsafeRawBufferPointer(start: slicePtr, count: self.count)
250339
return try body(sliceBuffer)
251340
}
341+
#endif
252342
}
253343
}
254344

Tests/FoundationEssentialsTests/DataLegacyABITests.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,41 @@ extension Foundation.__DataStorage {
7474
final func __testable_withUnsafeMutableBytes<R>(in: Range<Int>, apply: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R
7575
}
7676

77+
extension UnsafeRawBufferPointer {
78+
@_silgen_name("$sSW10FoundationE15withUnsafeBytesyxxSWKXEKlF")
79+
func __testable_withUnsafeBytes<R>(body: (UnsafeRawBufferPointer) throws -> R) throws -> R
80+
}
81+
82+
extension UnsafeMutableRawBufferPointer {
83+
@_silgen_name("$sSw10FoundationE15withUnsafeBytesyxxSWKXEKlF")
84+
func __testable_withUnsafeBytes<R>(body: (UnsafeRawBufferPointer) throws -> R) throws -> R
85+
}
86+
87+
extension UnsafeBufferPointer where Element == UInt8 {
88+
@_silgen_name("$sSR10Foundations5UInt8VRszlE15withUnsafeBytesyqd__qd__SWKXEKlF")
89+
func __testable_withUnsafeBytes<R>(body: (UnsafeRawBufferPointer) throws -> R) throws -> R
90+
}
91+
92+
extension UnsafeMutableBufferPointer where Element == UInt8 {
93+
@_silgen_name("$sSr10Foundations5UInt8VRszlE15withUnsafeBytesyqd__qd__SWKXEKlF")
94+
func __testable_withUnsafeBytes<R>(body: (UnsafeRawBufferPointer) throws -> R) throws -> R
95+
}
96+
97+
extension EmptyCollection where Element == UInt8 {
98+
@_silgen_name("$ss15EmptyCollectionV10Foundations5UInt8VRszlE15withUnsafeBytesyqd__qd__SWKXEKlF")
99+
func __testable_withUnsafeBytes<R>(body: (UnsafeRawBufferPointer) throws -> R) throws -> R
100+
}
101+
102+
extension CollectionOfOne where Element == UInt8 {
103+
@_silgen_name("$ss15CollectionOfOneV10Foundations5UInt8VRszlE15withUnsafeBytesyqd__qd__SWKXEKlF")
104+
func __testable_withUnsafeBytes<R>(body: (UnsafeRawBufferPointer) throws -> R) throws -> R
105+
}
106+
107+
extension Slice where Base: ContiguousBytes {
108+
@_silgen_name("$ss5SliceV10FoundationAC15ContiguousBytesRzrlE010withUnsafeD0yqd__qd__SWKXEKlF")
109+
func __testable_withUnsafeBytes<R>(body: (UnsafeRawBufferPointer) throws -> R) throws -> R
110+
}
111+
77112
@Suite("Foundation Legacy ABI")
78113
private final class FoundationLegacyABITests {
79114

@@ -178,6 +213,42 @@ private final class FoundationLegacyABITests {
178213
#expect(slice[count7] == 31)
179214
#expect(countA.0 == 40-count7)
180215
}
216+
217+
@Test func validateContiguousBytesLegacyABI() throws {
218+
let ptr = UnsafeMutablePointer<UInt8>(bitPattern: 0xABC)
219+
try UnsafeRawBufferPointer(start: ptr, count: 1).__testable_withUnsafeBytes { buffer in
220+
#expect(buffer.baseAddress == UnsafeRawPointer(ptr))
221+
#expect(buffer.count == 1)
222+
}
223+
try UnsafeMutableRawBufferPointer(start: ptr, count: 1).__testable_withUnsafeBytes { buffer in
224+
#expect(buffer.baseAddress == UnsafeRawPointer(ptr))
225+
#expect(buffer.count == 1)
226+
}
227+
try UnsafeBufferPointer<UInt8>(start: ptr, count: 1).__testable_withUnsafeBytes { buffer in
228+
#expect(buffer.baseAddress == UnsafeRawPointer(ptr))
229+
#expect(buffer.count == 1)
230+
}
231+
try UnsafeMutableBufferPointer<UInt8>(start: ptr, count: 1).__testable_withUnsafeBytes { buffer in
232+
#expect(buffer.baseAddress == UnsafeRawPointer(ptr))
233+
#expect(buffer.count == 1)
234+
}
235+
236+
try EmptyCollection().__testable_withUnsafeBytes { buffer in
237+
#expect(buffer.baseAddress == nil)
238+
#expect(buffer.count == 0)
239+
}
240+
241+
try CollectionOfOne(UInt8(2)).__testable_withUnsafeBytes { buffer in
242+
#expect(buffer.baseAddress != nil)
243+
#expect(buffer.count == 1)
244+
}
245+
246+
try Slice<Data>().__testable_withUnsafeBytes { buffer in
247+
// Data never provide's a nil base address, so Slice<Data> won't either
248+
#expect(buffer.baseAddress != nil)
249+
#expect(buffer.count == 0)
250+
}
251+
}
181252
}
182253

183254
extension LargeDataTests {

0 commit comments

Comments
 (0)