From 30a3eaec4ffb9314ca584ebeed1aa3d8de9a5674 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 26 Feb 2025 16:50:53 -0800 Subject: [PATCH 01/23] [stdlib] add `MutableSpan` and `MutableRawSpan` --- stdlib/public/core/CMakeLists.txt | 2 + stdlib/public/core/GroupInfo.json | 2 + stdlib/public/core/LifetimeManager.swift | 19 + stdlib/public/core/Span/MutableRawSpan.swift | 419 +++++++++++++ stdlib/public/core/Span/MutableSpan.swift | 619 +++++++++++++++++++ 5 files changed, 1061 insertions(+) create mode 100644 stdlib/public/core/Span/MutableRawSpan.swift create mode 100644 stdlib/public/core/Span/MutableSpan.swift diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index ecc5bfc6ee639..37207f189ee49 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -157,6 +157,8 @@ split_embedded_sources( EMBEDDED Slice.swift EMBEDDED SmallString.swift EMBEDDED Sort.swift + EMBEDDED Span/MutableRawSpan.swift + EMBEDDED Span/MutableSpan.swift EMBEDDED Span/RawSpan.swift EMBEDDED Span/Span.swift EMBEDDED StaticString.swift diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index 248898b021a15..655d20c95b06f 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -200,6 +200,8 @@ "UnsafeRawBufferPointer.swift" ], "Span": [ + "MutableRawSpan.swift", + "MutableSpan.swift", "RawSpan.swift", "Span.swift" ], diff --git a/stdlib/public/core/LifetimeManager.swift b/stdlib/public/core/LifetimeManager.swift index 2a0cb3154a077..13c6b0e857f90 100644 --- a/stdlib/public/core/LifetimeManager.swift +++ b/stdlib/public/core/LifetimeManager.swift @@ -297,3 +297,22 @@ internal func _overrideLifetime< // should be expressed by a builtin that is hidden within the function body. dependent } + +/// Unsafely discard any lifetime dependency on the `dependent` argument. +/// Return a value identical to `dependent` with a lifetime dependency +/// on the caller's exclusive borrow scope of the `source` argument. +@unsafe +@_unsafeNonescapableResult +@_alwaysEmitIntoClient +@_transparent +@lifetime(borrow source) +internal func _overrideLifetime< + T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable +>( + _ dependent: consuming T, + mutating source: inout U +) -> T { + // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence + // should be expressed by a builtin that is hidden within the function body. + dependent +} diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift new file mode 100644 index 0000000000000..e6e800585be13 --- /dev/null +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -0,0 +1,419 @@ +//===--- MutableRawSpan.swift ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 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 +// +//===----------------------------------------------------------------------===// + +// A MutableRawSpan represents a span of memory which +// contains initialized `Element` instances. +@frozen +@available(SwiftStdlib 6.2, *) +public struct MutableRawSpan: ~Copyable & ~Escapable { + @usableFromInline + internal let _pointer: UnsafeMutableRawPointer? + + @usableFromInline + internal let _count: Int + + @_alwaysEmitIntoClient + internal func _start() -> UnsafeMutableRawPointer { + _pointer.unsafelyUnwrapped + } + + @usableFromInline @inline(__always) + @lifetime(borrow pointer) + init( + _unchecked pointer: UnsafeMutableRawPointer?, + count: Int + ) { + _pointer = pointer + _count = count + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan: @unchecked Sendable {} + +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan { + + @_alwaysEmitIntoClient + @lifetime(borrow bytes) + public init( + _unsafeBytes bytes: UnsafeMutableRawBufferPointer + ) { + let baseAddress = bytes.baseAddress + let span = MutableRawSpan(_unchecked: baseAddress, count: bytes.count) + self = _overrideLifetime(span, borrowing: bytes) + } + + @_alwaysEmitIntoClient + @lifetime(borrow bytes) + public init( + _unsafeBytes bytes: borrowing Slice + ) { + let rebased = UnsafeMutableRawBufferPointer(rebasing: bytes) + let span = MutableRawSpan(_unsafeBytes: rebased) + self = _overrideLifetime(span, borrowing: bytes) + } + + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + public init( + _unsafeStart pointer: UnsafeMutableRawPointer, + byteCount: Int + ) { + precondition(byteCount >= 0, "Count must not be negative") + self.init(_unchecked: pointer, count: byteCount) + } + + @_alwaysEmitIntoClient + @lifetime(borrow elements) + public init( + _unsafeElements elements: UnsafeMutableBufferPointer + ) { + let bytes = UnsafeMutableRawBufferPointer(elements) + let span = MutableRawSpan(_unsafeBytes: bytes) + self = _overrideLifetime(span, borrowing: elements) + } + + @_alwaysEmitIntoClient + @lifetime(borrow elements) + public init( + _unsafeElements elements: borrowing Slice> + ) { + let rebased = UnsafeMutableBufferPointer(rebasing: elements) + let span = MutableRawSpan(_unsafeElements: rebased) + self = _overrideLifetime(span, borrowing: elements) + } + + @_alwaysEmitIntoClient + @lifetime(elements) + public init( + _elements elements: consuming MutableSpan + ) { + let bytes = UnsafeMutableRawBufferPointer( + start: elements._pointer, + count: elements.count &* MemoryLayout.stride + ) + let span = MutableRawSpan(_unsafeBytes: bytes) + self = _overrideLifetime(span, copying: elements) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan { + @_alwaysEmitIntoClient + public var byteCount: Int { _count } + + @_alwaysEmitIntoClient + public var isEmpty: Bool { byteCount == 0 } + + @_alwaysEmitIntoClient + public var byteOffsets: Range { + .init(uncheckedBounds: (0, byteCount)) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan { + + @_alwaysEmitIntoClient + public func withUnsafeBytes( + _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + guard let pointer = _pointer, _count > 0 else { + return try body(.init(start: nil, count: 0)) + } + return try body(.init(start: pointer, count: _count)) + } + + @_alwaysEmitIntoClient + public mutating func withUnsafeMutableBytes( + _ body: (UnsafeMutableRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + guard let pointer = _pointer, _count > 0 else { + return try body(.init(start: nil, count: 0)) + } + return try body(.init(start: pointer, count: _count)) + } +} + +@available(SwiftStdlib 6.2, *) +extension RawSpan { + + @_alwaysEmitIntoClient + @lifetime(borrow mutableSpan) + public init(_unsafeMutableRawSpan mutableSpan: borrowing MutableRawSpan) { + let start = mutableSpan._start() + let span = RawSpan(_unsafeStart: start, byteCount: mutableSpan.byteCount) + self = _overrideLifetime(span, borrowing: mutableSpan) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan { + + public var bytes: RawSpan { + @_alwaysEmitIntoClient + @lifetime(borrow self) + borrowing get { + return RawSpan(_unsafeMutableRawSpan: self) + } + } + + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + public borrowing func _unsafeView( + as type: T.Type + ) -> Span { + let bytes = UnsafeRawBufferPointer(start: _pointer, count: _count) + let span = Span(_unsafeBytes: bytes) + return _overrideLifetime(span, borrowing: self) + } + + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + public mutating func _unsafeMutableView( + as type: T.Type + ) -> MutableSpan { + let bytes = UnsafeMutableRawBufferPointer(start: _pointer, count: _count) + let span = MutableSpan(_unsafeBytes: bytes) + return _overrideLifetime(span, mutating: &self) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan { + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be properly aligned for + /// accessing `T` and initialized to `T` or another type that is layout + /// compatible with `T`. + /// + /// This is an unsafe operation. Failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance is memory-managed and unassociated + /// with the value in the memory referenced by this pointer. + @unsafe + @_alwaysEmitIntoClient + public func unsafeLoad( + fromByteOffset offset: Int = 0, as: T.Type + ) -> T { + precondition( + UInt(bitPattern: offset) <= UInt(bitPattern: _count) && + MemoryLayout.size <= (_count &- offset), + "Byte offset range out of bounds" + ) + return unsafeLoad(fromUncheckedByteOffset: offset, as: T.self) + } + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be properly aligned for + /// accessing `T` and initialized to `T` or another type that is layout + /// compatible with `T`. + /// + /// This is an unsafe operation. This function does not validate the bounds + /// of the memory access, and failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance is memory-managed and unassociated + /// with the value in the memory referenced by this pointer. + @unsafe + @_alwaysEmitIntoClient + public func unsafeLoad( + fromUncheckedByteOffset offset: Int, as: T.Type + ) -> T { + _start().load(fromByteOffset: offset, as: T.self) + } + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be initialized to `T` + /// or another type that is layout compatible with `T`. + /// + /// This is an unsafe operation. Failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance isn't associated + /// with the value in the range of memory referenced by this pointer. + @unsafe + @_alwaysEmitIntoClient + public func unsafeLoadUnaligned( + fromByteOffset offset: Int = 0, as: T.Type + ) -> T { + precondition( + UInt(bitPattern: offset) <= UInt(bitPattern: _count) && + MemoryLayout.size <= (_count &- offset), + "Byte offset range out of bounds" + ) + return unsafeLoadUnaligned(fromUncheckedByteOffset: offset, as: T.self) + } + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be initialized to `T` + /// or another type that is layout compatible with `T`. + /// + /// This is an unsafe operation. This function does not validate the bounds + /// of the memory access, and failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance isn't associated + /// with the value in the range of memory referenced by this pointer. + @unsafe + @_alwaysEmitIntoClient + public func unsafeLoadUnaligned( + fromUncheckedByteOffset offset: Int, as: T.Type + ) -> T { + _start().loadUnaligned(fromByteOffset: offset, as: T.self) + } + + @_alwaysEmitIntoClient + public func storeBytes( + of value: T, toByteOffset offset: Int = 0, as type: T.Type + ) { + precondition( + UInt(bitPattern: offset) <= UInt(bitPattern: _count) && + MemoryLayout.size <= (_count &- offset), + "Byte offset range out of bounds" + ) + storeBytes(of: value, toUncheckedByteOffset: offset, as: type) + } + + @unsafe + @_alwaysEmitIntoClient + public func storeBytes( + of value: T, toUncheckedByteOffset offset: Int, as type: T.Type + ) { + _start().storeBytes(of: value, toByteOffset: offset, as: type) + } +} + +//MARK: copyMemory +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan { + + @_alwaysEmitIntoClient + public mutating func update( + startingAt byteOffset: Int = 0, + from source: S + ) -> (unwritten: S.Iterator, byteOffset: Int) where S.Element: BitwiseCopyable { + var iterator = source.makeIterator() + let offset = update(startingAt: byteOffset, from: &iterator) + return (iterator, offset) + } + + @_alwaysEmitIntoClient + public mutating func update( + startingAt byteOffset: Int = 0, + from elements: inout some IteratorProtocol + ) -> Int { + var offset = byteOffset + while offset + MemoryLayout.stride <= _count { + guard let element = elements.next() else { break } + storeBytes(of: element, toUncheckedByteOffset: offset, as: Element.self) + offset &+= MemoryLayout.stride + } + return offset + } + + @_alwaysEmitIntoClient + public mutating func update( + startingAt byteOffset: Int = 0, + fromContentsOf source: C + ) -> Int where C.Element: BitwiseCopyable { + let newOffset = source.withContiguousStorageIfAvailable { + self.update( + startingAt: byteOffset, fromContentsOf: Span(_unsafeElements: $0) + ) + } + if let newOffset { return newOffset } + + var elements = source.makeIterator() + let lastOffset = update(startingAt: byteOffset, from: &elements) + precondition( + elements.next() == nil, + "destination span cannot contain every element from source." + ) + return lastOffset + } + + @_alwaysEmitIntoClient + public mutating func update( + startingAt byteOffset: Int = 0, + fromContentsOf source: Span + ) -> Int { +// update(startingAt: byteOffset, from: source.bytes) + source.withUnsafeBytes { + update(startingAt: byteOffset, fromContentsOf: $0) + } + } + + @_alwaysEmitIntoClient + public mutating func update( + startingAt byteOffset: Int = 0, + fromContentsOf source: borrowing MutableSpan + ) -> Int { +// update(startingAt: byteOffset, from: source.storage.bytes) + source.withUnsafeBytes { + update(startingAt: byteOffset, fromContentsOf: $0) + } + } + + @_alwaysEmitIntoClient + public mutating func update( + startingAt byteOffset: Int = 0, + from source: RawSpan + ) -> Int { + if source.byteCount == 0 { return byteOffset } + source.withUnsafeBytes { + _start().advanced(by: byteOffset) + .copyMemory(from: $0.baseAddress!, byteCount: $0.count) + } + return byteOffset &+ source.byteCount + } + + @_alwaysEmitIntoClient + public mutating func update( + startingAt byteOffset: Int = 0, + from source: borrowing MutableRawSpan + ) -> Int { + update(startingAt: byteOffset, from: source.bytes) + } +} diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift new file mode 100644 index 0000000000000..ec52a70772cb5 --- /dev/null +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -0,0 +1,619 @@ +//===--- MutableSpan.swift ------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 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 +// +//===----------------------------------------------------------------------===// + +// A MutableSpan represents a span of memory which +// contains initialized `Element` instances. +@frozen +@available(SwiftStdlib 6.2, *) +public struct MutableSpan +: ~Copyable, ~Escapable { + @usableFromInline + internal let _pointer: UnsafeMutableRawPointer? + + @usableFromInline + internal let _count: Int + + @_alwaysEmitIntoClient + internal func _start() -> UnsafeMutableRawPointer { + _pointer.unsafelyUnwrapped + } + + @usableFromInline @inline(__always) + @lifetime(borrow start) + init( + _unchecked start: UnsafeMutableRawPointer?, + count: Int + ) { + _pointer = start + _count = count + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan: @unchecked Sendable where Element: Sendable {} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + + @usableFromInline + @lifetime(borrow elements) + internal init( + _unchecked elements: UnsafeMutableBufferPointer + ) { + _pointer = .init(elements.baseAddress) + _count = elements.count + } + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _unsafeElements buffer: UnsafeMutableBufferPointer + ) { + precondition( + ((Int(bitPattern: buffer.baseAddress) & + (MemoryLayout.alignment&-1)) == 0), + "baseAddress must be properly aligned to access Element" + ) + let ms = MutableSpan(_unchecked: buffer) + self = _overrideLifetime(ms, borrowing: buffer) + } + + @_alwaysEmitIntoClient + @lifetime(borrow start) + public init( + _unsafeStart start: UnsafeMutablePointer, + count: Int + ) { + precondition(count >= 0, "Count must not be negative") + let buffer = UnsafeMutableBufferPointer(start: start, count: count) + let ms = MutableSpan(_unsafeElements: buffer) + self = _overrideLifetime(ms, borrowing: start) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan { + + @_alwaysEmitIntoClient + @lifetime(borrow elements) + public init( + _unsafeElements elements: borrowing Slice> + ) { + let rb = UnsafeMutableBufferPointer(rebasing: elements) + let ms = MutableSpan(_unsafeElements: rb) + self = _overrideLifetime(ms, borrowing: elements) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: BitwiseCopyable { + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _unsafeBytes buffer: UnsafeMutableRawBufferPointer + ) { + precondition( + ((Int(bitPattern: buffer.baseAddress) & + (MemoryLayout.alignment&-1)) == 0), + "baseAddress must be properly aligned to access Element" + ) + let (byteCount, stride) = (buffer.count, MemoryLayout.stride) + let (count, remainder) = byteCount.quotientAndRemainder(dividingBy: stride) + precondition(remainder == 0, "Span must contain a whole number of elements") + let elements = UnsafeMutableBufferPointer( + start: buffer.baseAddress?.assumingMemoryBound(to: Element.self), + count: count + ) + let ms = MutableSpan(_unsafeElements: elements) + self = _overrideLifetime(ms, borrowing: buffer) + } + + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + public init( + _unsafeStart pointer: UnsafeMutableRawPointer, + byteCount: Int + ) { + precondition(byteCount >= 0, "Count must not be negative") + let bytes = UnsafeMutableRawBufferPointer(start: pointer, count: byteCount) + let ms = MutableSpan(_unsafeBytes: bytes) + self = _overrideLifetime(ms, borrowing: pointer) + } + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _unsafeBytes buffer: borrowing Slice + ) { + let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer) + let ms = MutableSpan(_unsafeBytes: bytes) + self = _overrideLifetime(ms, borrowing: buffer) + } +} + +@available(SwiftStdlib 6.2, *) +extension Span where Element: ~Copyable { + + @_alwaysEmitIntoClient + @lifetime(borrow mutableSpan) + public init(_unsafeMutableSpan mutableSpan: borrowing MutableSpan) { + let pointer = mutableSpan._pointer?.assumingMemoryBound(to: Element.self) + let buffer = UnsafeBufferPointer(start: pointer, count: mutableSpan.count) + let span = Span(_unsafeElements: buffer) + self = _overrideLifetime(span, borrowing: mutableSpan) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public var span: Span { + @lifetime(borrow self) + borrowing get { + Span(_unsafeMutableSpan: self) + } + } +} + +@available(SwiftStdlib 6.2, *) +extension RawSpan { + + @_alwaysEmitIntoClient + public init( + _unsafeMutableSpan mutableSpan: borrowing MutableSpan + ) { + let pointer = mutableSpan._pointer + let byteCount = mutableSpan.count &* MemoryLayout.stride + let buffer = UnsafeRawBufferPointer(start: pointer, count: byteCount) + let rawSpan = RawSpan(_unsafeBytes: buffer) + self = _overrideLifetime(rawSpan, borrowing: mutableSpan) + } +} + +//@available(SwiftStdlib 6.2, *) +//extension MutableSpan where Element: Equatable { +// +// @_alwaysEmitIntoClient +// public func _elementsEqual(_ other: borrowing Self) -> Bool { +// _elementsEqual(Span(_unsafeMutableSpan: other)) +// } +// +// @_alwaysEmitIntoClient +// public func _elementsEqual(_ other: Span) -> Bool { +// Span(_unsafeMutableSpan: self)._elementsEqual(other) +// } +// +// @_alwaysEmitIntoClient +// public func _elementsEqual(_ other: some Collection) -> Bool { +// Span(_unsafeMutableSpan: self)._elementsEqual(other) +// } +// +// @_alwaysEmitIntoClient +// public func _elementsEqual(_ other: some Sequence) -> Bool { +// Span(_unsafeMutableSpan: self)._elementsEqual(other) +// } +//} +// +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public var _description: String { + let addr = String(UInt(bitPattern: _pointer), radix: 16, uppercase: false) + return "(0x\(addr), \(_count))" + } +} + +//MARK: Collection, RandomAccessCollection +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable & ~Escapable { + + @_alwaysEmitIntoClient + public var count: Int { _count } + + @_alwaysEmitIntoClient + public var isEmpty: Bool { _count == 0 } + + public typealias Index = Int + + @_alwaysEmitIntoClient + public var indices: Range { + Range(uncheckedBounds: (0, _count)) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: BitwiseCopyable { + + /// Construct a RawSpan over the memory represented by this span + /// + /// - Returns: a RawSpan over the memory represented by this span + @_alwaysEmitIntoClient + public var bytes: RawSpan { + @lifetime(borrow self) + borrowing get { + RawSpan(_unsafeMutableSpan: self) + } + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + + /// Accesses the element at the specified position in the `Span`. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public subscript(_ position: Index) -> Element { + unsafeAddress { + precondition(indices.contains(position), "index out of bounds") + return UnsafePointer(_unsafeAddressOfElement(unchecked: position)) + } + unsafeMutableAddress { + precondition(indices.contains(position), "index out of bounds") + return _unsafeAddressOfElement(unchecked: position) + } + } + + /// Accesses the element at the specified position in the `Span`. + /// + /// This subscript does not validate `position`; this is an unsafe operation. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public subscript(unchecked position: Index) -> Element { + unsafeAddress { + UnsafePointer(_unsafeAddressOfElement(unchecked: position)) + } + unsafeMutableAddress { + _unsafeAddressOfElement(unchecked: position) + } + } + + @unsafe + @_alwaysEmitIntoClient + internal func _unsafeAddressOfElement( + unchecked position: Index + ) -> UnsafeMutablePointer { + let elementOffset = position &* MemoryLayout.stride + let address = _start().advanced(by: elementOffset) + return address.assumingMemoryBound(to: Element.self) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public mutating func swapAt(_ i: Index, _ j: Index) { + precondition(indices.contains(Index(i))) + precondition(indices.contains(Index(j))) + swapAt(unchecked: i, unchecked: j) + } + + @_alwaysEmitIntoClient + public mutating func swapAt(unchecked i: Index, unchecked j: Index) { + let pi = _unsafeAddressOfElement(unchecked: i) + let pj = _unsafeAddressOfElement(unchecked: j) + let temporary = pi.move() + pi.initialize(to: pj.move()) + pj.initialize(to: consume temporary) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: BitwiseCopyable { + + /// Accesses the element at the specified position in the `Span`. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public subscript(_ position: Index) -> Element { + get { + precondition(indices.contains(position), "index out of bounds") + return self[unchecked: position] + } + set { + precondition(indices.contains(position), "index out of bounds") + self[unchecked: position] = newValue + } + } + + /// Accesses the element at the specified position in the `Span`. + /// + /// This subscript does not validate `position`; this is an unsafe operation. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public subscript(unchecked position: Index) -> Element { + get { + let offset = position&*MemoryLayout.stride + return _start().loadUnaligned(fromByteOffset: offset, as: Element.self) + } + set { + let offset = position&*MemoryLayout.stride + _start().storeBytes(of: newValue, toByteOffset: offset, as: Element.self) + } + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + + //FIXME: mark closure parameter as non-escaping + @_alwaysEmitIntoClient + public func withUnsafeBufferPointer( + _ body: (_ buffer: UnsafeBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + try Span(_unsafeMutableSpan: self).withUnsafeBufferPointer(body) + } + + //FIXME: mark closure parameter as non-escaping + @_alwaysEmitIntoClient + public mutating func withUnsafeMutableBufferPointer( + _ body: (UnsafeMutableBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + guard let pointer = _pointer, count > 0 else { + return try body(.init(start: nil, count: 0)) + } + // bind memory by hand to sidestep alignment concerns + let binding = Builtin.bindMemory( + pointer._rawValue, count._builtinWordValue, Element.self + ) + defer { Builtin.rebindMemory(pointer._rawValue, binding) } + return try body(.init(start: .init(pointer._rawValue), count: count)) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: BitwiseCopyable { + + //FIXME: mark closure parameter as non-escaping + @_alwaysEmitIntoClient + public func withUnsafeBytes( + _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + try RawSpan(_unsafeMutableSpan: self).withUnsafeBytes(body) + } + + //FIXME: mark closure parameter as non-escaping + @_alwaysEmitIntoClient + public mutating func withUnsafeMutableBytes( + _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + let bytes = UnsafeMutableRawBufferPointer( + start: (_count == 0) ? nil : _start(), + count: _count &* MemoryLayout.stride + ) + return try body(bytes) + } +} + +//MARK: bulk-update functions +@available(SwiftStdlib 6.2, *) +extension MutableSpan { + + @_alwaysEmitIntoClient + public mutating func update(repeating repeatedValue: consuming Element) { + _start().withMemoryRebound(to: Element.self, capacity: count) { + $0.update(repeating: repeatedValue, count: count) + } + } + + @_alwaysEmitIntoClient + public mutating func update( + from source: S + ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element { + var iterator = source.makeIterator() + let index = update(from: &iterator) + return (iterator, index) + } + + @_alwaysEmitIntoClient + public mutating func update( + from elements: inout some IteratorProtocol + ) -> Index { + var index = 0 + while index < _count { + guard let element = elements.next() else { break } + self[unchecked: index] = element + index &+= 1 + } + return index + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: some Collection + ) -> Index { + let updated = source.withContiguousStorageIfAvailable { + self.update(fromContentsOf: Span(_unsafeElements: $0)) + } + if let updated { + return updated + } + + //TODO: use _copyContents here + + var iterator = source.makeIterator() + let index = update(from: &iterator) + precondition( + iterator.next() == nil, + "destination buffer view cannot contain every element from source." + ) + return index + } + + @_alwaysEmitIntoClient + public mutating func update(fromContentsOf source: Span) -> Index { + guard !source.isEmpty else { return 0 } + precondition( + source.count <= self.count, + "destination span cannot contain every element from source." + ) + _start().withMemoryRebound(to: Element.self, capacity: source.count) { dest in + source.withUnsafeBufferPointer { + dest.update(from: $0.baseAddress!, count: $0.count) + } + } + return source.count + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: borrowing MutableSpan + ) -> Index { + update(fromContentsOf: source.span) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + +// @_alwaysEmitIntoClient +// public mutating func moveUpdate( +// fromContentsOf source: consuming OutputSpan +// ) -> Index { +// guard !source.isEmpty else { return 0 } +// precondition( +// source.count <= self.count, +// "destination span cannot contain every element from source." +// ) +// let buffer = source.relinquishBorrowedMemory() +// // we must now deinitialize the returned UMBP +// _start().moveInitializeMemory( +// as: Element.self, from: buffer.baseAddress!, count: buffer.count +// ) +// return buffer.count +// } + + @_alwaysEmitIntoClient + public mutating func moveUpdate( + fromContentsOf source: UnsafeMutableBufferPointer + ) -> Index { +// let source = OutputSpan(_initializing: source, initialized: source.count) +// return self.moveUpdate(fromContentsOf: source) + withUnsafeMutableBufferPointer { + $0.moveUpdate(fromContentsOf: source) + } + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan { + + @_alwaysEmitIntoClient + public mutating func moveUpdate( + fromContentsOf source: Slice> + ) -> Index { + moveUpdate(fromContentsOf: UnsafeMutableBufferPointer(rebasing: source)) + } +} + +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: BitwiseCopyable { + + @_alwaysEmitIntoClient + public mutating func update( + repeating repeatedValue: Element + ) where Element: BitwiseCopyable { + guard count > 0 else { return } + // rebind _start manually in order to avoid assumptions about alignment. + let rp = _start()._rawValue + let binding = Builtin.bindMemory(rp, count._builtinWordValue, Element.self) + UnsafeMutablePointer(rp).update(repeating: repeatedValue, count: count) + Builtin.rebindMemory(rp, binding) + } + + @_alwaysEmitIntoClient + public mutating func update( + from source: S + ) -> (unwritten: S.Iterator, index: Index) + where S.Element == Element, Element: BitwiseCopyable { + var iterator = source.makeIterator() + let index = update(from: &iterator) + return (iterator, index) + } + + @_alwaysEmitIntoClient + public mutating func update( + from elements: inout some IteratorProtocol + ) -> Index { + var index = 0 + while index < _count { + guard let element = elements.next() else { break } + self[unchecked: index] = element + index &+= 1 + } + return index + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: some Collection + ) -> Index where Element: BitwiseCopyable { + let updated = source.withContiguousStorageIfAvailable { + self.update(fromContentsOf: Span(_unsafeElements: $0)) + } + if let updated { + return updated + } + + //TODO: use _copyContents here + + var iterator = source.makeIterator() + let index = update(from: &iterator) + precondition( + iterator.next() == nil, + "destination buffer view cannot contain every element from source." + ) + return index + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: Span + ) -> Index where Element: BitwiseCopyable { + guard !source.isEmpty else { return 0 } + precondition( + source.count <= self.count, + "destination span cannot contain every element from source." + ) + source.withUnsafeBufferPointer { + _start().copyMemory( + from: $0.baseAddress!, byteCount: $0.count&*MemoryLayout.stride + ) + } + return source.count + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: borrowing MutableSpan + ) -> Index where Element: BitwiseCopyable { + update(fromContentsOf: source.span) + } +} From ad8e91bc77d567994ead0622a2e99fcf646bff88 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 26 Feb 2025 16:51:27 -0800 Subject: [PATCH 02/23] [test] MutableSpan tests --- test/stdlib/Span/MutableSpanTests.swift | 574 ++++++++++++++++++++++++ 1 file changed, 574 insertions(+) create mode 100644 test/stdlib/Span/MutableSpanTests.swift diff --git a/test/stdlib/Span/MutableSpanTests.swift b/test/stdlib/Span/MutableSpanTests.swift new file mode 100644 index 0000000000000..5a18db2d90850 --- /dev/null +++ b/test/stdlib/Span/MutableSpanTests.swift @@ -0,0 +1,574 @@ +//===--- MutableSpanTests.swift -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// RUN: %target-run-stdlib-swift + +// REQUIRES: executable_test + +import StdlibUnittest + +var suite = TestSuite("MutableSpan Tests") +defer { runAllTests() } + +suite.test("Initialize with ordinary element") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let capacity = 4 + var s = (0..(_unsafeStart: rp, byteCount: bc) + expectEqual(b.count, capacity) + + let stride = MemoryLayout.stride + let r = MutableSpan(_unsafeBytes: $0.dropFirst(stride)) + expectEqual(r.count, (capacity-1)*stride) + expectEqual(r.count, bc-stride) + } + + let v = UnsafeMutableRawBufferPointer(start: nil, count: 0) + let m = MutableSpan(_unsafeBytes: v) + expectEqual(m.count, 0) +} + +suite.test("isEmpty") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + var array = [0, 1, 2] + array.withUnsafeMutableBufferPointer { + let span = MutableSpan(_unsafeElements: $0) + let e = span.isEmpty + expectFalse(e) + } + + array = [] + array.withUnsafeMutableBufferPointer { + let span = MutableSpan(_unsafeElements: $0) + let e = span.isEmpty + expectTrue(e) + } +} + +suite.test("Span from MutableSpan") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + var array = [0, 1, 2] + array.withUnsafeMutableBufferPointer { + let mutable = MutableSpan(_unsafeElements: $0) + let immutable = Span(_unsafeMutableSpan: mutable) + expectEqual(mutable.count, immutable.count) + } +} + +suite.test("RawSpan from MutableSpan") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let count = 4 + var array = Array(0...stride) + } +} + +suite.test("indices property") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let capacity = 4 + var a = Array(0..(start: nil, count: 0) + var span = MutableSpan(_unsafeElements: empty) + var (iterator, updated) = span.update(from: 0..<0) + expectNil(iterator.next()) + expectEqual(updated, 0) + + span = MutableSpan(_unsafeElements: $0) + (iterator, updated) = span.update(from: 0..<0) + expectNil(iterator.next()) + expectEqual(updated, 0) + + (iterator, updated) = span.update(from: 0..<10000) + expectNotNil(iterator.next()) + expectEqual(updated, capacity) + } + expectEqual(a.elementsEqual(0...allocate(capacity: 2*capacity) + let i = b.initialize(fromContentsOf: (0..<2*capacity).map(ID.init(id:))) + expectEqual(i, 2*capacity) + + a.withUnsafeMutableBufferPointer { + var span = MutableSpan(_unsafeElements: $0) + let updated = span.moveUpdate(fromContentsOf: b.suffix(capacity)) + expectEqual(updated, capacity) + } + expectEqual(a.map(\.id).elementsEqual(capacity..<2*capacity), true) + + a = [] + b.prefix(capacity).deinitialize() + b.deallocate() +} + +suite.test("span property") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let count = 8 + let b = UnsafeMutableBufferPointer.allocate(capacity: count) + _ = b.initialize(fromContentsOf: 0..(start: nil, count: 0) + defer { _ = e } + + var m = MutableSpan(_unsafeElements: b) + m[0] = 100 + expectEqual(m.count, count) + expectEqual(m[0], 100) + + var s = m.span + expectEqual(s.count, m.count) + expectEqual(s[0], m[0]) + + // we're done using `s` before it gets reassigned + m.update(repeating: 7) + + s = m.span + +// m[0] = -1 // exclusivity violation + + expectEqual(s.count, m.count) + expectEqual(s[0], m[0]) +} + +suite.test("swapAt") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let count = 8 + var array = Array(0.. Date: Wed, 26 Feb 2025 17:01:02 -0800 Subject: [PATCH 03/23] [test] own up to new abi --- test/abi/macOS/arm64/stdlib.swift | 16 +++++++++++++++- test/abi/macOS/x86_64/stdlib.swift | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/test/abi/macOS/arm64/stdlib.swift b/test/abi/macOS/arm64/stdlib.swift index 9951cc0f73e92..ca6d948c6961d 100644 --- a/test/abi/macOS/arm64/stdlib.swift +++ b/test/abi/macOS/arm64/stdlib.swift @@ -814,7 +814,21 @@ Added: _$ss7RawSpanVMa Added: _$ss7RawSpanVMn Added: _$ss7RawSpanVN -// Span-providing properties +// SE-0467 MutableSpan and MutableRawSpan +Added: _$ss11MutableSpanVMa +Added: _$ss11MutableSpanVMn +Added: _$ss11MutableSpanVsRi_zRi0_zrlE10_unchecked5countAByxGSvSg_SitcfC +Added: _$ss11MutableSpanVsRi_zRi0_zrlE6_countSivg +Added: _$ss11MutableSpanVsRi_zRi0_zrlE8_pointerSvSgvg +Added: _$ss11MutableSpanVsRi_zrlE10_uncheckedAByxGSryxG_tcfC +Added: _$ss14MutableRawSpanV10_unchecked5countABSvSg_SitcfC +Added: _$ss14MutableRawSpanV6_countSivg +Added: _$ss14MutableRawSpanV8_pointerSvSgvg +Added: _$ss14MutableRawSpanVMa +Added: _$ss14MutableRawSpanVMn +Added: _$ss14MutableRawSpanVN + +// SE-0456 Span-providing properties Added: _$sSRsRi_zrlE4spans4SpanVyxGvpMV Added: _$sSW5bytess7RawSpanVvpMV Added: _$sSa4spans4SpanVyxGvpMV diff --git a/test/abi/macOS/x86_64/stdlib.swift b/test/abi/macOS/x86_64/stdlib.swift index bc76c800889f9..f9ef101cd29af 100644 --- a/test/abi/macOS/x86_64/stdlib.swift +++ b/test/abi/macOS/x86_64/stdlib.swift @@ -815,7 +815,21 @@ Added: _$ss7RawSpanVMa Added: _$ss7RawSpanVMn Added: _$ss7RawSpanVN -// Span-providing properties +// SE-0467 MutableSpan and MutableRawSpan +Added: _$ss11MutableSpanVMa +Added: _$ss11MutableSpanVMn +Added: _$ss11MutableSpanVsRi_zRi0_zrlE10_unchecked5countAByxGSvSg_SitcfC +Added: _$ss11MutableSpanVsRi_zRi0_zrlE6_countSivg +Added: _$ss11MutableSpanVsRi_zRi0_zrlE8_pointerSvSgvg +Added: _$ss11MutableSpanVsRi_zrlE10_uncheckedAByxGSryxG_tcfC +Added: _$ss14MutableRawSpanV10_unchecked5countABSvSg_SitcfC +Added: _$ss14MutableRawSpanV6_countSivg +Added: _$ss14MutableRawSpanV8_pointerSvSgvg +Added: _$ss14MutableRawSpanVMa +Added: _$ss14MutableRawSpanVMn +Added: _$ss14MutableRawSpanVN + +// SE-0456 Span-providing properties Added: _$sSRsRi_zrlE4spans4SpanVyxGvpMV Added: _$sSW5bytess7RawSpanVvpMV Added: _$sSa4spans4SpanVyxGvpMV From 2fa2391c28f77b25785ee5c145e8bd58b0e0c479 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 26 Feb 2025 17:13:01 -0800 Subject: [PATCH 04/23] [stdlib] remove 2 symbols --- stdlib/public/core/Span/MutableRawSpan.swift | 4 ++-- stdlib/public/core/Span/MutableSpan.swift | 4 ++-- test/abi/macOS/arm64/stdlib.swift | 2 -- test/abi/macOS/x86_64/stdlib.swift | 2 -- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index e6e800585be13..af50f8c56c087 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -26,9 +26,9 @@ public struct MutableRawSpan: ~Copyable & ~Escapable { _pointer.unsafelyUnwrapped } - @usableFromInline @inline(__always) + @_alwaysEmitIntoClient @lifetime(borrow pointer) - init( + internal init( _unchecked pointer: UnsafeMutableRawPointer?, count: Int ) { diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index ec52a70772cb5..88869624037d2 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -27,9 +27,9 @@ public struct MutableSpan _pointer.unsafelyUnwrapped } - @usableFromInline @inline(__always) + @_alwaysEmitIntoClient @lifetime(borrow start) - init( + internal init( _unchecked start: UnsafeMutableRawPointer?, count: Int ) { diff --git a/test/abi/macOS/arm64/stdlib.swift b/test/abi/macOS/arm64/stdlib.swift index ca6d948c6961d..b76a53699f5b8 100644 --- a/test/abi/macOS/arm64/stdlib.swift +++ b/test/abi/macOS/arm64/stdlib.swift @@ -817,11 +817,9 @@ Added: _$ss7RawSpanVN // SE-0467 MutableSpan and MutableRawSpan Added: _$ss11MutableSpanVMa Added: _$ss11MutableSpanVMn -Added: _$ss11MutableSpanVsRi_zRi0_zrlE10_unchecked5countAByxGSvSg_SitcfC Added: _$ss11MutableSpanVsRi_zRi0_zrlE6_countSivg Added: _$ss11MutableSpanVsRi_zRi0_zrlE8_pointerSvSgvg Added: _$ss11MutableSpanVsRi_zrlE10_uncheckedAByxGSryxG_tcfC -Added: _$ss14MutableRawSpanV10_unchecked5countABSvSg_SitcfC Added: _$ss14MutableRawSpanV6_countSivg Added: _$ss14MutableRawSpanV8_pointerSvSgvg Added: _$ss14MutableRawSpanVMa diff --git a/test/abi/macOS/x86_64/stdlib.swift b/test/abi/macOS/x86_64/stdlib.swift index f9ef101cd29af..cb1c58d61af3b 100644 --- a/test/abi/macOS/x86_64/stdlib.swift +++ b/test/abi/macOS/x86_64/stdlib.swift @@ -818,11 +818,9 @@ Added: _$ss7RawSpanVN // SE-0467 MutableSpan and MutableRawSpan Added: _$ss11MutableSpanVMa Added: _$ss11MutableSpanVMn -Added: _$ss11MutableSpanVsRi_zRi0_zrlE10_unchecked5countAByxGSvSg_SitcfC Added: _$ss11MutableSpanVsRi_zRi0_zrlE6_countSivg Added: _$ss11MutableSpanVsRi_zRi0_zrlE8_pointerSvSgvg Added: _$ss11MutableSpanVsRi_zrlE10_uncheckedAByxGSryxG_tcfC -Added: _$ss14MutableRawSpanV10_unchecked5countABSvSg_SitcfC Added: _$ss14MutableRawSpanV6_countSivg Added: _$ss14MutableRawSpanV8_pointerSvSgvg Added: _$ss14MutableRawSpanVMa From f214ef29d03d0cc8889ef2773da336fb6b988f7e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 27 Feb 2025 03:14:42 -0800 Subject: [PATCH 05/23] [stdlib] small spelling fixes --- stdlib/public/core/Span/MutableRawSpan.swift | 20 ++++++------ stdlib/public/core/Span/MutableSpan.swift | 34 ++++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index af50f8c56c087..9492d1b587e47 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -30,10 +30,10 @@ public struct MutableRawSpan: ~Copyable & ~Escapable { @lifetime(borrow pointer) internal init( _unchecked pointer: UnsafeMutableRawPointer?, - count: Int + byteCount: Int ) { _pointer = pointer - _count = count + _count = byteCount } } @@ -49,7 +49,7 @@ extension MutableRawSpan { _unsafeBytes bytes: UnsafeMutableRawBufferPointer ) { let baseAddress = bytes.baseAddress - let span = MutableRawSpan(_unchecked: baseAddress, count: bytes.count) + let span = MutableRawSpan(_unchecked: baseAddress, byteCount: bytes.count) self = _overrideLifetime(span, borrowing: bytes) } @@ -69,8 +69,8 @@ extension MutableRawSpan { _unsafeStart pointer: UnsafeMutableRawPointer, byteCount: Int ) { - precondition(byteCount >= 0, "Count must not be negative") - self.init(_unchecked: pointer, count: byteCount) + _precondition(byteCount >= 0, "Count must not be negative") + self.init(_unchecked: pointer, byteCount: byteCount) } @_alwaysEmitIntoClient @@ -117,7 +117,7 @@ extension MutableRawSpan { @_alwaysEmitIntoClient public var byteOffsets: Range { - .init(uncheckedBounds: (0, byteCount)) + .init(_uncheckedBounds: (0, byteCount)) } } @@ -216,7 +216,7 @@ extension MutableRawSpan { public func unsafeLoad( fromByteOffset offset: Int = 0, as: T.Type ) -> T { - precondition( + _precondition( UInt(bitPattern: offset) <= UInt(bitPattern: _count) && MemoryLayout.size <= (_count &- offset), "Byte offset range out of bounds" @@ -271,7 +271,7 @@ extension MutableRawSpan { public func unsafeLoadUnaligned( fromByteOffset offset: Int = 0, as: T.Type ) -> T { - precondition( + _precondition( UInt(bitPattern: offset) <= UInt(bitPattern: _count) && MemoryLayout.size <= (_count &- offset), "Byte offset range out of bounds" @@ -308,7 +308,7 @@ extension MutableRawSpan { public func storeBytes( of value: T, toByteOffset offset: Int = 0, as type: T.Type ) { - precondition( + _precondition( UInt(bitPattern: offset) <= UInt(bitPattern: _count) && MemoryLayout.size <= (_count &- offset), "Byte offset range out of bounds" @@ -367,7 +367,7 @@ extension MutableRawSpan { var elements = source.makeIterator() let lastOffset = update(startingAt: byteOffset, from: &elements) - precondition( + _precondition( elements.next() == nil, "destination span cannot contain every element from source." ) diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index 88869624037d2..cdcc540c6b9f1 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -58,7 +58,7 @@ extension MutableSpan where Element: ~Copyable { public init( _unsafeElements buffer: UnsafeMutableBufferPointer ) { - precondition( + _precondition( ((Int(bitPattern: buffer.baseAddress) & (MemoryLayout.alignment&-1)) == 0), "baseAddress must be properly aligned to access Element" @@ -73,7 +73,7 @@ extension MutableSpan where Element: ~Copyable { _unsafeStart start: UnsafeMutablePointer, count: Int ) { - precondition(count >= 0, "Count must not be negative") + _precondition(count >= 0, "Count must not be negative") let buffer = UnsafeMutableBufferPointer(start: start, count: count) let ms = MutableSpan(_unsafeElements: buffer) self = _overrideLifetime(ms, borrowing: start) @@ -102,14 +102,14 @@ extension MutableSpan where Element: BitwiseCopyable { public init( _unsafeBytes buffer: UnsafeMutableRawBufferPointer ) { - precondition( + _precondition( ((Int(bitPattern: buffer.baseAddress) & (MemoryLayout.alignment&-1)) == 0), "baseAddress must be properly aligned to access Element" ) let (byteCount, stride) = (buffer.count, MemoryLayout.stride) let (count, remainder) = byteCount.quotientAndRemainder(dividingBy: stride) - precondition(remainder == 0, "Span must contain a whole number of elements") + _precondition(remainder == 0, "Span must contain a whole number of elements") let elements = UnsafeMutableBufferPointer( start: buffer.baseAddress?.assumingMemoryBound(to: Element.self), count: count @@ -124,7 +124,7 @@ extension MutableSpan where Element: BitwiseCopyable { _unsafeStart pointer: UnsafeMutableRawPointer, byteCount: Int ) { - precondition(byteCount >= 0, "Count must not be negative") + _precondition(byteCount >= 0, "Count must not be negative") let bytes = UnsafeMutableRawBufferPointer(start: pointer, count: byteCount) let ms = MutableSpan(_unsafeBytes: bytes) self = _overrideLifetime(ms, borrowing: pointer) @@ -229,7 +229,7 @@ extension MutableSpan where Element: ~Copyable & ~Escapable { @_alwaysEmitIntoClient public var indices: Range { - Range(uncheckedBounds: (0, _count)) + Range(_uncheckedBounds: (0, _count)) } } @@ -260,11 +260,11 @@ extension MutableSpan where Element: ~Copyable { @_alwaysEmitIntoClient public subscript(_ position: Index) -> Element { unsafeAddress { - precondition(indices.contains(position), "index out of bounds") + _precondition(indices.contains(position), "index out of bounds") return UnsafePointer(_unsafeAddressOfElement(unchecked: position)) } unsafeMutableAddress { - precondition(indices.contains(position), "index out of bounds") + _precondition(indices.contains(position), "index out of bounds") return _unsafeAddressOfElement(unchecked: position) } } @@ -303,8 +303,8 @@ extension MutableSpan where Element: ~Copyable { @_alwaysEmitIntoClient public mutating func swapAt(_ i: Index, _ j: Index) { - precondition(indices.contains(Index(i))) - precondition(indices.contains(Index(j))) + _precondition(indices.contains(Index(i))) + _precondition(indices.contains(Index(j))) swapAt(unchecked: i, unchecked: j) } @@ -330,11 +330,11 @@ extension MutableSpan where Element: BitwiseCopyable { @_alwaysEmitIntoClient public subscript(_ position: Index) -> Element { get { - precondition(indices.contains(position), "index out of bounds") + _precondition(indices.contains(position), "index out of bounds") return self[unchecked: position] } set { - precondition(indices.contains(position), "index out of bounds") + _precondition(indices.contains(position), "index out of bounds") self[unchecked: position] = newValue } } @@ -460,7 +460,7 @@ extension MutableSpan { var iterator = source.makeIterator() let index = update(from: &iterator) - precondition( + _precondition( iterator.next() == nil, "destination buffer view cannot contain every element from source." ) @@ -470,7 +470,7 @@ extension MutableSpan { @_alwaysEmitIntoClient public mutating func update(fromContentsOf source: Span) -> Index { guard !source.isEmpty else { return 0 } - precondition( + _precondition( source.count <= self.count, "destination span cannot contain every element from source." ) @@ -498,7 +498,7 @@ extension MutableSpan where Element: ~Copyable { // fromContentsOf source: consuming OutputSpan // ) -> Index { // guard !source.isEmpty else { return 0 } -// precondition( +// _precondition( // source.count <= self.count, // "destination span cannot contain every element from source." // ) @@ -586,7 +586,7 @@ extension MutableSpan where Element: BitwiseCopyable { var iterator = source.makeIterator() let index = update(from: &iterator) - precondition( + _precondition( iterator.next() == nil, "destination buffer view cannot contain every element from source." ) @@ -598,7 +598,7 @@ extension MutableSpan where Element: BitwiseCopyable { fromContentsOf source: Span ) -> Index where Element: BitwiseCopyable { guard !source.isEmpty else { return 0 } - precondition( + _precondition( source.count <= self.count, "destination span cannot contain every element from source." ) From 484905b42dd02dfb3a8bb236731b8fbadcb7addb Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 27 Feb 2025 03:17:34 -0800 Subject: [PATCH 06/23] [stdlib] add extracting() to MutableSpan and MutableRawSpan --- stdlib/public/core/Span/MutableRawSpan.swift | 228 ++++++++++++++++++ stdlib/public/core/Span/MutableSpan.swift | 229 +++++++++++++++++++ 2 files changed, 457 insertions(+) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index 9492d1b587e47..b44340206ac37 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -417,3 +417,231 @@ extension MutableRawSpan { update(startingAt: byteOffset, from: source.bytes) } } + +// MARK: sub-spans +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan { + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(_ bounds: Range) -> Self { + _precondition( + UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && + UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), + "Index range out of bounds" + ) + return _extracting(unchecked: bounds) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(unchecked bounds: Range) -> Self { + let newStart = _pointer?.advanced(by: bounds.lowerBound) + let newSpan = Self(_unchecked: newStart, byteCount: bounds.count) + return _overrideLifetime(newSpan, mutating: &self) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + _ bounds: some RangeExpression + ) -> Self { + _extracting(bounds.relative(to: byteOffsets).clamped(to: byteOffsets)) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + unchecked bounds: some RangeExpression + ) -> Self { + _extracting( + unchecked: bounds.relative(to: byteOffsets).clamped(to: byteOffsets) + ) + } + + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + unchecked bounds: ClosedRange + ) -> Self { + let range = Range( + _uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1) + ) + return _extracting(unchecked: range) + } + + /// Constructs a new span over all the items of this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Returns: A `MutableSpan` over all the items of this span. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(_: UnboundedRange) -> Self { + let newSpan = Self(_unchecked: _start(), byteCount: _count) + return _overrideLifetime(newSpan, mutating: &self) + } +} + +// MARK: prefixes and suffixes +@available(SwiftStdlib 6.2, *) +extension MutableRawSpan { + + /// Returns a span containing the initial elements of this span, + /// up to the specified maximum length. + /// + /// If the maximum length exceeds the length of this span, + /// the result contains all the elements. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter maxLength: The maximum number of elements to return. + /// `maxLength` must be greater than or equal to zero. + /// - Returns: A span with at most `maxLength` elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(first maxLength: Int) -> Self { + _precondition(maxLength >= 0, "Can't have a prefix of negative length") + let newCount = min(maxLength, byteCount) + let newSpan = Self(_unchecked: _pointer, byteCount: newCount) + return _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span over all but the given number of trailing elements. + /// + /// If the number of elements to drop exceeds the number of elements in + /// the span, the result is an empty span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter k: The number of elements to drop off the end of + /// the span. `k` must be greater than or equal to zero. + /// - Returns: A span leaving off the specified number of elements at the end. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(droppingLast k: Int) -> Self { + _precondition(k >= 0, "Can't drop a negative number of elements") + let dropped = min(k, byteCount) + let newSpan = Self(_unchecked: _pointer, byteCount: byteCount &- dropped) + return _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span containing the final elements of the span, + /// up to the given maximum length. + /// + /// If the maximum length exceeds the length of this span, + /// the result contains all the elements. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter maxLength: The maximum number of elements to return. + /// `maxLength` must be greater than or equal to zero. + /// - Returns: A span with at most `maxLength` elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(last maxLength: Int) -> Self { + _precondition(maxLength >= 0, "Can't have a suffix of negative length") + let newCount = min(maxLength, byteCount) + let newStart = _pointer?.advanced(by: byteCount &- newCount) + let newSpan = Self(_unchecked: newStart, byteCount: newCount) + return _overrideLifetime(newSpan, copying: self) + } + + /// Returns a span over all but the given number of initial elements. + /// + /// If the number of elements to drop exceeds the number of elements in + /// the span, the result is an empty span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter k: The number of elements to drop from the beginning of + /// the span. `k` must be greater than or equal to zero. + /// - Returns: A span starting after the specified number of elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(droppingFirst k: Int) -> Self { + _precondition(k >= 0, "Can't drop a negative number of bytes") + let dropped = min(k, byteCount) + let newStart = _pointer?.advanced(by: dropped) + let newSpan = Self(_unchecked: newStart, byteCount: byteCount &- dropped) + return _overrideLifetime(newSpan, mutating: &self) + } +} diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index cdcc540c6b9f1..92c1cc13b29b2 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -617,3 +617,232 @@ extension MutableSpan where Element: BitwiseCopyable { update(fromContentsOf: source.span) } } + +// MARK: sub-spans +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(_ bounds: Range) -> Self { + _precondition( + UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && + UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), + "Index range out of bounds" + ) + return _extracting(unchecked: bounds) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(unchecked bounds: Range) -> Self { + let delta = bounds.lowerBound &* MemoryLayout.stride + let newStart = _pointer?.advanced(by: delta) + let newSpan = Self(_unchecked: newStart, count: bounds.count) + return _overrideLifetime(newSpan, mutating: &self) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + _ bounds: some RangeExpression + ) -> Self { + _extracting(bounds.relative(to: indices)) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + unchecked bounds: some RangeExpression + ) -> Self { + _extracting(unchecked: bounds.relative(to: indices)) + } + + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + unchecked bounds: ClosedRange + ) -> Self { + let range = Range( + _uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1) + ) + return _extracting(unchecked: range) + } + + /// Constructs a new span over all the items of this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Returns: A `MutableSpan` over all the items of this span. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(_: UnboundedRange) -> Self { + let newSpan = Self(_unchecked: _start(), count: _count) + return _overrideLifetime(newSpan, mutating: &self) + } +} + +// MARK: prefixes and suffixes +@available(SwiftStdlib 6.2, *) +extension MutableSpan where Element: ~Copyable { + + /// Returns a span containing the initial elements of this span, + /// up to the specified maximum length. + /// + /// If the maximum length exceeds the length of this span, + /// the result contains all the elements. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter maxLength: The maximum number of elements to return. + /// `maxLength` must be greater than or equal to zero. + /// - Returns: A span with at most `maxLength` elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(first maxLength: Int) -> Self { + _precondition(maxLength >= 0, "Can't have a prefix of negative length") + let newCount = min(maxLength, count) + let newSpan = Self(_unchecked: _pointer, count: newCount) + return _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span over all but the given number of trailing elements. + /// + /// If the number of elements to drop exceeds the number of elements in + /// the span, the result is an empty span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter k: The number of elements to drop off the end of + /// the span. `k` must be greater than or equal to zero. + /// - Returns: A span leaving off the specified number of elements at the end. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(droppingLast k: Int) -> Self { + _precondition(k >= 0, "Can't drop a negative number of elements") + let droppedCount = min(k, count) + let newSpan = Self(_unchecked: _pointer, count: count &- droppedCount) + return _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span containing the final elements of the span, + /// up to the given maximum length. + /// + /// If the maximum length exceeds the length of this span, + /// the result contains all the elements. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter maxLength: The maximum number of elements to return. + /// `maxLength` must be greater than or equal to zero. + /// - Returns: A span with at most `maxLength` elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(last maxLength: Int) -> Self { + _precondition(maxLength >= 0, "Can't have a suffix of negative length") + let newCount = min(maxLength, count) + let offset = (count &- newCount) * MemoryLayout.stride + let newStart = _pointer?.advanced(by: offset) + let newSpan = Self(_unchecked: newStart, count: newCount) + return _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span over all but the given number of initial elements. + /// + /// If the number of elements to drop exceeds the number of elements in + /// the span, the result is an empty span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter k: The number of elements to drop from the beginning of + /// the span. `k` must be greater than or equal to zero. + /// - Returns: A span starting after the specified number of elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(droppingFirst k: Int) -> Self { + _precondition(k >= 0, "Can't drop a negative number of elements") + let droppedCount = min(k, count) + let offset = droppedCount * MemoryLayout.stride + let newStart = _pointer?.advanced(by: offset) + let newSpan = Self(_unchecked: newStart, count: count &- droppedCount) + return _overrideLifetime(newSpan, mutating: &self) + } +} From cbbe5df00b154d0dba8ed94d78ba2396adb69ace Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 28 Feb 2025 18:14:26 -0800 Subject: [PATCH 07/23] [stdlib] add unsafe annotations --- stdlib/public/core/Span/MutableRawSpan.swift | 90 ++++----- stdlib/public/core/Span/MutableSpan.swift | 181 +++++++++---------- 2 files changed, 137 insertions(+), 134 deletions(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index b44340206ac37..e0bafa10eac8e 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -12,6 +12,7 @@ // A MutableRawSpan represents a span of memory which // contains initialized `Element` instances. +@safe @frozen @available(SwiftStdlib 6.2, *) public struct MutableRawSpan: ~Copyable & ~Escapable { @@ -23,7 +24,7 @@ public struct MutableRawSpan: ~Copyable & ~Escapable { @_alwaysEmitIntoClient internal func _start() -> UnsafeMutableRawPointer { - _pointer.unsafelyUnwrapped + unsafe _pointer.unsafelyUnwrapped } @_alwaysEmitIntoClient @@ -32,7 +33,7 @@ public struct MutableRawSpan: ~Copyable & ~Escapable { _unchecked pointer: UnsafeMutableRawPointer?, byteCount: Int ) { - _pointer = pointer + _pointer = unsafe pointer _count = byteCount } } @@ -50,7 +51,7 @@ extension MutableRawSpan { ) { let baseAddress = bytes.baseAddress let span = MutableRawSpan(_unchecked: baseAddress, byteCount: bytes.count) - self = _overrideLifetime(span, borrowing: bytes) + self = unsafe _overrideLifetime(span, borrowing: bytes) } @_alwaysEmitIntoClient @@ -58,9 +59,9 @@ extension MutableRawSpan { public init( _unsafeBytes bytes: borrowing Slice ) { - let rebased = UnsafeMutableRawBufferPointer(rebasing: bytes) + let rebased = unsafe UnsafeMutableRawBufferPointer(rebasing: bytes) let span = MutableRawSpan(_unsafeBytes: rebased) - self = _overrideLifetime(span, borrowing: bytes) + self = unsafe _overrideLifetime(span, borrowing: bytes) } @_alwaysEmitIntoClient @@ -80,7 +81,7 @@ extension MutableRawSpan { ) { let bytes = UnsafeMutableRawBufferPointer(elements) let span = MutableRawSpan(_unsafeBytes: bytes) - self = _overrideLifetime(span, borrowing: elements) + self = unsafe _overrideLifetime(span, borrowing: elements) } @_alwaysEmitIntoClient @@ -88,9 +89,9 @@ extension MutableRawSpan { public init( _unsafeElements elements: borrowing Slice> ) { - let rebased = UnsafeMutableBufferPointer(rebasing: elements) + let rebased = unsafe UnsafeMutableBufferPointer(rebasing: elements) let span = MutableRawSpan(_unsafeElements: rebased) - self = _overrideLifetime(span, borrowing: elements) + self = unsafe _overrideLifetime(span, borrowing: elements) } @_alwaysEmitIntoClient @@ -98,12 +99,12 @@ extension MutableRawSpan { public init( _elements elements: consuming MutableSpan ) { - let bytes = UnsafeMutableRawBufferPointer( + let bytes = unsafe UnsafeMutableRawBufferPointer( start: elements._pointer, count: elements.count &* MemoryLayout.stride ) let span = MutableRawSpan(_unsafeBytes: bytes) - self = _overrideLifetime(span, copying: elements) + self = unsafe _overrideLifetime(span, copying: elements) } } @@ -129,9 +130,9 @@ extension MutableRawSpan { _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result ) throws(E) -> Result { guard let pointer = _pointer, _count > 0 else { - return try body(.init(start: nil, count: 0)) + return try unsafe body(.init(start: nil, count: 0)) } - return try body(.init(start: pointer, count: _count)) + return try unsafe body(.init(start: pointer, count: _count)) } @_alwaysEmitIntoClient @@ -139,9 +140,9 @@ extension MutableRawSpan { _ body: (UnsafeMutableRawBufferPointer) throws(E) -> Result ) throws(E) -> Result { guard let pointer = _pointer, _count > 0 else { - return try body(.init(start: nil, count: 0)) + return try unsafe body(.init(start: nil, count: 0)) } - return try body(.init(start: pointer, count: _count)) + return try unsafe body(.init(start: pointer, count: _count)) } } @@ -153,7 +154,7 @@ extension RawSpan { public init(_unsafeMutableRawSpan mutableSpan: borrowing MutableRawSpan) { let start = mutableSpan._start() let span = RawSpan(_unsafeStart: start, byteCount: mutableSpan.byteCount) - self = _overrideLifetime(span, borrowing: mutableSpan) + self = unsafe _overrideLifetime(span, borrowing: mutableSpan) } } @@ -174,9 +175,9 @@ extension MutableRawSpan { public borrowing func _unsafeView( as type: T.Type ) -> Span { - let bytes = UnsafeRawBufferPointer(start: _pointer, count: _count) + let bytes = unsafe UnsafeRawBufferPointer(start: _pointer, count: _count) let span = Span(_unsafeBytes: bytes) - return _overrideLifetime(span, borrowing: self) + return unsafe _overrideLifetime(span, borrowing: self) } @unsafe @@ -185,9 +186,11 @@ extension MutableRawSpan { public mutating func _unsafeMutableView( as type: T.Type ) -> MutableSpan { - let bytes = UnsafeMutableRawBufferPointer(start: _pointer, count: _count) + let bytes = unsafe UnsafeMutableRawBufferPointer( + start: _pointer, count: _count + ) let span = MutableSpan(_unsafeBytes: bytes) - return _overrideLifetime(span, mutating: &self) + return unsafe _overrideLifetime(span, mutating: &self) } } @@ -221,7 +224,7 @@ extension MutableRawSpan { MemoryLayout.size <= (_count &- offset), "Byte offset range out of bounds" ) - return unsafeLoad(fromUncheckedByteOffset: offset, as: T.self) + return unsafe unsafeLoad(fromUncheckedByteOffset: offset, as: T.self) } /// Returns a new instance of the given type, constructed from the raw memory @@ -247,7 +250,7 @@ extension MutableRawSpan { public func unsafeLoad( fromUncheckedByteOffset offset: Int, as: T.Type ) -> T { - _start().load(fromByteOffset: offset, as: T.self) + unsafe _start().load(fromByteOffset: offset, as: T.self) } /// Returns a new instance of the given type, constructed from the raw memory @@ -276,7 +279,7 @@ extension MutableRawSpan { MemoryLayout.size <= (_count &- offset), "Byte offset range out of bounds" ) - return unsafeLoadUnaligned(fromUncheckedByteOffset: offset, as: T.self) + return unsafe unsafeLoadUnaligned(fromUncheckedByteOffset: offset, as: T.self) } /// Returns a new instance of the given type, constructed from the raw memory @@ -301,7 +304,7 @@ extension MutableRawSpan { public func unsafeLoadUnaligned( fromUncheckedByteOffset offset: Int, as: T.Type ) -> T { - _start().loadUnaligned(fromByteOffset: offset, as: T.self) + unsafe _start().loadUnaligned(fromByteOffset: offset, as: T.self) } @_alwaysEmitIntoClient @@ -313,7 +316,7 @@ extension MutableRawSpan { MemoryLayout.size <= (_count &- offset), "Byte offset range out of bounds" ) - storeBytes(of: value, toUncheckedByteOffset: offset, as: type) + unsafe storeBytes(of: value, toUncheckedByteOffset: offset, as: type) } @unsafe @@ -321,7 +324,7 @@ extension MutableRawSpan { public func storeBytes( of value: T, toUncheckedByteOffset offset: Int, as type: T.Type ) { - _start().storeBytes(of: value, toByteOffset: offset, as: type) + unsafe _start().storeBytes(of: value, toByteOffset: offset, as: type) } } @@ -347,7 +350,9 @@ extension MutableRawSpan { var offset = byteOffset while offset + MemoryLayout.stride <= _count { guard let element = elements.next() else { break } - storeBytes(of: element, toUncheckedByteOffset: offset, as: Element.self) + unsafe storeBytes( + of: element, toUncheckedByteOffset: offset, as: Element.self + ) offset &+= MemoryLayout.stride } return offset @@ -403,8 +408,8 @@ extension MutableRawSpan { ) -> Int { if source.byteCount == 0 { return byteOffset } source.withUnsafeBytes { - _start().advanced(by: byteOffset) - .copyMemory(from: $0.baseAddress!, byteCount: $0.count) + unsafe _start().advanced(by: byteOffset) + .copyMemory(from: $0.baseAddress!, byteCount: $0.count) } return byteOffset &+ source.byteCount } @@ -443,7 +448,7 @@ extension MutableRawSpan { UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), "Index range out of bounds" ) - return _extracting(unchecked: bounds) + return unsafe _extracting(unchecked: bounds) } /// Constructs a new span over the items within the supplied range of @@ -465,9 +470,9 @@ extension MutableRawSpan { @_alwaysEmitIntoClient @lifetime(borrow self) mutating public func _extracting(unchecked bounds: Range) -> Self { - let newStart = _pointer?.advanced(by: bounds.lowerBound) + let newStart = unsafe _pointer?.advanced(by: bounds.lowerBound) let newSpan = Self(_unchecked: newStart, byteCount: bounds.count) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } /// Constructs a new span over the items within the supplied range of @@ -488,7 +493,7 @@ extension MutableRawSpan { mutating public func _extracting( _ bounds: some RangeExpression ) -> Self { - _extracting(bounds.relative(to: byteOffsets).clamped(to: byteOffsets)) + _extracting(bounds.relative(to: byteOffsets)) } /// Constructs a new span over the items within the supplied range of @@ -512,11 +517,10 @@ extension MutableRawSpan { mutating public func _extracting( unchecked bounds: some RangeExpression ) -> Self { - _extracting( - unchecked: bounds.relative(to: byteOffsets).clamped(to: byteOffsets) - ) + unsafe _extracting(unchecked: bounds.relative(to: byteOffsets)) } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow self) mutating public func _extracting( @@ -525,7 +529,7 @@ extension MutableRawSpan { let range = Range( _uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1) ) - return _extracting(unchecked: range) + return unsafe _extracting(unchecked: range) } /// Constructs a new span over all the items of this span. @@ -541,7 +545,7 @@ extension MutableRawSpan { @lifetime(borrow self) mutating public func _extracting(_: UnboundedRange) -> Self { let newSpan = Self(_unchecked: _start(), byteCount: _count) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } } @@ -570,7 +574,7 @@ extension MutableRawSpan { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, byteCount) let newSpan = Self(_unchecked: _pointer, byteCount: newCount) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } /// Returns a span over all but the given number of trailing elements. @@ -593,7 +597,7 @@ extension MutableRawSpan { _precondition(k >= 0, "Can't drop a negative number of elements") let dropped = min(k, byteCount) let newSpan = Self(_unchecked: _pointer, byteCount: byteCount &- dropped) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } /// Returns a span containing the final elements of the span, @@ -616,9 +620,9 @@ extension MutableRawSpan { mutating public func _extracting(last maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, byteCount) - let newStart = _pointer?.advanced(by: byteCount &- newCount) + let newStart = unsafe _pointer?.advanced(by: byteCount &- newCount) let newSpan = Self(_unchecked: newStart, byteCount: newCount) - return _overrideLifetime(newSpan, copying: self) + return unsafe _overrideLifetime(newSpan, copying: self) } /// Returns a span over all but the given number of initial elements. @@ -640,8 +644,8 @@ extension MutableRawSpan { mutating public func _extracting(droppingFirst k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of bytes") let dropped = min(k, byteCount) - let newStart = _pointer?.advanced(by: dropped) + let newStart = unsafe _pointer?.advanced(by: dropped) let newSpan = Self(_unchecked: newStart, byteCount: byteCount &- dropped) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } } diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index 92c1cc13b29b2..85be9b123b722 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -12,6 +12,7 @@ // A MutableSpan represents a span of memory which // contains initialized `Element` instances. +@safe @frozen @available(SwiftStdlib 6.2, *) public struct MutableSpan @@ -24,7 +25,7 @@ public struct MutableSpan @_alwaysEmitIntoClient internal func _start() -> UnsafeMutableRawPointer { - _pointer.unsafelyUnwrapped + unsafe _pointer.unsafelyUnwrapped } @_alwaysEmitIntoClient @@ -33,7 +34,7 @@ public struct MutableSpan _unchecked start: UnsafeMutableRawPointer?, count: Int ) { - _pointer = start + _pointer = unsafe start _count = count } } @@ -58,13 +59,13 @@ extension MutableSpan where Element: ~Copyable { public init( _unsafeElements buffer: UnsafeMutableBufferPointer ) { - _precondition( + unsafe _precondition( ((Int(bitPattern: buffer.baseAddress) & - (MemoryLayout.alignment&-1)) == 0), + (MemoryLayout.alignment &- 1)) == 0), "baseAddress must be properly aligned to access Element" ) let ms = MutableSpan(_unchecked: buffer) - self = _overrideLifetime(ms, borrowing: buffer) + self = unsafe _overrideLifetime(ms, borrowing: buffer) } @_alwaysEmitIntoClient @@ -74,9 +75,9 @@ extension MutableSpan where Element: ~Copyable { count: Int ) { _precondition(count >= 0, "Count must not be negative") - let buffer = UnsafeMutableBufferPointer(start: start, count: count) + let buffer = unsafe UnsafeMutableBufferPointer(start: start, count: count) let ms = MutableSpan(_unsafeElements: buffer) - self = _overrideLifetime(ms, borrowing: start) + self = unsafe _overrideLifetime(ms, borrowing: start) } } @@ -88,9 +89,9 @@ extension MutableSpan { public init( _unsafeElements elements: borrowing Slice> ) { - let rb = UnsafeMutableBufferPointer(rebasing: elements) + let rb = unsafe UnsafeMutableBufferPointer(rebasing: elements) let ms = MutableSpan(_unsafeElements: rb) - self = _overrideLifetime(ms, borrowing: elements) + self = unsafe _overrideLifetime(ms, borrowing: elements) } } @@ -102,20 +103,20 @@ extension MutableSpan where Element: BitwiseCopyable { public init( _unsafeBytes buffer: UnsafeMutableRawBufferPointer ) { - _precondition( + unsafe _precondition( ((Int(bitPattern: buffer.baseAddress) & - (MemoryLayout.alignment&-1)) == 0), + (MemoryLayout.alignment &- 1)) == 0), "baseAddress must be properly aligned to access Element" ) let (byteCount, stride) = (buffer.count, MemoryLayout.stride) let (count, remainder) = byteCount.quotientAndRemainder(dividingBy: stride) _precondition(remainder == 0, "Span must contain a whole number of elements") - let elements = UnsafeMutableBufferPointer( + let elements = unsafe UnsafeMutableBufferPointer( start: buffer.baseAddress?.assumingMemoryBound(to: Element.self), count: count ) let ms = MutableSpan(_unsafeElements: elements) - self = _overrideLifetime(ms, borrowing: buffer) + self = unsafe _overrideLifetime(ms, borrowing: buffer) } @_alwaysEmitIntoClient @@ -125,9 +126,11 @@ extension MutableSpan where Element: BitwiseCopyable { byteCount: Int ) { _precondition(byteCount >= 0, "Count must not be negative") - let bytes = UnsafeMutableRawBufferPointer(start: pointer, count: byteCount) + let bytes = unsafe UnsafeMutableRawBufferPointer( + start: pointer, count: byteCount + ) let ms = MutableSpan(_unsafeBytes: bytes) - self = _overrideLifetime(ms, borrowing: pointer) + self = unsafe _overrideLifetime(ms, borrowing: pointer) } @_alwaysEmitIntoClient @@ -135,9 +138,9 @@ extension MutableSpan where Element: BitwiseCopyable { public init( _unsafeBytes buffer: borrowing Slice ) { - let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer) + let bytes = unsafe UnsafeMutableRawBufferPointer(rebasing: buffer) let ms = MutableSpan(_unsafeBytes: bytes) - self = _overrideLifetime(ms, borrowing: buffer) + self = unsafe _overrideLifetime(ms, borrowing: buffer) } } @@ -147,10 +150,13 @@ extension Span where Element: ~Copyable { @_alwaysEmitIntoClient @lifetime(borrow mutableSpan) public init(_unsafeMutableSpan mutableSpan: borrowing MutableSpan) { - let pointer = mutableSpan._pointer?.assumingMemoryBound(to: Element.self) - let buffer = UnsafeBufferPointer(start: pointer, count: mutableSpan.count) + let pointer = + unsafe mutableSpan._pointer?.assumingMemoryBound(to: Element.self) + let buffer = unsafe UnsafeBufferPointer( + start: pointer, count: mutableSpan.count + ) let span = Span(_unsafeElements: buffer) - self = _overrideLifetime(span, borrowing: mutableSpan) + self = unsafe _overrideLifetime(span, borrowing: mutableSpan) } } @@ -175,42 +181,20 @@ extension RawSpan { ) { let pointer = mutableSpan._pointer let byteCount = mutableSpan.count &* MemoryLayout.stride - let buffer = UnsafeRawBufferPointer(start: pointer, count: byteCount) + let buffer = unsafe UnsafeRawBufferPointer(start: pointer, count: byteCount) let rawSpan = RawSpan(_unsafeBytes: buffer) - self = _overrideLifetime(rawSpan, borrowing: mutableSpan) + self = unsafe _overrideLifetime(rawSpan, borrowing: mutableSpan) } } -//@available(SwiftStdlib 6.2, *) -//extension MutableSpan where Element: Equatable { -// -// @_alwaysEmitIntoClient -// public func _elementsEqual(_ other: borrowing Self) -> Bool { -// _elementsEqual(Span(_unsafeMutableSpan: other)) -// } -// -// @_alwaysEmitIntoClient -// public func _elementsEqual(_ other: Span) -> Bool { -// Span(_unsafeMutableSpan: self)._elementsEqual(other) -// } -// -// @_alwaysEmitIntoClient -// public func _elementsEqual(_ other: some Collection) -> Bool { -// Span(_unsafeMutableSpan: self)._elementsEqual(other) -// } -// -// @_alwaysEmitIntoClient -// public func _elementsEqual(_ other: some Sequence) -> Bool { -// Span(_unsafeMutableSpan: self)._elementsEqual(other) -// } -//} -// @available(SwiftStdlib 6.2, *) extension MutableSpan where Element: ~Copyable { @_alwaysEmitIntoClient public var _description: String { - let addr = String(UInt(bitPattern: _pointer), radix: 16, uppercase: false) + let addr = String( + unsafe UInt(bitPattern: _pointer), radix: 16, uppercase: false + ) return "(0x\(addr), \(_count))" } } @@ -261,11 +245,11 @@ extension MutableSpan where Element: ~Copyable { public subscript(_ position: Index) -> Element { unsafeAddress { _precondition(indices.contains(position), "index out of bounds") - return UnsafePointer(_unsafeAddressOfElement(unchecked: position)) + return unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: position)) } unsafeMutableAddress { _precondition(indices.contains(position), "index out of bounds") - return _unsafeAddressOfElement(unchecked: position) + return unsafe _unsafeAddressOfElement(unchecked: position) } } @@ -277,13 +261,14 @@ extension MutableSpan where Element: ~Copyable { /// must be greater or equal to zero, and less than `count`. /// /// - Complexity: O(1) + @unsafe @_alwaysEmitIntoClient public subscript(unchecked position: Index) -> Element { unsafeAddress { - UnsafePointer(_unsafeAddressOfElement(unchecked: position)) + unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: position)) } unsafeMutableAddress { - _unsafeAddressOfElement(unchecked: position) + unsafe _unsafeAddressOfElement(unchecked: position) } } @@ -293,8 +278,8 @@ extension MutableSpan where Element: ~Copyable { unchecked position: Index ) -> UnsafeMutablePointer { let elementOffset = position &* MemoryLayout.stride - let address = _start().advanced(by: elementOffset) - return address.assumingMemoryBound(to: Element.self) + let address = unsafe _start().advanced(by: elementOffset) + return unsafe address.assumingMemoryBound(to: Element.self) } } @@ -305,16 +290,17 @@ extension MutableSpan where Element: ~Copyable { public mutating func swapAt(_ i: Index, _ j: Index) { _precondition(indices.contains(Index(i))) _precondition(indices.contains(Index(j))) - swapAt(unchecked: i, unchecked: j) + unsafe swapAt(unchecked: i, unchecked: j) } + @unsafe @_alwaysEmitIntoClient public mutating func swapAt(unchecked i: Index, unchecked j: Index) { - let pi = _unsafeAddressOfElement(unchecked: i) - let pj = _unsafeAddressOfElement(unchecked: j) - let temporary = pi.move() - pi.initialize(to: pj.move()) - pj.initialize(to: consume temporary) + let pi = unsafe _unsafeAddressOfElement(unchecked: i) + let pj = unsafe _unsafeAddressOfElement(unchecked: j) + let temporary = unsafe pi.move() + unsafe pi.initialize(to: pj.move()) + unsafe pj.initialize(to: consume temporary) } } @@ -331,11 +317,11 @@ extension MutableSpan where Element: BitwiseCopyable { public subscript(_ position: Index) -> Element { get { _precondition(indices.contains(position), "index out of bounds") - return self[unchecked: position] + return unsafe self[unchecked: position] } set { _precondition(indices.contains(position), "index out of bounds") - self[unchecked: position] = newValue + unsafe self[unchecked: position] = newValue } } @@ -347,15 +333,20 @@ extension MutableSpan where Element: BitwiseCopyable { /// must be greater or equal to zero, and less than `count`. /// /// - Complexity: O(1) + @unsafe @_alwaysEmitIntoClient public subscript(unchecked position: Index) -> Element { get { let offset = position&*MemoryLayout.stride - return _start().loadUnaligned(fromByteOffset: offset, as: Element.self) + return unsafe _start().loadUnaligned( + fromByteOffset: offset, as: Element.self + ) } set { let offset = position&*MemoryLayout.stride - _start().storeBytes(of: newValue, toByteOffset: offset, as: Element.self) + unsafe _start().storeBytes( + of: newValue, toByteOffset: offset, as: Element.self + ) } } } @@ -373,18 +364,20 @@ extension MutableSpan where Element: ~Copyable { //FIXME: mark closure parameter as non-escaping @_alwaysEmitIntoClient - public mutating func withUnsafeMutableBufferPointer( + public mutating func withUnsafeMutableBufferPointer< + E: Error, Result: ~Copyable + >( _ body: (UnsafeMutableBufferPointer) throws(E) -> Result ) throws(E) -> Result { guard let pointer = _pointer, count > 0 else { - return try body(.init(start: nil, count: 0)) + return try unsafe body(.init(start: nil, count: 0)) } // bind memory by hand to sidestep alignment concerns let binding = Builtin.bindMemory( pointer._rawValue, count._builtinWordValue, Element.self ) defer { Builtin.rebindMemory(pointer._rawValue, binding) } - return try body(.init(start: .init(pointer._rawValue), count: count)) + return try unsafe body(.init(start: .init(pointer._rawValue), count: count)) } } @@ -404,11 +397,11 @@ extension MutableSpan where Element: BitwiseCopyable { public mutating func withUnsafeMutableBytes( _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result ) throws(E) -> Result { - let bytes = UnsafeMutableRawBufferPointer( + let bytes = unsafe UnsafeMutableRawBufferPointer( start: (_count == 0) ? nil : _start(), count: _count &* MemoryLayout.stride ) - return try body(bytes) + return try unsafe body(bytes) } } @@ -418,8 +411,8 @@ extension MutableSpan { @_alwaysEmitIntoClient public mutating func update(repeating repeatedValue: consuming Element) { - _start().withMemoryRebound(to: Element.self, capacity: count) { - $0.update(repeating: repeatedValue, count: count) + unsafe _start().withMemoryRebound(to: Element.self, capacity: count) { + unsafe $0.update(repeating: repeatedValue, count: count) } } @@ -439,7 +432,7 @@ extension MutableSpan { var index = 0 while index < _count { guard let element = elements.next() else { break } - self[unchecked: index] = element + unsafe self[unchecked: index] = element index &+= 1 } return index @@ -474,9 +467,11 @@ extension MutableSpan { source.count <= self.count, "destination span cannot contain every element from source." ) - _start().withMemoryRebound(to: Element.self, capacity: source.count) { dest in + unsafe _start().withMemoryRebound( + to: Element.self, capacity: source.count + ) { dest in source.withUnsafeBufferPointer { - dest.update(from: $0.baseAddress!, count: $0.count) + unsafe dest.update(from: $0.baseAddress!, count: $0.count) } } return source.count @@ -517,7 +512,7 @@ extension MutableSpan where Element: ~Copyable { // let source = OutputSpan(_initializing: source, initialized: source.count) // return self.moveUpdate(fromContentsOf: source) withUnsafeMutableBufferPointer { - $0.moveUpdate(fromContentsOf: source) + unsafe $0.moveUpdate(fromContentsOf: source) } } } @@ -529,7 +524,9 @@ extension MutableSpan { public mutating func moveUpdate( fromContentsOf source: Slice> ) -> Index { - moveUpdate(fromContentsOf: UnsafeMutableBufferPointer(rebasing: source)) + unsafe moveUpdate( + fromContentsOf: UnsafeMutableBufferPointer(rebasing: source) + ) } } @@ -544,7 +541,8 @@ extension MutableSpan where Element: BitwiseCopyable { // rebind _start manually in order to avoid assumptions about alignment. let rp = _start()._rawValue let binding = Builtin.bindMemory(rp, count._builtinWordValue, Element.self) - UnsafeMutablePointer(rp).update(repeating: repeatedValue, count: count) + let rebound = unsafe UnsafeMutablePointer(rp) + unsafe rebound.update(repeating: repeatedValue, count: count) Builtin.rebindMemory(rp, binding) } @@ -565,7 +563,7 @@ extension MutableSpan where Element: BitwiseCopyable { var index = 0 while index < _count { guard let element = elements.next() else { break } - self[unchecked: index] = element + unsafe self[unchecked: index] = element index &+= 1 } return index @@ -603,8 +601,9 @@ extension MutableSpan where Element: BitwiseCopyable { "destination span cannot contain every element from source." ) source.withUnsafeBufferPointer { - _start().copyMemory( - from: $0.baseAddress!, byteCount: $0.count&*MemoryLayout.stride + unsafe _start().copyMemory( + from: $0.baseAddress!, + byteCount: $0.count &* MemoryLayout.stride ) } return source.count @@ -643,7 +642,7 @@ extension MutableSpan where Element: ~Copyable { UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), "Index range out of bounds" ) - return _extracting(unchecked: bounds) + return unsafe _extracting(unchecked: bounds) } /// Constructs a new span over the items within the supplied range of @@ -666,9 +665,9 @@ extension MutableSpan where Element: ~Copyable { @lifetime(borrow self) mutating public func _extracting(unchecked bounds: Range) -> Self { let delta = bounds.lowerBound &* MemoryLayout.stride - let newStart = _pointer?.advanced(by: delta) + let newStart = unsafe _pointer?.advanced(by: delta) let newSpan = Self(_unchecked: newStart, count: bounds.count) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } /// Constructs a new span over the items within the supplied range of @@ -713,7 +712,7 @@ extension MutableSpan where Element: ~Copyable { mutating public func _extracting( unchecked bounds: some RangeExpression ) -> Self { - _extracting(unchecked: bounds.relative(to: indices)) + unsafe _extracting(unchecked: bounds.relative(to: indices)) } @_alwaysEmitIntoClient @@ -724,7 +723,7 @@ extension MutableSpan where Element: ~Copyable { let range = Range( _uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1) ) - return _extracting(unchecked: range) + return unsafe _extracting(unchecked: range) } /// Constructs a new span over all the items of this span. @@ -740,7 +739,7 @@ extension MutableSpan where Element: ~Copyable { @lifetime(borrow self) mutating public func _extracting(_: UnboundedRange) -> Self { let newSpan = Self(_unchecked: _start(), count: _count) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } } @@ -769,7 +768,7 @@ extension MutableSpan where Element: ~Copyable { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, count) let newSpan = Self(_unchecked: _pointer, count: newCount) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } /// Returns a span over all but the given number of trailing elements. @@ -792,7 +791,7 @@ extension MutableSpan where Element: ~Copyable { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) let newSpan = Self(_unchecked: _pointer, count: count &- droppedCount) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } /// Returns a span containing the final elements of the span, @@ -816,9 +815,9 @@ extension MutableSpan where Element: ~Copyable { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, count) let offset = (count &- newCount) * MemoryLayout.stride - let newStart = _pointer?.advanced(by: offset) + let newStart = unsafe _pointer?.advanced(by: offset) let newSpan = Self(_unchecked: newStart, count: newCount) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } /// Returns a span over all but the given number of initial elements. @@ -841,8 +840,8 @@ extension MutableSpan where Element: ~Copyable { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) let offset = droppedCount * MemoryLayout.stride - let newStart = _pointer?.advanced(by: offset) + let newStart = unsafe _pointer?.advanced(by: offset) let newSpan = Self(_unchecked: newStart, count: count &- droppedCount) - return _overrideLifetime(newSpan, mutating: &self) + return unsafe _overrideLifetime(newSpan, mutating: &self) } } From b39ee18f31f2f3b5ffe07e7b24fa4f5606ca7474 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 5 Mar 2025 09:58:35 -0800 Subject: [PATCH 08/23] [stdlib] tweaks to MutableSpan --- stdlib/public/core/Span/MutableSpan.swift | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index 85be9b123b722..c11e4e0c97d62 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -497,9 +497,9 @@ extension MutableSpan where Element: ~Copyable { // source.count <= self.count, // "destination span cannot contain every element from source." // ) -// let buffer = source.relinquishBorrowedMemory() +// let buffer = unsafe source.relinquishBorrowedMemory() // // we must now deinitialize the returned UMBP -// _start().moveInitializeMemory( +// unsafe _start().moveInitializeMemory( // as: Element.self, from: buffer.baseAddress!, count: buffer.count // ) // return buffer.count @@ -524,9 +524,7 @@ extension MutableSpan { public mutating func moveUpdate( fromContentsOf source: Slice> ) -> Index { - unsafe moveUpdate( - fromContentsOf: UnsafeMutableBufferPointer(rebasing: source) - ) + moveUpdate(fromContentsOf: unsafe .init(rebasing: source)) } } @@ -707,14 +705,6 @@ extension MutableSpan where Element: ~Copyable { /// /// - Complexity: O(1) @unsafe - @_alwaysEmitIntoClient - @lifetime(borrow self) - mutating public func _extracting( - unchecked bounds: some RangeExpression - ) -> Self { - unsafe _extracting(unchecked: bounds.relative(to: indices)) - } - @_alwaysEmitIntoClient @lifetime(borrow self) mutating public func _extracting( From 4a78667db8799083a6eccc404a7d49dae42b4d3f Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 5 Mar 2025 11:26:37 -0800 Subject: [PATCH 09/23] [stdlib] tweaks to MutableRawSpan --- stdlib/public/core/Span/MutableRawSpan.swift | 59 +++++++------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index e0bafa10eac8e..3d25fad6b3892 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -308,7 +308,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient - public func storeBytes( + public mutating func storeBytes( of value: T, toByteOffset offset: Int = 0, as type: T.Type ) { _precondition( @@ -321,7 +321,7 @@ extension MutableRawSpan { @unsafe @_alwaysEmitIntoClient - public func storeBytes( + public mutating func storeBytes( of value: T, toUncheckedByteOffset offset: Int, as type: T.Type ) { unsafe _start().storeBytes(of: value, toByteOffset: offset, as: type) @@ -334,20 +334,18 @@ extension MutableRawSpan { @_alwaysEmitIntoClient public mutating func update( - startingAt byteOffset: Int = 0, from source: S ) -> (unwritten: S.Iterator, byteOffset: Int) where S.Element: BitwiseCopyable { var iterator = source.makeIterator() - let offset = update(startingAt: byteOffset, from: &iterator) + let offset = update(from: &iterator) return (iterator, offset) } @_alwaysEmitIntoClient public mutating func update( - startingAt byteOffset: Int = 0, from elements: inout some IteratorProtocol ) -> Int { - var offset = byteOffset + var offset = 0 while offset + MemoryLayout.stride <= _count { guard let element = elements.next() else { break } unsafe storeBytes( @@ -360,18 +358,15 @@ extension MutableRawSpan { @_alwaysEmitIntoClient public mutating func update( - startingAt byteOffset: Int = 0, fromContentsOf source: C ) -> Int where C.Element: BitwiseCopyable { let newOffset = source.withContiguousStorageIfAvailable { - self.update( - startingAt: byteOffset, fromContentsOf: Span(_unsafeElements: $0) - ) + self.update(fromContentsOf: RawSpan(_unsafeElements: $0)) } if let newOffset { return newOffset } var elements = source.makeIterator() - let lastOffset = update(startingAt: byteOffset, from: &elements) + let lastOffset = update(from: &elements) _precondition( elements.next() == nil, "destination span cannot contain every element from source." @@ -381,45 +376,40 @@ extension MutableRawSpan { @_alwaysEmitIntoClient public mutating func update( - startingAt byteOffset: Int = 0, fromContentsOf source: Span ) -> Int { -// update(startingAt: byteOffset, from: source.bytes) +// update(from: source.bytes) source.withUnsafeBytes { - update(startingAt: byteOffset, fromContentsOf: $0) + update(fromContentsOf: $0) } } @_alwaysEmitIntoClient public mutating func update( - startingAt byteOffset: Int = 0, fromContentsOf source: borrowing MutableSpan ) -> Int { -// update(startingAt: byteOffset, from: source.storage.bytes) +// update(from: source.span.bytes) source.withUnsafeBytes { - update(startingAt: byteOffset, fromContentsOf: $0) + update(fromContentsOf: $0) } } @_alwaysEmitIntoClient public mutating func update( - startingAt byteOffset: Int = 0, - from source: RawSpan + fromContentsOf source: RawSpan ) -> Int { - if source.byteCount == 0 { return byteOffset } + if source.byteCount == 0 { return 0 } source.withUnsafeBytes { - unsafe _start().advanced(by: byteOffset) - .copyMemory(from: $0.baseAddress!, byteCount: $0.count) + unsafe _start().copyMemory(from: $0.baseAddress!, byteCount: $0.count) } - return byteOffset &+ source.byteCount + return source.byteCount } @_alwaysEmitIntoClient public mutating func update( - startingAt byteOffset: Int = 0, - from source: borrowing MutableRawSpan + fromContentsOf source: borrowing MutableRawSpan ) -> Int { - update(startingAt: byteOffset, from: source.bytes) + update(fromContentsOf: source.bytes) } } @@ -514,20 +504,9 @@ extension MutableRawSpan { @unsafe @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting( - unchecked bounds: some RangeExpression - ) -> Self { - unsafe _extracting(unchecked: bounds.relative(to: byteOffsets)) - } - - @unsafe - @_alwaysEmitIntoClient - @lifetime(borrow self) - mutating public func _extracting( - unchecked bounds: ClosedRange - ) -> Self { - let range = Range( - _uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1) + mutating public func _extracting(unchecked bounds: ClosedRange) -> Self { + let range = unsafe Range( + uncheckedBounds: (bounds.lowerBound, bounds.upperBound+1) ) return unsafe _extracting(unchecked: range) } From e470949083befbfbdb5b7525f189bccad5d4a2b2 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 5 Mar 2025 11:43:35 -0800 Subject: [PATCH 10/23] [test] MutableRawSpan tests --- test/stdlib/Span/MutableRawSpanTests.swift | 484 +++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 test/stdlib/Span/MutableRawSpanTests.swift diff --git a/test/stdlib/Span/MutableRawSpanTests.swift b/test/stdlib/Span/MutableRawSpanTests.swift new file mode 100644 index 0000000000000..67aceec933a14 --- /dev/null +++ b/test/stdlib/Span/MutableRawSpanTests.swift @@ -0,0 +1,484 @@ +//===--- MutableRawSpanTests.swift ----------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// RUN: %target-run-stdlib-swift + +// REQUIRES: executable_test + +import StdlibUnittest + +var suite = TestSuite("MutableRawSpan Tests") +defer { runAllTests() } + +suite.test("Basic Initializer") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + var s = Array("\(#file)+\(#function)--\(Int.random(in: 1000...9999))".utf8) + s.withUnsafeMutableBytes { + let b = MutableRawSpan(_unsafeBytes: $0) + expectEqual(b.byteCount, $0.count) + expectFalse(b.isEmpty) + expectEqual(b.byteOffsets, 0..<$0.count) + } +} + +suite.test("isEmpty property") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + var array = [0, 1, 2] + array.withUnsafeMutableBufferPointer { + var span = MutableRawSpan(_unsafeElements: $0) + expectFalse(span.isEmpty) + + let e = $0.extracting(0..<0) + span = MutableRawSpan(_unsafeElements: e) + expectTrue(span.isEmpty) + } +} + +suite.test("indices property") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let capacity = 4 as UInt8 + var a = Array(0...stride + + let s0 = span.unsafeLoad(as: String.self) + expectEqual(s0.contains("0"), true) + let s1 = span.unsafeLoad(fromByteOffset: stride, as: String.self) + expectEqual(s1.contains("1"), true) + let s2 = span.unsafeLoad(fromUncheckedByteOffset: 2*stride, as: String.self) + expectEqual(s2.contains("2"), true) + } +} + +suite.test("unsafeLoadUnaligned(as:)") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let capacity = 64 + var a = Array(0...stride/2, as: UInt.self + ) + } + expectEqual(a[0].littleEndian & 0xffff, 0xff) + expectEqual(a[0].bigEndian & 0xffff, 0xffff) +} + +suite.test("update(from: some Sequence)") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let capacity = 8 + var a = Array(repeating: Int.max, count: capacity) + expectEqual(a.allSatisfy({ $0 == .max }), true) + a.withUnsafeMutableBufferPointer { + let empty = UnsafeMutableBufferPointer(start: nil, count: 0) + var span = MutableRawSpan(_unsafeElements: empty) + var (iterator, updated) = span.update(from: 0..<0) + expectNil(iterator.next()) + expectEqual(updated, 0) + + span = MutableRawSpan(_unsafeElements: $0) + (iterator, updated) = span.update(from: 0..<0) + expectNil(iterator.next()) + expectEqual(updated, 0) + + (iterator, updated) = span.update(from: 0..<10000) + expectNotNil(iterator.next()) + expectEqual(updated, capacity*MemoryLayout.stride) + } + expectEqual(a.elementsEqual(0..)") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let capacity = 8 + var a = Array(repeating: Int.max, count: capacity) + let e = Array(EmptyCollection()) + expectEqual(a.allSatisfy({ $0 == .max }), true) + a.withUnsafeMutableBytes { + let emptyPrefix = $0.prefix(0) + var span = MutableRawSpan(_unsafeBytes: emptyPrefix) + var updated = span.update(fromContentsOf: e) + expectEqual(updated, 0) + + + updated = span.update(fromContentsOf: AnyCollection(e)) + expectEqual(updated, 0) + + span = MutableRawSpan(_unsafeBytes: $0) + updated = span.update(fromContentsOf: 0...stride) + } + expectEqual(a.elementsEqual(0...stride) + } + expectEqual(a.elementsEqual(0...stride) + } + } + expectEqual(a.allSatisfy({ $0 == Int.min }), true) + + a.withUnsafeMutableBytes { + var span = MutableRawSpan(_unsafeBytes: $0) + let array = Array(0...stride) + } + } + expectEqual(a.elementsEqual(0.. Date: Wed, 5 Mar 2025 11:48:10 -0800 Subject: [PATCH 11/23] [test] add MutableSpan slicing tests --- test/stdlib/Span/MutableSpanTests.swift | 127 ++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/test/stdlib/Span/MutableSpanTests.swift b/test/stdlib/Span/MutableSpanTests.swift index 5a18db2d90850..4fc7ed00f3909 100644 --- a/test/stdlib/Span/MutableSpanTests.swift +++ b/test/stdlib/Span/MutableSpanTests.swift @@ -572,3 +572,130 @@ suite.test("swapAt") expectEqual(array, (0.. + var span = MutableSpan(_unsafeElements: $0) + expectEqual(span.count, capacity) + + prefix = span._extracting(first: 1) + expectEqual(prefix[0], 0) + + prefix = span._extracting(first: capacity) + expectEqual(prefix[capacity-1], UInt8(capacity-1)) + + prefix = span._extracting(droppingLast: capacity) + expectEqual(prefix.isEmpty, true) + + prefix = span._extracting(droppingLast: 1) + expectEqual(prefix[capacity-2], UInt8(capacity-2)) + } + + do { + let b = UnsafeMutableBufferPointer(start: nil, count: 0) + var span = MutableSpan(_unsafeElements: b) + expectEqual(span.count, b.count) + expectEqual(span._extracting(first: 1).count, b.count) + expectEqual(span._extracting(droppingLast: 1).count, b.count) + } +} + +suite.test("_extracting suffixes") +.skip(.custom( + { if #available(SwiftStdlib 6.2, *) { false } else { true } }, + reason: "Requires Swift 6.2's standard library" +)) +.code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let capacity = 4 + var a = Array(0.. + var span = MutableSpan(_unsafeElements: $0) + expectEqual(span.count, capacity) + + suffix = span._extracting(last: capacity) + expectEqual(suffix[0], 0) + + suffix = span._extracting(last: capacity-1) + expectEqual(suffix[0], 1) + + suffix = span._extracting(last: 1) + expectEqual(suffix[0], UInt8(capacity-1)) + + suffix = span._extracting(droppingFirst: capacity) + expectTrue(suffix.isEmpty) + + suffix = span._extracting(droppingFirst: 1) + expectEqual(suffix[0], 1) + } + + do { + let b = UnsafeMutableBufferPointer(start: nil, count: 0) + var span = MutableSpan(_unsafeElements: b) + expectEqual(span.count, b.count) + expectEqual(span._extracting(last: 1).count, b.count) + expectEqual(span._extracting(droppingFirst: 1).count, b.count) + } +} From 9553982253350ee181b70ed7f94880ac84e4abbd Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 5 Mar 2025 13:40:45 -0800 Subject: [PATCH 12/23] [stdlib] use the internal unsafe-unwrap --- stdlib/public/core/Span/MutableRawSpan.swift | 2 +- stdlib/public/core/Span/MutableSpan.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index 3d25fad6b3892..cdcea26cad29e 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -24,7 +24,7 @@ public struct MutableRawSpan: ~Copyable & ~Escapable { @_alwaysEmitIntoClient internal func _start() -> UnsafeMutableRawPointer { - unsafe _pointer.unsafelyUnwrapped + unsafe _pointer._unsafelyUnwrappedUnchecked } @_alwaysEmitIntoClient diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index c11e4e0c97d62..77ac9fd99caef 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -25,7 +25,7 @@ public struct MutableSpan @_alwaysEmitIntoClient internal func _start() -> UnsafeMutableRawPointer { - unsafe _pointer.unsafelyUnwrapped + unsafe _pointer._unsafelyUnwrappedUnchecked } @_alwaysEmitIntoClient From 5a087d1190e780b2635383ffd34887fcaca46e0e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 5 Mar 2025 17:52:52 -0800 Subject: [PATCH 13/23] [stdlib] annotate unsafe initializers --- stdlib/public/core/Span/MutableRawSpan.swift | 61 +++++++++++-------- stdlib/public/core/Span/MutableSpan.swift | 63 ++++++++++++-------- test/stdlib/Span/MutableSpanTests.swift | 2 +- 3 files changed, 74 insertions(+), 52 deletions(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index cdcea26cad29e..651df9761f44c 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -27,6 +27,8 @@ public struct MutableRawSpan: ~Copyable & ~Escapable { unsafe _pointer._unsafelyUnwrappedUnchecked } + @unsafe + @_unsafeNonescapableResult @_alwaysEmitIntoClient @lifetime(borrow pointer) internal init( @@ -44,26 +46,29 @@ extension MutableRawSpan: @unchecked Sendable {} @available(SwiftStdlib 6.2, *) extension MutableRawSpan { + @unsafe @_alwaysEmitIntoClient @lifetime(borrow bytes) public init( _unsafeBytes bytes: UnsafeMutableRawBufferPointer ) { - let baseAddress = bytes.baseAddress - let span = MutableRawSpan(_unchecked: baseAddress, byteCount: bytes.count) + let (baseAddress, count) = (bytes.baseAddress, bytes.count) + let span = unsafe MutableRawSpan(_unchecked: baseAddress, byteCount: count) self = unsafe _overrideLifetime(span, borrowing: bytes) } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow bytes) public init( _unsafeBytes bytes: borrowing Slice ) { let rebased = unsafe UnsafeMutableRawBufferPointer(rebasing: bytes) - let span = MutableRawSpan(_unsafeBytes: rebased) + let span = unsafe MutableRawSpan(_unsafeBytes: rebased) self = unsafe _overrideLifetime(span, borrowing: bytes) } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow pointer) public init( @@ -71,26 +76,28 @@ extension MutableRawSpan { byteCount: Int ) { _precondition(byteCount >= 0, "Count must not be negative") - self.init(_unchecked: pointer, byteCount: byteCount) + unsafe self.init(_unchecked: pointer, byteCount: byteCount) } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow elements) public init( _unsafeElements elements: UnsafeMutableBufferPointer ) { let bytes = UnsafeMutableRawBufferPointer(elements) - let span = MutableRawSpan(_unsafeBytes: bytes) + let span = unsafe MutableRawSpan(_unsafeBytes: bytes) self = unsafe _overrideLifetime(span, borrowing: elements) } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow elements) public init( _unsafeElements elements: borrowing Slice> ) { let rebased = unsafe UnsafeMutableBufferPointer(rebasing: elements) - let span = MutableRawSpan(_unsafeElements: rebased) + let span = unsafe MutableRawSpan(_unsafeElements: rebased) self = unsafe _overrideLifetime(span, borrowing: elements) } @@ -103,7 +110,7 @@ extension MutableRawSpan { start: elements._pointer, count: elements.count &* MemoryLayout.stride ) - let span = MutableRawSpan(_unsafeBytes: bytes) + let span = unsafe MutableRawSpan(_unsafeBytes: bytes) self = unsafe _overrideLifetime(span, copying: elements) } } @@ -150,11 +157,11 @@ extension MutableRawSpan { extension RawSpan { @_alwaysEmitIntoClient - @lifetime(borrow mutableSpan) - public init(_unsafeMutableRawSpan mutableSpan: borrowing MutableRawSpan) { - let start = mutableSpan._start() - let span = RawSpan(_unsafeStart: start, byteCount: mutableSpan.byteCount) - self = unsafe _overrideLifetime(span, borrowing: mutableSpan) + @lifetime(borrow mutableRawSpan) + public init(_mutableRawSpan mutableRawSpan: borrowing MutableRawSpan) { + let (start, count) = (mutableRawSpan._start(), mutableRawSpan._count) + let span = unsafe RawSpan(_unsafeStart: start, byteCount: count) + self = unsafe _overrideLifetime(span, borrowing: mutableRawSpan) } } @@ -165,7 +172,7 @@ extension MutableRawSpan { @_alwaysEmitIntoClient @lifetime(borrow self) borrowing get { - return RawSpan(_unsafeMutableRawSpan: self) + return RawSpan(_mutableRawSpan: self) } } @@ -176,7 +183,7 @@ extension MutableRawSpan { as type: T.Type ) -> Span { let bytes = unsafe UnsafeRawBufferPointer(start: _pointer, count: _count) - let span = Span(_unsafeBytes: bytes) + let span = unsafe Span(_unsafeBytes: bytes) return unsafe _overrideLifetime(span, borrowing: self) } @@ -189,7 +196,7 @@ extension MutableRawSpan { let bytes = unsafe UnsafeMutableRawBufferPointer( start: _pointer, count: _count ) - let span = MutableSpan(_unsafeBytes: bytes) + let span = unsafe MutableSpan(_unsafeBytes: bytes) return unsafe _overrideLifetime(span, mutating: &self) } } @@ -361,7 +368,7 @@ extension MutableRawSpan { fromContentsOf source: C ) -> Int where C.Element: BitwiseCopyable { let newOffset = source.withContiguousStorageIfAvailable { - self.update(fromContentsOf: RawSpan(_unsafeElements: $0)) + self.update(fromContentsOf: unsafe RawSpan(_unsafeElements: $0)) } if let newOffset { return newOffset } @@ -461,7 +468,7 @@ extension MutableRawSpan { @lifetime(borrow self) mutating public func _extracting(unchecked bounds: Range) -> Self { let newStart = unsafe _pointer?.advanced(by: bounds.lowerBound) - let newSpan = Self(_unchecked: newStart, byteCount: bounds.count) + let newSpan = unsafe Self(_unchecked: newStart, byteCount: bounds.count) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -506,7 +513,7 @@ extension MutableRawSpan { @lifetime(borrow self) mutating public func _extracting(unchecked bounds: ClosedRange) -> Self { let range = unsafe Range( - uncheckedBounds: (bounds.lowerBound, bounds.upperBound+1) + _uncheckedBounds: (bounds.lowerBound, bounds.upperBound+1) ) return unsafe _extracting(unchecked: range) } @@ -523,7 +530,7 @@ extension MutableRawSpan { @_alwaysEmitIntoClient @lifetime(borrow self) mutating public func _extracting(_: UnboundedRange) -> Self { - let newSpan = Self(_unchecked: _start(), byteCount: _count) + let newSpan = unsafe Self(_unchecked: _start(), byteCount: _count) return unsafe _overrideLifetime(newSpan, mutating: &self) } } @@ -552,7 +559,7 @@ extension MutableRawSpan { mutating public func _extracting(first maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, byteCount) - let newSpan = Self(_unchecked: _pointer, byteCount: newCount) + let newSpan = unsafe Self(_unchecked: _pointer, byteCount: newCount) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -574,8 +581,9 @@ extension MutableRawSpan { @lifetime(borrow self) mutating public func _extracting(droppingLast k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") - let dropped = min(k, byteCount) - let newSpan = Self(_unchecked: _pointer, byteCount: byteCount &- dropped) + let droppedCount = min(k, byteCount) + let newCount = byteCount &- droppedCount + let newSpan = unsafe Self(_unchecked: _pointer, byteCount: newCount) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -600,7 +608,7 @@ extension MutableRawSpan { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, byteCount) let newStart = unsafe _pointer?.advanced(by: byteCount &- newCount) - let newSpan = Self(_unchecked: newStart, byteCount: newCount) + let newSpan = unsafe Self(_unchecked: newStart, byteCount: newCount) return unsafe _overrideLifetime(newSpan, copying: self) } @@ -622,9 +630,10 @@ extension MutableRawSpan { @lifetime(borrow self) mutating public func _extracting(droppingFirst k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of bytes") - let dropped = min(k, byteCount) - let newStart = unsafe _pointer?.advanced(by: dropped) - let newSpan = Self(_unchecked: newStart, byteCount: byteCount &- dropped) + let droppedCount = min(k, byteCount) + let newStart = unsafe _pointer?.advanced(by: droppedCount) + let newCount = byteCount &- droppedCount + let newSpan = unsafe Self(_unchecked: newStart, byteCount: newCount) return unsafe _overrideLifetime(newSpan, mutating: &self) } } diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index 77ac9fd99caef..688ce4528cdca 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -28,6 +28,8 @@ public struct MutableSpan unsafe _pointer._unsafelyUnwrappedUnchecked } + @unsafe + @_unsafeNonescapableResult @_alwaysEmitIntoClient @lifetime(borrow start) internal init( @@ -45,6 +47,8 @@ extension MutableSpan: @unchecked Sendable where Element: Sendable {} @available(SwiftStdlib 6.2, *) extension MutableSpan where Element: ~Copyable { + @unsafe + @_unsafeNonescapableResult @usableFromInline @lifetime(borrow elements) internal init( @@ -54,20 +58,22 @@ extension MutableSpan where Element: ~Copyable { _count = elements.count } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow buffer) public init( _unsafeElements buffer: UnsafeMutableBufferPointer ) { - unsafe _precondition( + _precondition( ((Int(bitPattern: buffer.baseAddress) & (MemoryLayout.alignment &- 1)) == 0), "baseAddress must be properly aligned to access Element" ) - let ms = MutableSpan(_unchecked: buffer) + let ms = unsafe MutableSpan(_unchecked: buffer) self = unsafe _overrideLifetime(ms, borrowing: buffer) } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow start) public init( @@ -76,7 +82,7 @@ extension MutableSpan where Element: ~Copyable { ) { _precondition(count >= 0, "Count must not be negative") let buffer = unsafe UnsafeMutableBufferPointer(start: start, count: count) - let ms = MutableSpan(_unsafeElements: buffer) + let ms = unsafe MutableSpan(_unsafeElements: buffer) self = unsafe _overrideLifetime(ms, borrowing: start) } } @@ -84,13 +90,14 @@ extension MutableSpan where Element: ~Copyable { @available(SwiftStdlib 6.2, *) extension MutableSpan { + @unsafe @_alwaysEmitIntoClient @lifetime(borrow elements) public init( _unsafeElements elements: borrowing Slice> ) { let rb = unsafe UnsafeMutableBufferPointer(rebasing: elements) - let ms = MutableSpan(_unsafeElements: rb) + let ms = unsafe MutableSpan(_unsafeElements: rb) self = unsafe _overrideLifetime(ms, borrowing: elements) } } @@ -98,12 +105,13 @@ extension MutableSpan { @available(SwiftStdlib 6.2, *) extension MutableSpan where Element: BitwiseCopyable { + @unsafe @_alwaysEmitIntoClient @lifetime(borrow buffer) public init( _unsafeBytes buffer: UnsafeMutableRawBufferPointer ) { - unsafe _precondition( + _precondition( ((Int(bitPattern: buffer.baseAddress) & (MemoryLayout.alignment &- 1)) == 0), "baseAddress must be properly aligned to access Element" @@ -115,10 +123,11 @@ extension MutableSpan where Element: BitwiseCopyable { start: buffer.baseAddress?.assumingMemoryBound(to: Element.self), count: count ) - let ms = MutableSpan(_unsafeElements: elements) + let ms = unsafe MutableSpan(_unsafeElements: elements) self = unsafe _overrideLifetime(ms, borrowing: buffer) } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow pointer) public init( @@ -129,17 +138,18 @@ extension MutableSpan where Element: BitwiseCopyable { let bytes = unsafe UnsafeMutableRawBufferPointer( start: pointer, count: byteCount ) - let ms = MutableSpan(_unsafeBytes: bytes) + let ms = unsafe MutableSpan(_unsafeBytes: bytes) self = unsafe _overrideLifetime(ms, borrowing: pointer) } + @unsafe @_alwaysEmitIntoClient @lifetime(borrow buffer) public init( _unsafeBytes buffer: borrowing Slice ) { let bytes = unsafe UnsafeMutableRawBufferPointer(rebasing: buffer) - let ms = MutableSpan(_unsafeBytes: bytes) + let ms = unsafe MutableSpan(_unsafeBytes: bytes) self = unsafe _overrideLifetime(ms, borrowing: buffer) } } @@ -149,13 +159,13 @@ extension Span where Element: ~Copyable { @_alwaysEmitIntoClient @lifetime(borrow mutableSpan) - public init(_unsafeMutableSpan mutableSpan: borrowing MutableSpan) { + public init(_mutableSpan mutableSpan: borrowing MutableSpan) { let pointer = unsafe mutableSpan._pointer?.assumingMemoryBound(to: Element.self) let buffer = unsafe UnsafeBufferPointer( start: pointer, count: mutableSpan.count ) - let span = Span(_unsafeElements: buffer) + let span = unsafe Span(_unsafeElements: buffer) self = unsafe _overrideLifetime(span, borrowing: mutableSpan) } } @@ -167,7 +177,7 @@ extension MutableSpan where Element: ~Copyable { public var span: Span { @lifetime(borrow self) borrowing get { - Span(_unsafeMutableSpan: self) + Span(_mutableSpan: self) } } } @@ -176,13 +186,14 @@ extension MutableSpan where Element: ~Copyable { extension RawSpan { @_alwaysEmitIntoClient + @lifetime(borrow mutableSpan) public init( - _unsafeMutableSpan mutableSpan: borrowing MutableSpan + _mutableSpan mutableSpan: borrowing MutableSpan ) { let pointer = mutableSpan._pointer let byteCount = mutableSpan.count &* MemoryLayout.stride let buffer = unsafe UnsafeRawBufferPointer(start: pointer, count: byteCount) - let rawSpan = RawSpan(_unsafeBytes: buffer) + let rawSpan = unsafe RawSpan(_unsafeBytes: buffer) self = unsafe _overrideLifetime(rawSpan, borrowing: mutableSpan) } } @@ -193,7 +204,7 @@ extension MutableSpan where Element: ~Copyable { @_alwaysEmitIntoClient public var _description: String { let addr = String( - unsafe UInt(bitPattern: _pointer), radix: 16, uppercase: false + UInt(bitPattern: _pointer), radix: 16, uppercase: false ) return "(0x\(addr), \(_count))" } @@ -227,7 +238,7 @@ extension MutableSpan where Element: BitwiseCopyable { public var bytes: RawSpan { @lifetime(borrow self) borrowing get { - RawSpan(_unsafeMutableSpan: self) + RawSpan(_mutableSpan: self) } } } @@ -359,7 +370,7 @@ extension MutableSpan where Element: ~Copyable { public func withUnsafeBufferPointer( _ body: (_ buffer: UnsafeBufferPointer) throws(E) -> Result ) throws(E) -> Result { - try Span(_unsafeMutableSpan: self).withUnsafeBufferPointer(body) + try Span(_mutableSpan: self).withUnsafeBufferPointer(body) } //FIXME: mark closure parameter as non-escaping @@ -389,7 +400,7 @@ extension MutableSpan where Element: BitwiseCopyable { public func withUnsafeBytes( _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result ) throws(E) -> Result { - try RawSpan(_unsafeMutableSpan: self).withUnsafeBytes(body) + try RawSpan(_mutableSpan: self).withUnsafeBytes(body) } //FIXME: mark closure parameter as non-escaping @@ -443,7 +454,7 @@ extension MutableSpan { fromContentsOf source: some Collection ) -> Index { let updated = source.withContiguousStorageIfAvailable { - self.update(fromContentsOf: Span(_unsafeElements: $0)) + self.update(fromContentsOf: unsafe Span(_unsafeElements: $0)) } if let updated { return updated @@ -572,7 +583,7 @@ extension MutableSpan where Element: BitwiseCopyable { fromContentsOf source: some Collection ) -> Index where Element: BitwiseCopyable { let updated = source.withContiguousStorageIfAvailable { - self.update(fromContentsOf: Span(_unsafeElements: $0)) + self.update(fromContentsOf: unsafe Span(_unsafeElements: $0)) } if let updated { return updated @@ -664,7 +675,7 @@ extension MutableSpan where Element: ~Copyable { mutating public func _extracting(unchecked bounds: Range) -> Self { let delta = bounds.lowerBound &* MemoryLayout.stride let newStart = unsafe _pointer?.advanced(by: delta) - let newSpan = Self(_unchecked: newStart, count: bounds.count) + let newSpan = unsafe Self(_unchecked: newStart, count: bounds.count) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -728,7 +739,7 @@ extension MutableSpan where Element: ~Copyable { @_alwaysEmitIntoClient @lifetime(borrow self) mutating public func _extracting(_: UnboundedRange) -> Self { - let newSpan = Self(_unchecked: _start(), count: _count) + let newSpan = unsafe Self(_unchecked: _start(), count: _count) return unsafe _overrideLifetime(newSpan, mutating: &self) } } @@ -757,7 +768,7 @@ extension MutableSpan where Element: ~Copyable { mutating public func _extracting(first maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, count) - let newSpan = Self(_unchecked: _pointer, count: newCount) + let newSpan = unsafe Self(_unchecked: _pointer, count: newCount) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -780,7 +791,8 @@ extension MutableSpan where Element: ~Copyable { mutating public func _extracting(droppingLast k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) - let newSpan = Self(_unchecked: _pointer, count: count &- droppedCount) + let newCount = count &- droppedCount + let newSpan = unsafe Self(_unchecked: _pointer, count: newCount) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -806,7 +818,7 @@ extension MutableSpan where Element: ~Copyable { let newCount = min(maxLength, count) let offset = (count &- newCount) * MemoryLayout.stride let newStart = unsafe _pointer?.advanced(by: offset) - let newSpan = Self(_unchecked: newStart, count: newCount) + let newSpan = unsafe Self(_unchecked: newStart, count: newCount) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -831,7 +843,8 @@ extension MutableSpan where Element: ~Copyable { let droppedCount = min(k, count) let offset = droppedCount * MemoryLayout.stride let newStart = unsafe _pointer?.advanced(by: offset) - let newSpan = Self(_unchecked: newStart, count: count &- droppedCount) + let newCount = count &- droppedCount + let newSpan = unsafe Self(_unchecked: newStart, count: newCount) return unsafe _overrideLifetime(newSpan, mutating: &self) } } diff --git a/test/stdlib/Span/MutableSpanTests.swift b/test/stdlib/Span/MutableSpanTests.swift index 4fc7ed00f3909..cb4223820cbb0 100644 --- a/test/stdlib/Span/MutableSpanTests.swift +++ b/test/stdlib/Span/MutableSpanTests.swift @@ -102,7 +102,7 @@ suite.test("Span from MutableSpan") var array = [0, 1, 2] array.withUnsafeMutableBufferPointer { let mutable = MutableSpan(_unsafeElements: $0) - let immutable = Span(_unsafeMutableSpan: mutable) + let immutable = Span(_mutableSpan: mutable) expectEqual(mutable.count, immutable.count) } } From 90684a87a3f6c566f78a17e3851bb768d5fa99b0 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 11 Mar 2025 11:25:45 -0700 Subject: [PATCH 14/23] [stdlib] add missing unsafe annotations --- stdlib/public/core/Span/MutableRawSpan.swift | 2 +- stdlib/public/core/Span/MutableSpan.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index 651df9761f44c..b27cf29aa486e 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -125,7 +125,7 @@ extension MutableRawSpan { @_alwaysEmitIntoClient public var byteOffsets: Range { - .init(_uncheckedBounds: (0, byteCount)) + unsafe Range(_uncheckedBounds: (0, byteCount)) } } diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index 688ce4528cdca..e07b38ab799cb 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -224,7 +224,7 @@ extension MutableSpan where Element: ~Copyable & ~Escapable { @_alwaysEmitIntoClient public var indices: Range { - Range(_uncheckedBounds: (0, _count)) + unsafe Range(_uncheckedBounds: (0, _count)) } } @@ -721,7 +721,7 @@ extension MutableSpan where Element: ~Copyable { mutating public func _extracting( unchecked bounds: ClosedRange ) -> Self { - let range = Range( + let range = unsafe Range( _uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1) ) return unsafe _extracting(unchecked: range) From ecebaa591e0fceec41e26f27b7885bb2d0f7f476 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 24 Feb 2025 17:49:51 -0800 Subject: [PATCH 15/23] [stdlib] move `span` properties of array types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …to a more logical spot in each of their files --- stdlib/public/core/Array.swift | 40 +++++++++++++----------- stdlib/public/core/ArraySlice.swift | 22 ++++++------- stdlib/public/core/ContiguousArray.swift | 23 +++++++------- 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 8d9d8c2548f1e..febe21dfa1769 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1428,25 +1428,6 @@ extension Array: RangeReplaceableCollection { } } - @available(SwiftStdlib 6.2, *) - public var span: Span { - @lifetime(borrow self) - @_alwaysEmitIntoClient - borrowing get { -#if _runtime(_ObjC) - if _slowPath(!_buffer._isNative) { - let buffer = _buffer.getOrAllocateAssociatedObjectBuffer() - let (pointer, count) = unsafe (buffer.firstElementAddress, buffer.count) - let span = unsafe Span(_unsafeStart: pointer, count: count) - return unsafe _overrideLifetime(span, borrowing: self) - } -#endif - let (pointer, count) = unsafe (_buffer.firstElementAddress, _buffer.count) - let span = unsafe Span(_unsafeStart: pointer, count: count) - return unsafe _overrideLifetime(span, borrowing: self) - } - } - @inlinable public __consuming func _copyToContiguousArray() -> ContiguousArray { if let n = _buffer.requestNativeBuffer() { @@ -1630,6 +1611,27 @@ extension Array { return try unsafe _buffer.withUnsafeBufferPointer(body) } + @available(SwiftStdlib 6.2, *) + public var span: Span { + @lifetime(borrow self) + @_alwaysEmitIntoClient + borrowing get { +#if _runtime(_ObjC) + if _slowPath(!_buffer._isNative) { + let buffer = _buffer.getOrAllocateAssociatedObjectBuffer() + let pointer = unsafe buffer.firstElementAddress + let count = buffer.immutableCount + let span = unsafe Span(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, borrowing: self) + } +#endif + let pointer = unsafe _buffer.firstElementAddress + let count = _buffer.immutableCount + let span = unsafe Span(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, borrowing: self) + } + } + // Superseded by the typed-throws version of this function, but retained // for ABI reasons. @_semantics("array.withUnsafeMutableBufferPointer") diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index 6ea26d6b04d32..dc2c81a529321 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -1116,17 +1116,6 @@ extension ArraySlice: RangeReplaceableCollection { } } - @available(SwiftStdlib 6.2, *) - public var span: Span { - @lifetime(borrow self) - @_alwaysEmitIntoClient - borrowing get { - let (pointer, count) = (_buffer.firstElementAddress, _buffer.count) - let span = unsafe Span(_unsafeStart: pointer, count: count) - return unsafe _overrideLifetime(span, borrowing: self) - } - } - @inlinable public __consuming func _copyToContiguousArray() -> ContiguousArray { if let n = _buffer.requestNativeBuffer() { @@ -1221,6 +1210,17 @@ extension ArraySlice { return try _buffer.withUnsafeBufferPointer(body) } + @available(SwiftStdlib 6.2, *) + public var span: Span { + @lifetime(borrow self) + @_alwaysEmitIntoClient + borrowing get { + let (pointer, count) = unsafe (_buffer.firstElementAddress, _buffer.count) + let span = unsafe Span(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, borrowing: self) + } + } + // Superseded by the typed-throws version of this function, but retained // for ABI reasons. @_semantics("array.withUnsafeMutableBufferPointer") diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index 5ecd938d4d4a5..8ead0dfc3de60 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -1021,17 +1021,6 @@ extension ContiguousArray: RangeReplaceableCollection { } } - @available(SwiftStdlib 6.2, *) - public var span: Span { - @lifetime(borrow self) - @_alwaysEmitIntoClient - borrowing get { - let (pointer, count) = unsafe (_buffer.firstElementAddress, _buffer.count) - let span = unsafe Span(_unsafeStart: pointer, count: count) - return unsafe _overrideLifetime(span, borrowing: self) - } - } - @inlinable public __consuming func _copyToContiguousArray() -> ContiguousArray { if let n = _buffer.requestNativeBuffer() { @@ -1163,6 +1152,18 @@ extension ContiguousArray { return try unsafe _buffer.withUnsafeBufferPointer(body) } + @available(SwiftStdlib 6.2, *) + public var span: Span { + @lifetime(borrow self) + @_alwaysEmitIntoClient + borrowing get { + let pointer = unsafe _buffer.firstElementAddress + let count = _buffer.immutableCount + let span = unsafe Span(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, borrowing: self) + } + } + // Superseded by the typed-throws version of this function, but retained // for ABI reasons. @_semantics("array.withUnsafeMutableBufferPointer") From 107b38f9e04f617c3ae723b728e4785d27fb8d20 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 11 Mar 2025 15:54:28 -0700 Subject: [PATCH 16/23] [stdlib] add `mutableSpan` to array types --- stdlib/public/core/Array.swift | 16 ++++++++++++++++ stdlib/public/core/ArraySlice.swift | 15 +++++++++++++++ stdlib/public/core/ContiguousArray.swift | 16 ++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index febe21dfa1769..ccec5d1c45a10 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1730,6 +1730,22 @@ extension Array { return try unsafe body(&inoutBufferPointer) } + @available(SwiftStdlib 6.2, *) + public var mutableSpan: MutableSpan { + @lifetime(/*inout*/borrow self) + @_alwaysEmitIntoClient + mutating get { + _makeMutableAndUnique() + // NOTE: We don't have the ability to schedule a call to + // ContiguousArrayBuffer.endCOWMutation(). + // rdar://146785284 (lifetime analysis for end of mutation) + let pointer = unsafe _buffer.firstElementAddress + let count = _buffer.mutableCount + let span = unsafe MutableSpan(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, mutating: &self) + } + } + @inlinable public __consuming func _copyContents( initializing buffer: UnsafeMutableBufferPointer diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index dc2c81a529321..93d71f8090d2a 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -1300,6 +1300,21 @@ extension ArraySlice { return try unsafe body(&inoutBufferPointer) } + @available(SwiftStdlib 6.2, *) + public var mutableSpan: MutableSpan { + @lifetime(/*inout*/borrow self) + @_alwaysEmitIntoClient + mutating get { + _makeMutableAndUnique() + // NOTE: We don't have the ability to schedule a call to + // ContiguousArrayBuffer.endCOWMutation(). + // rdar://146785284 (lifetime analysis for end of mutation) + let (pointer, count) = unsafe (_buffer.firstElementAddress, _buffer.count) + let span = unsafe MutableSpan(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, mutating: &self) + } + } + @inlinable public __consuming func _copyContents( initializing buffer: UnsafeMutableBufferPointer diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index 8ead0dfc3de60..3c4b099b14e71 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -1242,6 +1242,22 @@ extension ContiguousArray { return try unsafe body(&inoutBufferPointer) } + @available(SwiftStdlib 6.2, *) + public var mutableSpan: MutableSpan { + @lifetime(/*inout*/borrow self) + @_alwaysEmitIntoClient + mutating get { + _makeMutableAndUnique() + // NOTE: We don't have the ability to schedule a call to + // ContiguousArrayBuffer.endCOWMutation(). + // rdar://146785284 (lifetime analysis for end of mutation) + let pointer = unsafe _buffer.firstElementAddress + let count = _buffer.mutableCount + let span = unsafe MutableSpan(_unsafeStart: pointer, count: count) + return unsafe _overrideLifetime(span, mutating: &self) + } + } + @inlinable public __consuming func _copyContents( initializing buffer: UnsafeMutableBufferPointer From 0c3f0846308878a32fa179bf58e1479754cbe416 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 11 Mar 2025 16:06:40 -0700 Subject: [PATCH 17/23] [test] Array-family `mutableSpan` properties --- test/stdlib/Span/ArraySpanProperties.swift | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/stdlib/Span/ArraySpanProperties.swift b/test/stdlib/Span/ArraySpanProperties.swift index b35959bae59b2..3baeddb28f06a 100644 --- a/test/stdlib/Span/ArraySpanProperties.swift +++ b/test/stdlib/Span/ArraySpanProperties.swift @@ -34,6 +34,20 @@ suite.test("Array.span property") expectEqual(span[0], a[0]) } +suite.test("Array.mutableSpan property") +.require(.stdlib_6_2).code { + guard #available(SwiftStdlib 6.2, *) else { return } + + let capacity = 4 + var a = Array(0.. Date: Tue, 11 Mar 2025 22:59:53 -0700 Subject: [PATCH 18/23] [stdlib] de-underscore the `extracting()` methods --- stdlib/public/core/Span/MutableRawSpan.swift | 24 +++++----- stdlib/public/core/Span/MutableSpan.swift | 24 +++++----- test/stdlib/Span/MutableRawSpanTests.swift | 46 ++++++++++---------- test/stdlib/Span/MutableSpanTests.swift | 44 +++++++++---------- 4 files changed, 69 insertions(+), 69 deletions(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index b27cf29aa486e..95ee419dfe32d 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -439,13 +439,13 @@ extension MutableRawSpan { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(_ bounds: Range) -> Self { + mutating public func extracting(_ bounds: Range) -> Self { _precondition( UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), "Index range out of bounds" ) - return unsafe _extracting(unchecked: bounds) + return unsafe extracting(unchecked: bounds) } /// Constructs a new span over the items within the supplied range of @@ -466,7 +466,7 @@ extension MutableRawSpan { @unsafe @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(unchecked bounds: Range) -> Self { + mutating public func extracting(unchecked bounds: Range) -> Self { let newStart = unsafe _pointer?.advanced(by: bounds.lowerBound) let newSpan = unsafe Self(_unchecked: newStart, byteCount: bounds.count) return unsafe _overrideLifetime(newSpan, mutating: &self) @@ -487,10 +487,10 @@ extension MutableRawSpan { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting( + mutating public func extracting( _ bounds: some RangeExpression ) -> Self { - _extracting(bounds.relative(to: byteOffsets)) + extracting(bounds.relative(to: byteOffsets)) } /// Constructs a new span over the items within the supplied range of @@ -511,11 +511,11 @@ extension MutableRawSpan { @unsafe @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(unchecked bounds: ClosedRange) -> Self { + mutating public func extracting(unchecked bounds: ClosedRange) -> Self { let range = unsafe Range( _uncheckedBounds: (bounds.lowerBound, bounds.upperBound+1) ) - return unsafe _extracting(unchecked: range) + return unsafe extracting(unchecked: range) } /// Constructs a new span over all the items of this span. @@ -529,7 +529,7 @@ extension MutableRawSpan { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(_: UnboundedRange) -> Self { + mutating public func extracting(_: UnboundedRange) -> Self { let newSpan = unsafe Self(_unchecked: _start(), byteCount: _count) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -556,7 +556,7 @@ extension MutableRawSpan { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(first maxLength: Int) -> Self { + mutating public func extracting(first maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, byteCount) let newSpan = unsafe Self(_unchecked: _pointer, byteCount: newCount) @@ -579,7 +579,7 @@ extension MutableRawSpan { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(droppingLast k: Int) -> Self { + mutating public func extracting(droppingLast k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, byteCount) let newCount = byteCount &- droppedCount @@ -604,7 +604,7 @@ extension MutableRawSpan { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(last maxLength: Int) -> Self { + mutating public func extracting(last maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, byteCount) let newStart = unsafe _pointer?.advanced(by: byteCount &- newCount) @@ -628,7 +628,7 @@ extension MutableRawSpan { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(droppingFirst k: Int) -> Self { + mutating public func extracting(droppingFirst k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of bytes") let droppedCount = min(k, byteCount) let newStart = unsafe _pointer?.advanced(by: droppedCount) diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index e07b38ab799cb..a95c8799c66de 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -645,13 +645,13 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(_ bounds: Range) -> Self { + mutating public func extracting(_ bounds: Range) -> Self { _precondition( UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), "Index range out of bounds" ) - return unsafe _extracting(unchecked: bounds) + return unsafe extracting(unchecked: bounds) } /// Constructs a new span over the items within the supplied range of @@ -672,7 +672,7 @@ extension MutableSpan where Element: ~Copyable { @unsafe @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(unchecked bounds: Range) -> Self { + mutating public func extracting(unchecked bounds: Range) -> Self { let delta = bounds.lowerBound &* MemoryLayout.stride let newStart = unsafe _pointer?.advanced(by: delta) let newSpan = unsafe Self(_unchecked: newStart, count: bounds.count) @@ -694,10 +694,10 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting( + mutating public func extracting( _ bounds: some RangeExpression ) -> Self { - _extracting(bounds.relative(to: indices)) + extracting(bounds.relative(to: indices)) } /// Constructs a new span over the items within the supplied range of @@ -718,13 +718,13 @@ extension MutableSpan where Element: ~Copyable { @unsafe @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting( + mutating public func extracting( unchecked bounds: ClosedRange ) -> Self { let range = unsafe Range( _uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1) ) - return unsafe _extracting(unchecked: range) + return unsafe extracting(unchecked: range) } /// Constructs a new span over all the items of this span. @@ -738,7 +738,7 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(_: UnboundedRange) -> Self { + mutating public func extracting(_: UnboundedRange) -> Self { let newSpan = unsafe Self(_unchecked: _start(), count: _count) return unsafe _overrideLifetime(newSpan, mutating: &self) } @@ -765,7 +765,7 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(first maxLength: Int) -> Self { + mutating public func extracting(first maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, count) let newSpan = unsafe Self(_unchecked: _pointer, count: newCount) @@ -788,7 +788,7 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(droppingLast k: Int) -> Self { + mutating public func extracting(droppingLast k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) let newCount = count &- droppedCount @@ -813,7 +813,7 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(last maxLength: Int) -> Self { + mutating public func extracting(last maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, count) let offset = (count &- newCount) * MemoryLayout.stride @@ -838,7 +838,7 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @_alwaysEmitIntoClient @lifetime(borrow self) - mutating public func _extracting(droppingFirst k: Int) -> Self { + mutating public func extracting(droppingFirst k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) let offset = droppedCount * MemoryLayout.stride diff --git a/test/stdlib/Span/MutableRawSpanTests.swift b/test/stdlib/Span/MutableRawSpanTests.swift index 67aceec933a14..51b9cbcb30228 100644 --- a/test/stdlib/Span/MutableRawSpanTests.swift +++ b/test/stdlib/Span/MutableRawSpanTests.swift @@ -214,7 +214,7 @@ suite.test("unsafeLoadUnaligned(as:)") a.withUnsafeMutableBytes { var span = MutableRawSpan(_unsafeBytes: $0) - let suffix = span._extracting(droppingFirst: 2) + let suffix = span.extracting(droppingFirst: 2) let u0 = suffix.unsafeLoadUnaligned(as: UInt64.self) expectEqual(u0 & 0xff, 2) expectEqual(u0.byteSwapped & 0xff, 9) @@ -350,7 +350,7 @@ suite.test("update(fromContentsOf:) (contiguous memory)") expectEqual(a.elementsEqual(0..(start: nil, count: 0) var span = MutableSpan(_unsafeElements: b) expectEqual(span.count, b.count) - expectEqual(span._extracting(first: 1).count, b.count) - expectEqual(span._extracting(droppingLast: 1).count, b.count) + expectEqual(span.extracting(first: 1).count, b.count) + expectEqual(span.extracting(droppingLast: 1).count, b.count) } } -suite.test("_extracting suffixes") +suite.test("extracting suffixes") .skip(.custom( { if #available(SwiftStdlib 6.2, *) { false } else { true } }, reason: "Requires Swift 6.2's standard library" @@ -675,19 +675,19 @@ suite.test("_extracting suffixes") var span = MutableSpan(_unsafeElements: $0) expectEqual(span.count, capacity) - suffix = span._extracting(last: capacity) + suffix = span.extracting(last: capacity) expectEqual(suffix[0], 0) - suffix = span._extracting(last: capacity-1) + suffix = span.extracting(last: capacity-1) expectEqual(suffix[0], 1) - suffix = span._extracting(last: 1) + suffix = span.extracting(last: 1) expectEqual(suffix[0], UInt8(capacity-1)) - suffix = span._extracting(droppingFirst: capacity) + suffix = span.extracting(droppingFirst: capacity) expectTrue(suffix.isEmpty) - suffix = span._extracting(droppingFirst: 1) + suffix = span.extracting(droppingFirst: 1) expectEqual(suffix[0], 1) } @@ -695,7 +695,7 @@ suite.test("_extracting suffixes") let b = UnsafeMutableBufferPointer(start: nil, count: 0) var span = MutableSpan(_unsafeElements: b) expectEqual(span.count, b.count) - expectEqual(span._extracting(last: 1).count, b.count) - expectEqual(span._extracting(droppingFirst: 1).count, b.count) + expectEqual(span.extracting(last: 1).count, b.count) + expectEqual(span.extracting(droppingFirst: 1).count, b.count) } } From 0805abd8eeb7cc146ef2e19d9a8583a9fddd07c7 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 12 Mar 2025 07:34:09 -0700 Subject: [PATCH 19/23] [test] own up to new abi --- test/abi/macOS/arm64/stdlib.swift | 5 +++++ test/abi/macOS/x86_64/stdlib.swift | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/test/abi/macOS/arm64/stdlib.swift b/test/abi/macOS/arm64/stdlib.swift index b76a53699f5b8..4fa3cb131b307 100644 --- a/test/abi/macOS/arm64/stdlib.swift +++ b/test/abi/macOS/arm64/stdlib.swift @@ -838,6 +838,11 @@ Added: _$ss15CollectionOfOneV4spans4SpanVyxGvpMV Added: _$ss15ContiguousArrayV4spans4SpanVyxGvpMV Added: _$ss4SpanVss15BitwiseCopyableRzlE5bytess03RawA0VvpMV +// SE-0467 mutableSpan properties +Added: _$sSa11mutableSpans07MutableB0VyxGvr +Added: _$ss10ArraySliceV11mutableSpans07MutableD0VyxGvr +Added: _$ss15ContiguousArrayV11mutableSpans07MutableD0VyxGvr + // _SwiftifyInfo enum for _SwiftifyImports macro Added: _$ss13_SwiftifyExprO5paramyABSicABmFWC Added: _$ss13_SwiftifyExprO6returnyA2BmFWC diff --git a/test/abi/macOS/x86_64/stdlib.swift b/test/abi/macOS/x86_64/stdlib.swift index cb1c58d61af3b..33a0291667700 100644 --- a/test/abi/macOS/x86_64/stdlib.swift +++ b/test/abi/macOS/x86_64/stdlib.swift @@ -839,6 +839,11 @@ Added: _$ss15CollectionOfOneV4spans4SpanVyxGvpMV Added: _$ss15ContiguousArrayV4spans4SpanVyxGvpMV Added: _$ss4SpanVss15BitwiseCopyableRzlE5bytess03RawA0VvpMV +// SE-0467 mutableSpan properties +Added: _$sSa11mutableSpans07MutableB0VyxGvr +Added: _$ss10ArraySliceV11mutableSpans07MutableD0VyxGvr +Added: _$ss15ContiguousArrayV11mutableSpans07MutableD0VyxGvr + // _SwiftifyInfo enum for _SwiftifyImports macro Added: _$ss13_SwiftifyExprO5paramyABSicABmFWC Added: _$ss13_SwiftifyExprO6returnyA2BmFWC From 51db21acc482f62f6065d88001c36987cd68aafc Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 12 Mar 2025 09:34:56 -0700 Subject: [PATCH 20/23] [build] add files for the new build system --- Runtimes/Core/core/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Runtimes/Core/core/CMakeLists.txt b/Runtimes/Core/core/CMakeLists.txt index 8a29b5796fe4d..72be076530754 100644 --- a/Runtimes/Core/core/CMakeLists.txt +++ b/Runtimes/Core/core/CMakeLists.txt @@ -152,6 +152,8 @@ add_library(swiftCore Sort.swift Span/Span.swift Span/RawSpan.swift + Span/MutableSpan.swift + Span/MutableRawSpan.swift StaticString.swift StaticPrint.swift Stride.swift From 6a7dbb02f39041b846e6c2a9377991c9afa31df1 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 21 Mar 2025 12:54:13 -0700 Subject: [PATCH 21/23] [stdlib] update lifetime annotations for Mutable[Raw]Span --- stdlib/public/core/Span/MutableRawSpan.swift | 12 ++++++++++- stdlib/public/core/Span/MutableSpan.swift | 22 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index 95ee419dfe32d..bee7feab1450d 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -102,7 +102,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient - @lifetime(elements) + @lifetime(copy elements) public init( _elements elements: consuming MutableSpan ) { @@ -143,6 +143,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func withUnsafeMutableBytes( _ body: (UnsafeMutableRawBufferPointer) throws(E) -> Result ) throws(E) -> Result { @@ -315,6 +316,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func storeBytes( of value: T, toByteOffset offset: Int = 0, as type: T.Type ) { @@ -328,6 +330,7 @@ extension MutableRawSpan { @unsafe @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func storeBytes( of value: T, toUncheckedByteOffset offset: Int, as type: T.Type ) { @@ -340,6 +343,7 @@ extension MutableRawSpan { extension MutableRawSpan { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from source: S ) -> (unwritten: S.Iterator, byteOffset: Int) where S.Element: BitwiseCopyable { @@ -349,6 +353,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from elements: inout some IteratorProtocol ) -> Int { @@ -364,6 +369,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: C ) -> Int where C.Element: BitwiseCopyable { @@ -382,6 +388,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: Span ) -> Int { @@ -392,6 +399,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: borrowing MutableSpan ) -> Int { @@ -402,6 +410,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: RawSpan ) -> Int { @@ -413,6 +422,7 @@ extension MutableRawSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: borrowing MutableRawSpan ) -> Int { diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index a95c8799c66de..e24c09cb9ecae 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -258,6 +258,7 @@ extension MutableSpan where Element: ~Copyable { _precondition(indices.contains(position), "index out of bounds") return unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: position)) } + @lifetime(self: copy self) unsafeMutableAddress { _precondition(indices.contains(position), "index out of bounds") return unsafe _unsafeAddressOfElement(unchecked: position) @@ -278,6 +279,7 @@ extension MutableSpan where Element: ~Copyable { unsafeAddress { unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: position)) } + @lifetime(self: copy self) unsafeMutableAddress { unsafe _unsafeAddressOfElement(unchecked: position) } @@ -298,6 +300,7 @@ extension MutableSpan where Element: ~Copyable { extension MutableSpan where Element: ~Copyable { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func swapAt(_ i: Index, _ j: Index) { _precondition(indices.contains(Index(i))) _precondition(indices.contains(Index(j))) @@ -306,6 +309,7 @@ extension MutableSpan where Element: ~Copyable { @unsafe @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func swapAt(unchecked i: Index, unchecked j: Index) { let pi = unsafe _unsafeAddressOfElement(unchecked: i) let pj = unsafe _unsafeAddressOfElement(unchecked: j) @@ -330,6 +334,7 @@ extension MutableSpan where Element: BitwiseCopyable { _precondition(indices.contains(position), "index out of bounds") return unsafe self[unchecked: position] } + @lifetime(self: copy self) set { _precondition(indices.contains(position), "index out of bounds") unsafe self[unchecked: position] = newValue @@ -353,6 +358,7 @@ extension MutableSpan where Element: BitwiseCopyable { fromByteOffset: offset, as: Element.self ) } + @lifetime(self: copy self) set { let offset = position&*MemoryLayout.stride unsafe _start().storeBytes( @@ -375,6 +381,7 @@ extension MutableSpan where Element: ~Copyable { //FIXME: mark closure parameter as non-escaping @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func withUnsafeMutableBufferPointer< E: Error, Result: ~Copyable >( @@ -405,6 +412,7 @@ extension MutableSpan where Element: BitwiseCopyable { //FIXME: mark closure parameter as non-escaping @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func withUnsafeMutableBytes( _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result ) throws(E) -> Result { @@ -421,6 +429,7 @@ extension MutableSpan where Element: BitwiseCopyable { extension MutableSpan { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update(repeating repeatedValue: consuming Element) { unsafe _start().withMemoryRebound(to: Element.self, capacity: count) { unsafe $0.update(repeating: repeatedValue, count: count) @@ -428,6 +437,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from source: S ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element { @@ -437,6 +447,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from elements: inout some IteratorProtocol ) -> Index { @@ -450,6 +461,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: some Collection ) -> Index { @@ -472,6 +484,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update(fromContentsOf source: Span) -> Index { guard !source.isEmpty else { return 0 } _precondition( @@ -489,6 +502,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: borrowing MutableSpan ) -> Index { @@ -517,6 +531,7 @@ extension MutableSpan where Element: ~Copyable { // } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func moveUpdate( fromContentsOf source: UnsafeMutableBufferPointer ) -> Index { @@ -532,6 +547,7 @@ extension MutableSpan where Element: ~Copyable { extension MutableSpan { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func moveUpdate( fromContentsOf source: Slice> ) -> Index { @@ -543,6 +559,7 @@ extension MutableSpan { extension MutableSpan where Element: BitwiseCopyable { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( repeating repeatedValue: Element ) where Element: BitwiseCopyable { @@ -556,6 +573,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from source: S ) -> (unwritten: S.Iterator, index: Index) @@ -566,6 +584,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from elements: inout some IteratorProtocol ) -> Index { @@ -579,6 +598,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: some Collection ) -> Index where Element: BitwiseCopyable { @@ -601,6 +621,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: Span ) -> Index where Element: BitwiseCopyable { @@ -619,6 +640,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: borrowing MutableSpan ) -> Index where Element: BitwiseCopyable { From abf29e743370832e00bfc738e1adfe82e342b4d3 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 27 Mar 2025 17:19:15 -0700 Subject: [PATCH 22/23] =?UTF-8?q?[stdlib]=20adjust=20MutableSpan=E2=80=99s?= =?UTF-8?q?=20element=20constraint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stdlib/public/core/Span/MutableSpan.swift | 4 ++-- test/abi/macOS/arm64/stdlib.swift | 4 ++-- test/abi/macOS/x86_64/stdlib.swift | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index e24c09cb9ecae..dd2ff61ffee2c 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -15,7 +15,7 @@ @safe @frozen @available(SwiftStdlib 6.2, *) -public struct MutableSpan +public struct MutableSpan : ~Copyable, ~Escapable { @usableFromInline internal let _pointer: UnsafeMutableRawPointer? @@ -212,7 +212,7 @@ extension MutableSpan where Element: ~Copyable { //MARK: Collection, RandomAccessCollection @available(SwiftStdlib 6.2, *) -extension MutableSpan where Element: ~Copyable & ~Escapable { +extension MutableSpan where Element: ~Copyable { @_alwaysEmitIntoClient public var count: Int { _count } diff --git a/test/abi/macOS/arm64/stdlib.swift b/test/abi/macOS/arm64/stdlib.swift index 4fa3cb131b307..4b576a00ebe11 100644 --- a/test/abi/macOS/arm64/stdlib.swift +++ b/test/abi/macOS/arm64/stdlib.swift @@ -817,8 +817,8 @@ Added: _$ss7RawSpanVN // SE-0467 MutableSpan and MutableRawSpan Added: _$ss11MutableSpanVMa Added: _$ss11MutableSpanVMn -Added: _$ss11MutableSpanVsRi_zRi0_zrlE6_countSivg -Added: _$ss11MutableSpanVsRi_zRi0_zrlE8_pointerSvSgvg +Added: _$ss11MutableSpanVsRi_zrlE6_countSivg +Added: _$ss11MutableSpanVsRi_zrlE8_pointerSvSgvg Added: _$ss11MutableSpanVsRi_zrlE10_uncheckedAByxGSryxG_tcfC Added: _$ss14MutableRawSpanV6_countSivg Added: _$ss14MutableRawSpanV8_pointerSvSgvg diff --git a/test/abi/macOS/x86_64/stdlib.swift b/test/abi/macOS/x86_64/stdlib.swift index 33a0291667700..a93fe54a5ea45 100644 --- a/test/abi/macOS/x86_64/stdlib.swift +++ b/test/abi/macOS/x86_64/stdlib.swift @@ -818,8 +818,8 @@ Added: _$ss7RawSpanVN // SE-0467 MutableSpan and MutableRawSpan Added: _$ss11MutableSpanVMa Added: _$ss11MutableSpanVMn -Added: _$ss11MutableSpanVsRi_zRi0_zrlE6_countSivg -Added: _$ss11MutableSpanVsRi_zRi0_zrlE8_pointerSvSgvg +Added: _$ss11MutableSpanVsRi_zrlE6_countSivg +Added: _$ss11MutableSpanVsRi_zrlE8_pointerSvSgvg Added: _$ss11MutableSpanVsRi_zrlE10_uncheckedAByxGSryxG_tcfC Added: _$ss14MutableRawSpanV6_countSivg Added: _$ss14MutableRawSpanV8_pointerSvSgvg From 32e1ea1180c59be50b4c457e51d42a0c938c5dca Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 28 Mar 2025 07:50:10 -0700 Subject: [PATCH 23/23] [test] add required LifetimeDependence feature --- .../CxxToSwiftToCxx/bridge-cxx-enum-back-to-cxx-execution.cpp | 3 ++- .../bridge-cxx-struct-back-to-cxx-execution.cpp | 3 ++- test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/Interop/CxxToSwiftToCxx/bridge-cxx-enum-back-to-cxx-execution.cpp b/test/Interop/CxxToSwiftToCxx/bridge-cxx-enum-back-to-cxx-execution.cpp index 292d2d71a0022..7a1a7c17df0f3 100644 --- a/test/Interop/CxxToSwiftToCxx/bridge-cxx-enum-back-to-cxx-execution.cpp +++ b/test/Interop/CxxToSwiftToCxx/bridge-cxx-enum-back-to-cxx-execution.cpp @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: split-file %s %t -// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies +// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies -enable-experimental-feature LifetimeDependence // RUN: %target-swift-frontend -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxx -emit-clang-header-path %t/UseCxx.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=all-public // RUN: %target-interop-build-clangxx -std=c++20 -c %t/use-swift-cxx-types.cpp -I %t -o %t/swift-cxx-execution.o -g @@ -11,6 +11,7 @@ // RUN: %target-run %t/swift-cxx-execution // REQUIRES: executable_test +// REQUIRES: swift_feature_LifetimeDependence //--- header.h enum class SomeEnum { diff --git a/test/Interop/CxxToSwiftToCxx/bridge-cxx-struct-back-to-cxx-execution.cpp b/test/Interop/CxxToSwiftToCxx/bridge-cxx-struct-back-to-cxx-execution.cpp index 778228ec9a983..354760458e97f 100644 --- a/test/Interop/CxxToSwiftToCxx/bridge-cxx-struct-back-to-cxx-execution.cpp +++ b/test/Interop/CxxToSwiftToCxx/bridge-cxx-struct-back-to-cxx-execution.cpp @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: split-file %s %t -// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies +// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies -enable-experimental-feature LifetimeDependence // RUN: %target-swift-frontend -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxx -emit-clang-header-path %t/UseCxx.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=all-public @@ -12,6 +12,7 @@ // RUN: %target-run %t/swift-cxx-execution | %FileCheck %s // REQUIRES: executable_test +// REQUIRES: swift_feature_LifetimeDependence //--- header.h diff --git a/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift b/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift index 4be96537c19f4..ffbceace05e89 100644 --- a/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift +++ b/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift @@ -1,10 +1,12 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr-or-stdlib -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies +// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr-or-stdlib -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies -enable-experimental-feature LifetimeDependence // RUN: %FileCheck %s < %t/Swift.h // RUN: %check-interop-cxx-header-in-clang(%t/Swift.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-unused-private-field -Wno-unused-function -Wc++98-compat-extra-semi) // RUN: %check-interop-cxx-header-in-clang(%t/Swift.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-unused-private-field -Wno-unused-function -Wc++98-compat-extra-semi -DDEBUG=1) +// REQUIRES: swift_feature_LifetimeDependence + // CHECK: namespace swift SWIFT_PRIVATE_ATTR SWIFT_SYMBOL_MODULE("swift") { // CHECK: template