From 74c32fc0b76b7fd1f92ee55c3b94487fec1473b7 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 4 Dec 2025 14:19:52 -0800 Subject: [PATCH 01/10] =?UTF-8?q?Generalize=20Data=E2=80=99s=20`withUnsafe?= =?UTF-8?q?Bytes()`=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/FoundationEssentials/Data/Data.swift | 13 ++++++++++--- .../Data/Representations/Data+Inline.swift | 16 +++++++++++----- .../Data/Representations/Data+InlineSlice.swift | 13 ++++++++++--- .../Data/Representations/Data+LargeSlice.swift | 13 ++++++++++--- .../Representations/Data+Representation.swift | 13 ++++++++++--- .../Data/Representations/DataStorage.swift | 14 ++++++++++---- 6 files changed, 61 insertions(+), 21 deletions(-) diff --git a/Sources/FoundationEssentials/Data/Data.swift b/Sources/FoundationEssentials/Data/Data.swift index ddef89081..02d06a695 100644 --- a/Sources/FoundationEssentials/Data/Data.swift +++ b/Sources/FoundationEssentials/Data/Data.swift @@ -345,9 +345,16 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable } } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { - return try _representation.withUnsafeBytes(body) + @_alwaysEmitIntoClient + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws(E) -> ResultType) throws(E) -> ResultType { + try _representation.withUnsafeBytes(body) + } + + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeBytes(body) } @available(macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *) diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift index 22d4bf0a9..ad4cab7fb 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift @@ -126,14 +126,20 @@ extension Data { return count } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - let count = Int(length) - return try Swift.withUnsafeBytes(of: bytes) { (rawBuffer) throws -> Result in - return try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count)) + @_alwaysEmitIntoClient + func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result { + try Swift.withUnsafeBytes(of: bytes) { [count = Int(length)] (rawBuffer) throws(E) -> Result in + try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count)) } } + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeBytes(body) + } + @inlinable // This is @inlinable as a generic, trivially forwarding function. mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { let count = Int(length) diff --git a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift index 0e2abd7e6..c9297fc2f 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift @@ -157,11 +157,18 @@ extension Data { } } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try storage.withUnsafeBytes(in: range, apply: apply) + @_alwaysEmitIntoClient + func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result { + try storage.withUnsafeBytes(in: range, apply: apply) } + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeBytes(body) + } + @inlinable // This is @inlinable as a generic, trivially forwarding function. mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { ensureUniqueReference() diff --git a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift index 2974d2921..b8b10b016 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift @@ -145,11 +145,18 @@ extension Data { return slice.range } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try storage.withUnsafeBytes(in: range, apply: apply) + @_alwaysEmitIntoClient + func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result { + try storage.withUnsafeBytes(in: range, apply: apply) } + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeBytes(body) + } + @inlinable // This is @inlinable as a generic, trivially forwarding function. mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { ensureUniqueReference() diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift index a6de6d806..99b0b23af 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift @@ -213,8 +213,8 @@ extension Data { } } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { + @_alwaysEmitIntoClient + func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result { switch self { case .empty: let empty = InlineData() @@ -227,7 +227,14 @@ extension Data { return try slice.withUnsafeBytes(apply) } } - + + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeBytes(body) + } + @inlinable // This is @inlinable as a generic, trivially forwarding function. mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { switch self { diff --git a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift index 146da906a..9d0552c5e 100644 --- a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift +++ b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift @@ -97,12 +97,18 @@ internal final class __DataStorage : @unchecked Sendable { return UnsafeRawPointer(_bytes)?.advanced(by: -_offset) } - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. - @discardableResult - func withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) + @_alwaysEmitIntoClient + func withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result { + try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) } + @abi(func withUnsafeBytes(in: Range, apply: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + func _legacy_withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { + try withUnsafeBytes(in: range, apply: apply) + } + @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. @discardableResult func withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { From 2e9f9cc809e452389862081afd0249d7413ff9d1 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 4 Dec 2025 14:20:23 -0800 Subject: [PATCH 02/10] Tweak a test (silence a warning) --- Tests/FoundationEssentialsTests/DataTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/FoundationEssentialsTests/DataTests.swift b/Tests/FoundationEssentialsTests/DataTests.swift index 0bab5ee15..86251e844 100644 --- a/Tests/FoundationEssentialsTests/DataTests.swift +++ b/Tests/FoundationEssentialsTests/DataTests.swift @@ -1767,7 +1767,7 @@ private final class DataTests { let byteCount = span.byteCount #expect(byteCount == count) let v = UInt8.random(in: 10..<100) - var sub = span.extracting(i.. Date: Thu, 4 Dec 2025 14:35:41 -0800 Subject: [PATCH 03/10] =?UTF-8?q?Generalize=20Data=E2=80=99s=20`withUnsafe?= =?UTF-8?q?MutableBytes`=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/FoundationEssentials/Data/Data.swift | 13 ++++++++++--- .../Data/Representations/Data+Inline.swift | 18 ++++++++++++------ .../Representations/Data+InlineSlice.swift | 13 ++++++++++--- .../Data/Representations/Data+LargeSlice.swift | 13 ++++++++++--- .../Representations/Data+Representation.swift | 13 ++++++++++--- .../Data/Representations/DataStorage.swift | 14 ++++++++++---- 6 files changed, 62 insertions(+), 22 deletions(-) diff --git a/Sources/FoundationEssentials/Data/Data.swift b/Sources/FoundationEssentials/Data/Data.swift index 02d06a695..fc0c3efec 100644 --- a/Sources/FoundationEssentials/Data/Data.swift +++ b/Sources/FoundationEssentials/Data/Data.swift @@ -480,9 +480,16 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable } } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { - return try _representation.withUnsafeMutableBytes(body) + @_alwaysEmitIntoClient + public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws(E) -> ResultType) throws(E) -> ResultType { + try _representation.withUnsafeMutableBytes(body) + } + + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeMutableBytes(body) } // MARK: - diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift index ad4cab7fb..98c958b75 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift @@ -140,14 +140,20 @@ extension Data { try withUnsafeBytes(body) } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - let count = Int(length) - return try Swift.withUnsafeMutableBytes(of: &bytes) { (rawBuffer) throws -> Result in - return try apply(UnsafeMutableRawBufferPointer(start: rawBuffer.baseAddress, count: count)) + @_alwaysEmitIntoClient + mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { + try Swift.withUnsafeMutableBytes(of: &bytes) { [count = Int(length)] (rawBuffer) throws(E) -> Result in + try apply(UnsafeMutableRawBufferPointer(start: rawBuffer.baseAddress, count: count)) } } - + + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeMutableBytes(body) + } + @inlinable // This is @inlinable as trivially computable. mutating func append(byte: UInt8) { let count = self.count diff --git a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift index c9297fc2f..b6023c23c 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift @@ -169,12 +169,19 @@ extension Data { try withUnsafeBytes(body) } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { + @_alwaysEmitIntoClient + mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { ensureUniqueReference() return try storage.withUnsafeMutableBytes(in: range, apply: apply) } - + + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeMutableBytes(body) + } + @inlinable // This is @inlinable as reasonably small. mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { assert(endIndex + buffer.count < HalfInt.max) diff --git a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift index b8b10b016..dfd93de04 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift @@ -157,12 +157,19 @@ extension Data { try withUnsafeBytes(body) } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { + @_alwaysEmitIntoClient + mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { ensureUniqueReference() return try storage.withUnsafeMutableBytes(in: range, apply: apply) } - + + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeMutableBytes(body) + } + @inlinable // This is @inlinable as reasonably small. mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { ensureUniqueReference() diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift index 99b0b23af..74ddf42de 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift @@ -235,8 +235,8 @@ extension Data { try withUnsafeBytes(body) } - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { + @_alwaysEmitIntoClient + mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { switch self { case .empty: var empty = InlineData() @@ -254,7 +254,14 @@ extension Data { return try slice.withUnsafeMutableBytes(apply) } } - + + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeMutableBytes(body) + } + @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Index, _ stop: inout Bool) -> Void) { switch self { diff --git a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift index 9d0552c5e..1961aa377 100644 --- a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift +++ b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift @@ -109,12 +109,18 @@ internal final class __DataStorage : @unchecked Sendable { try withUnsafeBytes(in: range, apply: apply) } - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. - @discardableResult - func withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - return try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) + @_alwaysEmitIntoClient + func withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { + try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) } + @abi(func withUnsafeMutableBytes(in: Range, apply: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @_spi(FoundationLegacyABI) + @usableFromInline + internal func _legacy_withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + try withUnsafeMutableBytes(in: range, apply: apply) + } + @inlinable // This is @inlinable as trivially computable. var mutableBytes: UnsafeMutableRawPointer? { return _bytes?.advanced(by: _offset &* -1) // _offset is guaranteed to be non-negative, so it can never overflow when negating From 70cef22eeb6d15a377214a44f416d9bbbe33611f Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 4 Dec 2025 16:32:37 -0800 Subject: [PATCH 04/10] Test generalized functions --- .../FoundationEssentialsTests/DataTests.swift | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/Tests/FoundationEssentialsTests/DataTests.swift b/Tests/FoundationEssentialsTests/DataTests.swift index 86251e844..d40756c71 100644 --- a/Tests/FoundationEssentialsTests/DataTests.swift +++ b/Tests/FoundationEssentialsTests/DataTests.swift @@ -1484,6 +1484,79 @@ private final class DataTests { } } + private struct Value: ~Copyable { + var stored: Int + init(_ value: Int) { stored = value } + } + + private enum LocalError: Error, Equatable { case error } + + @Test func validateGeneralizedParameters_withUnsafeBytes() { + var data: Data + + do throws(LocalError) { + data = Data(repeating: 2, count: 12) + let value = data.withUnsafeBytes { + let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } + return Value(sum) + } + #expect(value.stored == 24) + try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) } + Issue.record("Should be unreachable") + } catch { + #expect(error == .error) + } + + do throws(LocalError) { + data = Data(repeating: 1, count: 128) + let value = data.withUnsafeBytes { + let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } + return Value(sum) + } + #expect(value.stored == 128) + try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) } + Issue.record("Should be unreachable") + } catch { + #expect(error == .error) + } + } + + @Test func validateGeneralizedParameters_withUnsafeMutableBytes() { + var data: Data + + do throws(LocalError) { + data = Data(count: 12) + let value = data.withUnsafeMutableBytes { + $0.withMemoryRebound(to: UInt8.self) { + for i in $0.indices { $0[i] = 2 } + } + let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } + return Value(sum) + } + #expect(value.stored == 24) + try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) } + Issue.record("Should be unreachable") + } catch { + #expect(error == .error) + } + + do throws(LocalError) { + data = Data(count: 128) + let value = data.withUnsafeMutableBytes { + $0.withMemoryRebound(to: UInt8.self) { + for i in $0.indices { $0[i] = 1 } + } + let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } + return Value(sum) + } + #expect(value.stored == 128) + try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) } + Issue.record("Should be unreachable") + } catch { + #expect(error == .error) + } + } + @Test func sliceHash() { let base1 = Data([0, 0xFF, 0xFF, 0]) let base2 = Data([0, 0xFF, 0xFF, 0]) From fce135da738b8a5fa0178f2a3b91c8acec1062d9 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 4 Dec 2025 15:39:58 -0800 Subject: [PATCH 05/10] Only compile old ABI for FOUNDATION_FRAMEWORK --- Sources/FoundationEssentials/Data/Data.swift | 4 ++++ .../Data/Representations/Data+Inline.swift | 6 +++++- .../Data/Representations/Data+InlineSlice.swift | 6 +++++- .../Data/Representations/Data+LargeSlice.swift | 6 +++++- .../Data/Representations/Data+Representation.swift | 4 ++++ .../Data/Representations/DataStorage.swift | 10 +++++++--- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Sources/FoundationEssentials/Data/Data.swift b/Sources/FoundationEssentials/Data/Data.swift index fc0c3efec..6fc0deed1 100644 --- a/Sources/FoundationEssentials/Data/Data.swift +++ b/Sources/FoundationEssentials/Data/Data.swift @@ -350,12 +350,14 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable try _representation.withUnsafeBytes(body) } +#if FOUNDATION_FRAMEWORK @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeBytes(body) } +#endif // FOUNDATION_FRAMEWORK @available(macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *) @_alwaysEmitIntoClient @@ -485,12 +487,14 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable try _representation.withUnsafeMutableBytes(body) } +#if FOUNDATION_FRAMEWORK @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeMutableBytes(body) } +#endif // FOUNDATION_FRAMEWORK // MARK: - diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift index 98c958b75..f552e1e12 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift @@ -132,13 +132,15 @@ extension Data { try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count)) } } - + +#if FOUNDATION_FRAMEWORK @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeBytes(body) } +#endif // FOUNDATION_FRAMEWORK @_alwaysEmitIntoClient mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { @@ -147,12 +149,14 @@ extension Data { } } +#if FOUNDATION_FRAMEWORK @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeMutableBytes(body) } +#endif // FOUNDATION_FRAMEWORK @inlinable // This is @inlinable as trivially computable. mutating func append(byte: UInt8) { diff --git a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift index b6023c23c..cea15bb50 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift @@ -161,13 +161,15 @@ extension Data { func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result { try storage.withUnsafeBytes(in: range, apply: apply) } - + +#if FOUNDATION_FRAMEWORK @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeBytes(body) } +#endif // FOUNDATION_FRAMEWORK @_alwaysEmitIntoClient mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { @@ -175,12 +177,14 @@ extension Data { return try storage.withUnsafeMutableBytes(in: range, apply: apply) } +#if FOUNDATION_FRAMEWORK @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeMutableBytes(body) } +#endif // FOUNDATION_FRAMEWORK @inlinable // This is @inlinable as reasonably small. mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { diff --git a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift index dfd93de04..c24047727 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift @@ -149,13 +149,15 @@ extension Data { func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result { try storage.withUnsafeBytes(in: range, apply: apply) } - + +#if FOUNDATION_FRAMEWORK @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeBytes(body) } +#endif // FOUNDATION_FRAMEWORK @_alwaysEmitIntoClient mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { @@ -163,12 +165,14 @@ extension Data { return try storage.withUnsafeMutableBytes(in: range, apply: apply) } +#if FOUNDATION_FRAMEWORK @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeMutableBytes(body) } +#endif // FOUNDATION_FRAMEWORK @inlinable // This is @inlinable as reasonably small. mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift index 74ddf42de..6cd5455f9 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift @@ -228,12 +228,14 @@ extension Data { } } +#if FOUNDATION_FRAMEWORK @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeBytes(body) } +#endif // FOUNDATION_FRAMEWORK @_alwaysEmitIntoClient mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { @@ -255,12 +257,14 @@ extension Data { } } +#if FOUNDATION_FRAMEWORK @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeMutableBytes(body) } +#endif // FOUNDATION_FRAMEWORK @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Index, _ stop: inout Bool) -> Void) { diff --git a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift index 1961aa377..c97106463 100644 --- a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift +++ b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift @@ -101,25 +101,29 @@ internal final class __DataStorage : @unchecked Sendable { func withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws(E) -> Result) throws(E) -> Result { try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) } - + +#if FOUNDATION_FRAMEWORK @abi(func withUnsafeBytes(in: Range, apply: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline func _legacy_withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { try withUnsafeBytes(in: range, apply: apply) } +#endif // FOUNDATION_FRAMEWORK @_alwaysEmitIntoClient func withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws(E) -> Result) throws(E) -> Result { try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) } - - @abi(func withUnsafeMutableBytes(in: Range, apply: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + +#if FOUNDATION_FRAMEWORK + @abi(func withUnsafeMutableBytes(in: Range, apply: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) @_spi(FoundationLegacyABI) @usableFromInline internal func _legacy_withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { try withUnsafeMutableBytes(in: range, apply: apply) } +#endif // FOUNDATION_FRAMEWORK @inlinable // This is @inlinable as trivially computable. var mutableBytes: UnsafeMutableRawPointer? { From 955a7e9299f480b7f52eaeab89b526e3076ed301 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 4 Dec 2025 16:33:02 -0800 Subject: [PATCH 06/10] Verify the old ABI --- .../DataLegacyABITests.swift | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Tests/FoundationEssentialsTests/DataLegacyABITests.swift diff --git a/Tests/FoundationEssentialsTests/DataLegacyABITests.swift b/Tests/FoundationEssentialsTests/DataLegacyABITests.swift new file mode 100644 index 000000000..d55ae7641 --- /dev/null +++ b/Tests/FoundationEssentialsTests/DataLegacyABITests.swift @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if FOUNDATION_FRAMEWORK +@_spi(FoundationLegacyABI) @testable import Foundation +import Testing + +@Test func validateLegacyABI() { + var data = Data() + + data._legacy_withUnsafeBytes { _ in } + data._legacy_withUnsafeMutableBytes { _ in } +} + +#endif // FOUNDATION_FRAMEWORK From f8b021308c6b7b98581976eea427685db8d3168a Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 5 Dec 2025 09:07:44 -0800 Subject: [PATCH 07/10] Spell `rethrows` as `throws` in ABI-compatibility functions --- Sources/FoundationEssentials/Data/Data.swift | 8 ++++---- .../Data/Representations/Data+Inline.swift | 8 ++++---- .../Data/Representations/Data+InlineSlice.swift | 8 ++++---- .../Data/Representations/Data+LargeSlice.swift | 8 ++++---- .../Data/Representations/Data+Representation.swift | 8 ++++---- .../Data/Representations/DataStorage.swift | 8 ++++---- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Sources/FoundationEssentials/Data/Data.swift b/Sources/FoundationEssentials/Data/Data.swift index 6fc0deed1..2f4260c50 100644 --- a/Sources/FoundationEssentials/Data/Data.swift +++ b/Sources/FoundationEssentials/Data/Data.swift @@ -351,10 +351,10 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable } #if FOUNDATION_FRAMEWORK - @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeBytes(body) } #endif // FOUNDATION_FRAMEWORK @@ -488,10 +488,10 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable } #if FOUNDATION_FRAMEWORK - @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeMutableBytes(body) } #endif // FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift index f552e1e12..6cec361d2 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift @@ -134,10 +134,10 @@ extension Data { } #if FOUNDATION_FRAMEWORK - @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeBytes(body) } #endif // FOUNDATION_FRAMEWORK @@ -150,10 +150,10 @@ extension Data { } #if FOUNDATION_FRAMEWORK - @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeMutableBytes(body) } #endif // FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift index cea15bb50..6a9a6fd7d 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift @@ -163,10 +163,10 @@ extension Data { } #if FOUNDATION_FRAMEWORK - @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeBytes(body) } #endif // FOUNDATION_FRAMEWORK @@ -178,10 +178,10 @@ extension Data { } #if FOUNDATION_FRAMEWORK - @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeMutableBytes(body) } #endif // FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift index c24047727..a3752a8e0 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift @@ -151,10 +151,10 @@ extension Data { } #if FOUNDATION_FRAMEWORK - @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeBytes(body) } #endif // FOUNDATION_FRAMEWORK @@ -166,10 +166,10 @@ extension Data { } #if FOUNDATION_FRAMEWORK - @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeMutableBytes(body) } #endif // FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift index 6cd5455f9..536df6186 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift @@ -229,10 +229,10 @@ extension Data { } #if FOUNDATION_FRAMEWORK - @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @abi(func withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal func _legacy_withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeBytes(body) } #endif // FOUNDATION_FRAMEWORK @@ -258,10 +258,10 @@ extension Data { } #if FOUNDATION_FRAMEWORK - @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @abi(mutating func withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal mutating func _legacy_withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeMutableBytes(body) } #endif // FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift index c97106463..bbe5c46fc 100644 --- a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift +++ b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift @@ -103,10 +103,10 @@ internal final class __DataStorage : @unchecked Sendable { } #if FOUNDATION_FRAMEWORK - @abi(func withUnsafeBytes(in: Range, apply: (UnsafeRawBufferPointer) throws -> R) rethrows -> R) + @abi(func withUnsafeBytes(in: Range, apply: (UnsafeRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - func _legacy_withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { + func _legacy_withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws -> Result) throws -> Result { try withUnsafeBytes(in: range, apply: apply) } #endif // FOUNDATION_FRAMEWORK @@ -117,10 +117,10 @@ internal final class __DataStorage : @unchecked Sendable { } #if FOUNDATION_FRAMEWORK - @abi(func withUnsafeMutableBytes(in: Range, apply: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R) + @abi(func withUnsafeMutableBytes(in: Range, apply: (UnsafeMutableRawBufferPointer) throws -> R) throws -> R) @_spi(FoundationLegacyABI) @usableFromInline - internal func _legacy_withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { + internal func _legacy_withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> ResultType) throws -> ResultType { try withUnsafeMutableBytes(in: range, apply: apply) } #endif // FOUNDATION_FRAMEWORK From ddca718476ffb30167c8beb559d8cc733c44b605 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 5 Dec 2025 13:22:31 -0800 Subject: [PATCH 08/10] Improve test by using a more specific #expect --- .../FoundationEssentialsTests/DataTests.swift | 76 ++++++++----------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/Tests/FoundationEssentialsTests/DataTests.swift b/Tests/FoundationEssentialsTests/DataTests.swift index d40756c71..0a7719612 100644 --- a/Tests/FoundationEssentialsTests/DataTests.swift +++ b/Tests/FoundationEssentialsTests/DataTests.swift @@ -1494,66 +1494,54 @@ private final class DataTests { @Test func validateGeneralizedParameters_withUnsafeBytes() { var data: Data - do throws(LocalError) { - data = Data(repeating: 2, count: 12) - let value = data.withUnsafeBytes { - let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } - return Value(sum) - } - #expect(value.stored == 24) + data = Data(repeating: 2, count: 12) + let value1 = data.withUnsafeBytes { + let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } + return Value(sum) + } + #expect(value1.stored == 24) + #expect(throws: LocalError.error) { try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) } - Issue.record("Should be unreachable") - } catch { - #expect(error == .error) } - do throws(LocalError) { - data = Data(repeating: 1, count: 128) - let value = data.withUnsafeBytes { - let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } - return Value(sum) - } - #expect(value.stored == 128) + data = Data(repeating: 1, count: 128) + let value2 = data.withUnsafeBytes { + let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } + return Value(sum) + } + #expect(value2.stored == 128) + #expect(throws: LocalError.error) { try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) } - Issue.record("Should be unreachable") - } catch { - #expect(error == .error) } } @Test func validateGeneralizedParameters_withUnsafeMutableBytes() { var data: Data - do throws(LocalError) { - data = Data(count: 12) - let value = data.withUnsafeMutableBytes { - $0.withMemoryRebound(to: UInt8.self) { - for i in $0.indices { $0[i] = 2 } - } - let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } - return Value(sum) + data = Data(count: 12) + let value1 = data.withUnsafeMutableBytes { + $0.withMemoryRebound(to: UInt8.self) { + for i in $0.indices { $0[i] = 2 } } - #expect(value.stored == 24) + let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } + return Value(sum) + } + #expect(value1.stored == 24) + #expect(throws: LocalError.error) { try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) } - Issue.record("Should be unreachable") - } catch { - #expect(error == .error) } - do throws(LocalError) { - data = Data(count: 128) - let value = data.withUnsafeMutableBytes { - $0.withMemoryRebound(to: UInt8.self) { - for i in $0.indices { $0[i] = 1 } - } - let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } - return Value(sum) + data = Data(count: 128) + let value2 = data.withUnsafeMutableBytes { + $0.withMemoryRebound(to: UInt8.self) { + for i in $0.indices { $0[i] = 1 } } - #expect(value.stored == 128) + let sum = $0.withMemoryRebound(to: UInt8.self) { Int($0.reduce(0,+)) } + return Value(sum) + } + #expect(value2.stored == 128) + #expect(throws: LocalError.error) { try data.withUnsafeBytes { _ throws(LocalError) in throw(LocalError.error) } - Issue.record("Should be unreachable") - } catch { - #expect(error == .error) } } From 248dc87adfbf33b0ea841fc3a6b9392fb962f98e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 5 Dec 2025 13:58:13 -0800 Subject: [PATCH 09/10] Add a test suite specifically for legacy ABI tests --- .../DataLegacyABITests.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Tests/FoundationEssentialsTests/DataLegacyABITests.swift b/Tests/FoundationEssentialsTests/DataLegacyABITests.swift index d55ae7641..79bd08a40 100644 --- a/Tests/FoundationEssentialsTests/DataLegacyABITests.swift +++ b/Tests/FoundationEssentialsTests/DataLegacyABITests.swift @@ -14,11 +14,15 @@ @_spi(FoundationLegacyABI) @testable import Foundation import Testing -@Test func validateLegacyABI() { - var data = Data() +@Suite("Foundation Legacy ABI") +private final class FoundationLegacyABITests { - data._legacy_withUnsafeBytes { _ in } - data._legacy_withUnsafeMutableBytes { _ in } + @Test func validateDataLegacyABI() { + var data = Data() + + data._legacy_withUnsafeBytes { _ in } + data._legacy_withUnsafeMutableBytes { _ in } + } } #endif // FOUNDATION_FRAMEWORK From 6ab735c226889825f4a3c4e0486dfa686339f657 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 11 Dec 2025 12:00:42 -0800 Subject: [PATCH 10/10] Generalize `withContiguousStorageIfAvailable` as well --- Sources/FoundationEssentials/Data/Data.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/FoundationEssentials/Data/Data.swift b/Sources/FoundationEssentials/Data/Data.swift index 2f4260c50..8c091da65 100644 --- a/Sources/FoundationEssentials/Data/Data.swift +++ b/Sources/FoundationEssentials/Data/Data.swift @@ -476,9 +476,11 @@ public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceable } @_alwaysEmitIntoClient - public func withContiguousStorageIfAvailable(_ body: (_ buffer: UnsafeBufferPointer) throws -> ResultType) rethrows -> ResultType? { - return try _representation.withUnsafeBytes { - return try $0.withMemoryRebound(to: UInt8.self, body) + public func withContiguousStorageIfAvailable( + _ body: (_ buffer: UnsafeBufferPointer) throws(E) -> ResultType + ) throws(E) -> ResultType? { + try _representation.withUnsafeBytes { bytes throws(E) in + try bytes.withMemoryRebound(to: UInt8.self, body) } }