From a9ee0b6ba0d0d2c67cc6fcc5999e10616777ab40 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 3 Nov 2025 13:43:34 -0800 Subject: [PATCH 1/7] Add Iterable BorrowIteratorProtocol --- .../cmake/modules/ExperimentalFeatures.cmake | 3 +- Runtimes/Core/core/CMakeLists.txt | 2 + stdlib/cmake/modules/SwiftSource.cmake | 9 +- stdlib/public/core/Array.swift | 36 +++- .../public/core/BorrowIteratorProtocol.swift | 185 ++++++++++++++++++ stdlib/public/core/CMakeLists.txt | 6 +- stdlib/public/core/GroupInfo.json | 6 +- stdlib/public/core/Iterable.swift | 58 ++++++ 8 files changed, 289 insertions(+), 16 deletions(-) create mode 100644 stdlib/public/core/BorrowIteratorProtocol.swift create mode 100644 stdlib/public/core/Iterable.swift diff --git a/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake b/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake index d11fb6e8fcab3..e6c1d3c3523c9 100644 --- a/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake +++ b/Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake @@ -13,4 +13,5 @@ add_compile_options( "$<$:SHELL:-enable-experimental-feature BitwiseCopyable>" "$<$:SHELL:-enable-experimental-feature Extern>" "$<$:SHELL:-enable-experimental-feature AllowUnsafeAttribute>" - "$<$:SHELL:-enable-experimental-feature ValueGenerics>") + "$<$:SHELL:-enable-experimental-feature ValueGenerics>" + "$<$:SHELL:-enable-experimental-feature Lifetimes>") diff --git a/Runtimes/Core/core/CMakeLists.txt b/Runtimes/Core/core/CMakeLists.txt index 023c23fe9e433..7b99164d5c05a 100644 --- a/Runtimes/Core/core/CMakeLists.txt +++ b/Runtimes/Core/core/CMakeLists.txt @@ -42,6 +42,7 @@ add_library(swiftCore BidirectionalCollection.swift Bitset.swift Bool.swift + BorrowIteratorProtocol.swift BridgeObjectiveC.swift BridgeStorage.swift BridgingBuffer.swift @@ -93,6 +94,7 @@ add_library(swiftCore InputStream.swift IntegerParsing.swift Integers.swift + Iterable.swift Join.swift KeyPath.swift KeyValuePairs.swift diff --git a/stdlib/cmake/modules/SwiftSource.cmake b/stdlib/cmake/modules/SwiftSource.cmake index 98ead31c28e32..6e81f20cadde2 100644 --- a/stdlib/cmake/modules/SwiftSource.cmake +++ b/stdlib/cmake/modules/SwiftSource.cmake @@ -342,15 +342,15 @@ function(_add_target_variant_swift_compile_flags if(SWIFT_STDLIB_OS_VERSIONING) list(APPEND result "-D" "SWIFT_RUNTIME_OS_VERSIONING") endif() - + if(SWIFT_STDLIB_STATIC_PRINT) list(APPEND result "-D" "SWIFT_STDLIB_STATIC_PRINT") endif() - + if(SWIFT_STDLIB_ENABLE_UNICODE_DATA) list(APPEND result "-D" "SWIFT_STDLIB_ENABLE_UNICODE_DATA") endif() - + if(SWIFT_STDLIB_ENABLE_VECTOR_TYPES) list(APPEND result "-D" "SWIFT_STDLIB_ENABLE_VECTOR_TYPES") endif() @@ -612,7 +612,7 @@ function(_compile_swift_files list(APPEND swift_flags "-autolink-force-load") endif() - # Don't need to link runtime compatibility libraries for older runtimes + # Don't need to link runtime compatibility libraries for older runtimes # into the new runtime. if (SWIFTFILE_IS_STDLIB OR SWIFTFILE_IS_SDK_OVERLAY) list(APPEND swift_flags "-runtime-compatibility-version" "none") @@ -640,6 +640,7 @@ function(_compile_swift_files list(APPEND swift_flags "-enable-experimental-feature" "LifetimeDependence") list(APPEND swift_flags "-enable-experimental-feature" "InoutLifetimeDependence") list(APPEND swift_flags "-enable-experimental-feature" "LifetimeDependenceMutableAccessors") + list(APPEND swift_flags "-enable-experimental-feature" "Lifetimes") list(APPEND swift_flags "-enable-upcoming-feature" "MemberImportVisibility") diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index a590a5ba2782c..6346a7658f712 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -498,7 +498,7 @@ extension Array: _ArrayProtocol { @inlinable // FIXME(inline-always) @inline(__always) get { - return _buffer.owner + return _buffer.owner } } @@ -814,7 +814,7 @@ extension Array: RandomAccessCollection, MutableCollection { } } } - + /// The number of elements in the array. @inlinable @_semantics("array.get_count") @@ -1249,14 +1249,14 @@ extension Array: RangeReplaceableCollection { let oldCount = _buffer.mutableCount let startNewElements = unsafe _buffer.mutableFirstElementAddress + oldCount let buf = unsafe UnsafeMutableBufferPointer( - start: startNewElements, + start: startNewElements, count: _buffer.mutableCapacity - oldCount) var (remainder,writtenUpTo) = unsafe buf.initialize(from: newElements) - + // trap on underflow from the sequence's underestimate: let writtenCount = unsafe buf.distance(from: buf.startIndex, to: writtenUpTo) - _precondition(newElementsCount <= writtenCount, + _precondition(newElementsCount <= writtenCount, "newElements.underestimatedCount was an overestimate") // can't check for overflow as sequences can underestimate @@ -1440,7 +1440,7 @@ extension Array: RangeReplaceableCollection { return try unsafe body(bufferPointer) } } - + @inlinable public __consuming func _copyToContiguousArray() -> ContiguousArray { if let n = _buffer.requestNativeBuffer() { @@ -1799,7 +1799,7 @@ extension Array { // a precondition and Array never lies about its count. guard var p = buffer.baseAddress else { _preconditionFailure("Attempt to copy contents into nil buffer pointer") } - _precondition(self.count <= buffer.count, + _precondition(self.count <= buffer.count, "Insufficient space allocated to copy array contents") if let s = unsafe _baseAddressIfContiguous { @@ -2160,3 +2160,25 @@ internal struct _ArrayAnyHashableBox } extension Array: @unchecked Sendable where Element: Sendable { } + +// FIXME: This should be accepted, but we get the following: +// error: type 'Array' does not conform to protocol 'Iterable' +// note: multiple matching functions named '_customContainsEquatableElement' with type '(borrowing Array.Element) -> Bool?' (aka '(borrowing Element) -> Optional') + +// @available(SwiftStdlib 6.3, *) +// extension Array: Iterable { +// public typealias BorrowIterator = Span.BorrowIterator + +// @_alwaysEmitIntoClient +// @_transparent +// public var estimatedCount: EstimatedCount { +// .exactly(count) +// } + +// @_alwaysEmitIntoClient +// @_lifetime(borrow self) +// @_transparent +// public func startBorrowIteration() -> Span { +// self.span +// } +// } diff --git a/stdlib/public/core/BorrowIteratorProtocol.swift b/stdlib/public/core/BorrowIteratorProtocol.swift new file mode 100644 index 0000000000000..8a585b98abc21 --- /dev/null +++ b/stdlib/public/core/BorrowIteratorProtocol.swift @@ -0,0 +1,185 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +@available(SwiftStdlib 6.3, *) +public protocol BorrowIteratorProtocol: ~Copyable, ~Escapable { + associatedtype Element: ~Copyable + + // FIXME: This ought to be a core requirement, but `Ref` is not a thing yet. +// @_lifetime(&self) +// @_lifetime(self: copy self) +// mutating func next() -> Ref? + + /// Advance the iterator, returning an ephemeral span over the elements + /// that are ready to be visited. + /// + /// If the underlying iterable is a container type, then the returned span + /// typically directly addresses one of its storage buffers. On the other + /// hand, if the underlying iterable materializes its elements on demand, + /// then the returned span addresses some temporary buffer associated with + /// the iterator itself. Consequently, the returned span is tied to this + /// particular invocation of `nextSpan`, and it cannot survive until the next + /// invocation of it. + /// + /// If the iterator has not yet reached the end of the underlying iterable, + /// then this method returns a non-empty span of at most `maximumCount` + /// elements, and updates the iterator's current position to the element + /// following the last item in the returned span (or the end, if there is + /// none). The `maximumCount` argument allows callers to avoid getting more + /// items that they are able to process in one go, simplifying usage, and + /// avoiding materializing more elements than needed. + /// + /// If the iterator's current position is at the end of the container, then + /// this method returns an empty span without updating the position. + /// + /// This method can be used to efficiently process the items of a container + /// in bulk, by directly iterating over its piecewise contiguous pieces of + /// storage: + /// + /// var it = items.startBorrowIteration() + /// while true { + /// let span = it.nextSpan(after: &index) + /// if span.isEmpty { break } + /// // Process items in `span` + /// } + /// + /// Note: The spans returned by this method are not guaranteed to be disjunct. + /// Iterators that materialize elements on demand typically reuse the same + /// buffer over and over again; and even some proper containers may link to a + /// single storage chunk (or parts of a storage chunk) multiple times, for + /// example to repeat their contents. + /// + /// Note: Repeatedly iterating over the same container is expected to return + /// the same items (collected in similarly sized span instances), but the + /// returned spans are not guaranteed to be identical. For example, this is + /// the case with containers that store some of their contents within their + /// direct representation. Such containers may not always have a unique + /// address in memory, and so the locations of the spans exposed by this + /// method may vary between different borrows of the same container.) + @_lifetime(&self) + @_lifetime(self: copy self) + mutating func nextSpan(maximumCount: Int) -> Span + + /// Advance the position of this iterator by the specified offset, or until + /// the end of the underlying iterable. + /// + /// Returns the number of items that were skipped. If this is less than + /// `maximumOffset`, then the iterable did not have enough elements left to + /// skip the requested number of items. In this case, the iterator's current + /// position is set to the end of the iterable. + /// + /// `maximumOffset` must be nonnegative, unless this is a bidirectional + /// or random-access iterator. + @_lifetime(self: copy self) + mutating func skip(by maximumOffset: Int) -> Int + + // FIXME: Add BidirectionalBorrowIteratorProtocol and RandomAccessBorrowIteratorProtocol. + // BidirectionalBorrowIteratorProtocol would need to have a `previousSpan` + // method, which considerably complicates implementation. + // Perhaps these would be better left to as variants of protocol Container, + // which do not need a separate iterator concept. +} + +@available(SwiftStdlib 6.3, *) +extension BorrowIteratorProtocol where Self: ~Copyable & ~Escapable { + @_alwaysEmitIntoClient + @_lifetime(&self) + @_lifetime(self: copy self) + @_transparent + public mutating func nextSpan() -> Span { + nextSpan(maximumCount: Int.max) + } +} + +@available(SwiftStdlib 6.3, *) +extension BorrowIteratorProtocol where Self: ~Copyable & ~Escapable { + @_alwaysEmitIntoClient + @_lifetime(self: copy self) + public mutating func skip(by offset: Int) -> Int { + var remainder = offset + while remainder > 0 { + let span = nextSpan(maximumCount: remainder) + if span.isEmpty { break } + remainder &-= span.count + } + return offset &- remainder + } +} + +@available(SwiftStdlib 6.3, *) +extension Span: Iterable where Element: ~Copyable { + // FIXME: This simple definition cannot also be a backward (or bidirectional) + // iterator, nor a random-access iterator. If we want to go in that direction, + // we'll need to rather introduce a type more like `RigidArray.BorrowIterator`. + public typealias BorrowIterator = Self + + @_alwaysEmitIntoClient + @_transparent + public var estimatedCount: EstimatedCount { + .exactly(count) + } + + @_alwaysEmitIntoClient + @_lifetime(copy self) + @_transparent + public func startBorrowIteration() -> Span { + self + } +} + +@available(SwiftStdlib 6.3, *) +extension Span: BorrowIteratorProtocol where Element: ~Copyable { + @_alwaysEmitIntoClient + @_lifetime(&self) + @_lifetime(self: copy self) + public mutating func nextSpan(maximumCount: Int) -> Span { + let result = extracting(first: maximumCount) + self = extracting(droppingFirst: maximumCount) + return result + } +} + +@available(SwiftStdlib 6.3, *) +extension MutableSpan: Iterable where Element: ~Copyable { + public typealias BorrowIterator = Span.BorrowIterator + + @_alwaysEmitIntoClient + @_transparent + public var estimatedCount: EstimatedCount { + .exactly(count) + } + + @_alwaysEmitIntoClient + @_lifetime(borrow self) + @_transparent + public func startBorrowIteration() -> Span { + span + } +} + +@available(SwiftStdlib 6.3, *) +extension OutputSpan: Iterable where Element: ~Copyable { + public typealias BorrowIterator = Span.BorrowIterator + + @_alwaysEmitIntoClient + @_transparent + public var estimatedCount: EstimatedCount { + .exactly(count) + } + + @_alwaysEmitIntoClient + @_lifetime(borrow self) + @_transparent + public func startBorrowIteration() -> Span { + self.span + } +} diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 935c86843bedc..fc574b36436ec 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -45,6 +45,7 @@ split_embedded_sources( EMBEDDED BidirectionalCollection.swift EMBEDDED Bitset.swift EMBEDDED Bool.swift + EMBEDDED BorrowIteratorProtocol.swift NORMAL BridgeObjectiveC.swift EMBEDDED BridgeStorage.swift NORMAL BridgingBuffer.swift @@ -98,6 +99,7 @@ split_embedded_sources( EMBEDDED InputStream.swift EMBEDDED IntegerParsing.swift EMBEDDED Integers.swift + EMBEDDED Iterable.swift EMBEDDED Join.swift EMBEDDED KeyPath.swift EMBEDDED KeyValuePairs.swift @@ -275,7 +277,7 @@ if(SWIFT_STDLIB_ENABLE_VECTOR_TYPES) split_embedded_sources( OUT_LIST_EMBEDDED SWIFTLIB_EMBEDDED_VECTOR_GYB_SOURCES OUT_LIST_NORMAL SWIFTLIB_VECTOR_GYB_SOURCES - + EMBEDDED SIMDIntegerConcreteOperations.swift.gyb EMBEDDED SIMDFloatConcreteOperations.swift.gyb EMBEDDED SIMDMaskConcreteOperations.swift.gyb @@ -530,7 +532,7 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB) list(GET list 0 arch) list(GET list 1 mod) list(GET list 2 triple) - + set(SWIFT_SDK_embedded_ARCH_${arch}_MODULE "${mod}") set(SWIFT_SDK_embedded_LIB_SUBDIR "embedded") set(SWIFT_SDK_embedded_ARCH_${arch}_TRIPLE "${triple}") diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index 0c85960838b5b..2e021cc6637f4 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -211,7 +211,7 @@ "RawSpan.swift", "Span.swift" ], - "UTF8Span": [ + "UTF8Span": [ "UTF8EncodingError.swift", "UTF8Span.swift", "UTF8SpanBits.swift", @@ -276,7 +276,9 @@ "EmbeddedStubs.swift", "EmbeddedPrint.swift", "InlineArray.swift", - "_InlineArray.swift" + "_InlineArray.swift", + "Iterable.swift", + "BorrowIteratorProtocol.swift" ], "Result": [ "Result.swift" diff --git a/stdlib/public/core/Iterable.swift b/stdlib/public/core/Iterable.swift new file mode 100644 index 0000000000000..0b020293dc3f8 --- /dev/null +++ b/stdlib/public/core/Iterable.swift @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +@available(SwiftStdlib 6.3, *) +@frozen +public enum EstimatedCount { + case infinite + case exactly(Int) + case unknown +} + +@available(SwiftStdlib 6.3, *) +public protocol Iterable: ~Copyable, ~Escapable { + associatedtype Element: ~Copyable + associatedtype BorrowIterator: BorrowIteratorProtocol & ~Copyable & ~Escapable + + var isEmpty: Bool { get } + + var estimatedCount: EstimatedCount { get } + + @_lifetime(borrow self) + borrowing func startBorrowIteration() -> BorrowIterator + + func _customContainsEquatableElement( + _ element: borrowing Element + ) -> Bool? +} + +@available(SwiftStdlib 6.3, *) +extension Iterable where Self: ~Copyable & ~Escapable { + @_alwaysEmitIntoClient + @_transparent + public var underestimatedCount: Int { + switch estimatedCount { + case .infinite: + .max + case .exactly(let c): + c + case .unknown: + 0 + } + } + + @_alwaysEmitIntoClient + @_transparent + public func _customContainsEquatableElement(_ element: borrowing Element) -> Bool? { + nil + } +} From ff2e38733c1bd0016cf9f5d21bff86255deefb6e Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 3 Nov 2025 14:26:37 -0800 Subject: [PATCH 2/7] Add InlineArray Iterable conformance --- stdlib/public/core/InlineArray.swift | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/InlineArray.swift b/stdlib/public/core/InlineArray.swift index af4b93e337590..833d62ccd0240 100644 --- a/stdlib/public/core/InlineArray.swift +++ b/stdlib/public/core/InlineArray.swift @@ -30,7 +30,7 @@ /// let b: InlineArray<_, Int> = [1, 2, 3] /// let c: InlineArray<3, _> = [1, 2, 3] /// let d: InlineArray = [1, 2, 3] -/// +/// /// let e: [3 of Int] = [1, 2, 3] /// let f: [_ of Int] = [1, 2, 3] /// let g: [3 of _] = [1, 2, 3] @@ -606,3 +606,21 @@ extension InlineArray where Element: ~Copyable { } } } + +@available(SwiftStdlib 6.3, *) +extension InlineArray: Iterable where Element: ~Copyable { + public typealias BorrowIterator = Span.BorrowIterator + + @_alwaysEmitIntoClient + @_transparent + public var estimatedCount: EstimatedCount { + .exactly(count) + } + + @_alwaysEmitIntoClient + @_lifetime(borrow self) + @_transparent + public func startBorrowIteration() -> Span { + span + } +} From a9ff08d9f8df6c00bce42cd1ac7699bf744462c8 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 21 Nov 2025 11:53:29 -0600 Subject: [PATCH 3/7] Generalize Sequence to be ~Copyable & ~Escapable This change allows noncopyable and nonescapable Sequence types, with room for generalizing the Element type to be ~Copyable in the future. The new prototype adds a BorrowingIterator associated type to Sequence This commit adds a BorrowingIteratorProtocol for generalized bulk iteration, a BorrowingIteratorAdapter as an implementation of the new Sequence requirement for existing sequences, and a NeverIterator for new noncopyable/nonescapable sequence types. Conformance to Sequence for Span and InlineArray is also included. Issues: - Availability checking must be off due to the availability requirements on the new Sequence.BorrowingIterator associated type - Several existing sequences needed an explicit Element type alias to compile (note that this was during experimentation, so it may be possible to back out these changes) - Some of the default implementations for generalized sequences simply fatal error - InlineArray is a tricky case, due to its conditionally copyable design. It conforms to Sequence unconditionally, so it needs an unconditional implementation of (the original) Iterator associated type. However, when Sequence can use ~Copyable elements, that implementation won't be workable, since (a) it copies the inline array, and (b) it implements the `next() -> Element?` method, which can't return a noncopyable element. --- benchmark/single-source/DataBenchmarks.swift | 8 +- .../MinimalCollections.swift | 2 + .../StdlibUnittest/StdlibUnittest.swift | 5 +- .../SwiftReflectionTest.swift | 4 + .../Concurrency/Deque/Deque+Collection.swift | 2 + stdlib/public/RuntimeModule/Base64.swift | 2 + stdlib/public/RuntimeModule/Elf.swift | 2 + .../RuntimeModule/FramePointerUnwinder.swift | 1 + stdlib/public/StringProcessing/CMakeLists.txt | 1 + ....swift => BorrowingIteratorProtocol.swift} | 136 ++++++++++++------ stdlib/public/core/CMakeLists.txt | 3 +- stdlib/public/core/GroupInfo.json | 2 +- stdlib/public/core/InlineArray.swift | 34 ++--- stdlib/public/core/Iterable.swift | 2 +- stdlib/public/core/Optional.swift | 19 +++ stdlib/public/core/Sequence.swift | 86 ++++++++++- test/stdlib/Span/SpanTests.swift | 55 +++++++ 17 files changed, 293 insertions(+), 71 deletions(-) rename stdlib/public/core/{BorrowIteratorProtocol.swift => BorrowingIteratorProtocol.swift} (65%) diff --git a/benchmark/single-source/DataBenchmarks.swift b/benchmark/single-source/DataBenchmarks.swift index 23e15037bc580..f07a357f41950 100644 --- a/benchmark/single-source/DataBenchmarks.swift +++ b/benchmark/single-source/DataBenchmarks.swift @@ -327,6 +327,9 @@ let large = sampleData(.large) let array809 = byteArray(size: 809) struct Count0 : Sequence { + @available(macOS 9999, *) + public typealias BorrowingIterator = BorrowingIteratorAdapter + let base: S init (_ base:S) { self.base = base } func makeIterator() -> S.Iterator { return base.makeIterator() } @@ -334,7 +337,10 @@ struct Count0 : Sequence { } struct Bytes: Sequence, IteratorProtocol { - let count: Int + @available(macOS 9999, *) + public typealias BorrowingIterator = BorrowingIteratorAdapter + + let count: Int let exact: Bool var i: Int = 0 init(count: Int, exact: Bool) { diff --git a/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift b/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift index 019f26e376576..82e747b00708f 100644 --- a/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift +++ b/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift @@ -83,6 +83,8 @@ public enum UnderestimatedCountBehavior { /// /// This sequence is consumed when its generator is advanced. public struct MinimalSequence : Sequence, CustomDebugStringConvertible { + public typealias Element = T + public init( elements: S, underestimatedCount: UnderestimatedCountBehavior = .value(0) diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index 681abf91fecd1..e499d17fe81e1 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -2241,7 +2241,8 @@ public enum StdLibVersion: String { case stdlib_6_0 = "6.0" case stdlib_6_1 = "6.1" case stdlib_6_2 = "6.2" - + case stdlib_6_3 = "6.3" + var isAvailable: Bool { switch self { case .stdlib_5_7: @@ -2258,6 +2259,8 @@ public enum StdLibVersion: String { return if #available(SwiftStdlib 6.1, *) { true } else { false } case .stdlib_6_2: return if #available(SwiftStdlib 6.2, *) { true } else { false } + case .stdlib_6_3: + return if #available(SwiftStdlib 6.3, *) { true } else { false } } } } diff --git a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift index 5ba93bd7a67f0..b3cbc35f863d9 100644 --- a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift +++ b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift @@ -226,6 +226,10 @@ internal struct Section { /// Holds the addresses and sizes of sections related to reflection. internal struct ReflectionInfo : Sequence { + internal typealias Element = Section? + @available(macOS 9999, *) + internal typealias BorrowingIterator = BorrowingIteratorAdapter> + /// The name of the loaded image. internal let imageName: String diff --git a/stdlib/public/Concurrency/Deque/Deque+Collection.swift b/stdlib/public/Concurrency/Deque/Deque+Collection.swift index a40b41823dc2d..144370b6845ce 100644 --- a/stdlib/public/Concurrency/Deque/Deque+Collection.swift +++ b/stdlib/public/Concurrency/Deque/Deque+Collection.swift @@ -19,6 +19,8 @@ extension _Deque: Sequence { // This custom implementation performs direct storage access to eliminate any // and all index validation overhead. It also optimizes away repeated // conversions from indices to storage slots. + @available(macOS 9999, *) + internal typealias BorrowingIterator = BorrowingIteratorAdapter /// An iterator over the members of a deque. struct Iterator: IteratorProtocol { diff --git a/stdlib/public/RuntimeModule/Base64.swift b/stdlib/public/RuntimeModule/Base64.swift index 608ce60eb9947..bc32a9298edd8 100644 --- a/stdlib/public/RuntimeModule/Base64.swift +++ b/stdlib/public/RuntimeModule/Base64.swift @@ -217,6 +217,8 @@ public struct Base64Encoder: Sequence public struct Base64Decoder: Sequence where S.Element == UTF8.CodeUnit { + @available(macOS 9999, *) + public typealias BorrowingIterator = BorrowingIteratorAdapter public typealias Element = UInt8 var source: S diff --git a/stdlib/public/RuntimeModule/Elf.swift b/stdlib/public/RuntimeModule/Elf.swift index 96d57264eae22..98abbd0f6cb92 100644 --- a/stdlib/public/RuntimeModule/Elf.swift +++ b/stdlib/public/RuntimeModule/Elf.swift @@ -1266,6 +1266,8 @@ final class ElfImage } struct Notes: Sequence { + typealias Element = Note + var image: ElfImage struct NoteIterator: IteratorProtocol { diff --git a/stdlib/public/RuntimeModule/FramePointerUnwinder.swift b/stdlib/public/RuntimeModule/FramePointerUnwinder.swift index c5506ca9c902f..b2449ad0794df 100644 --- a/stdlib/public/RuntimeModule/FramePointerUnwinder.swift +++ b/stdlib/public/RuntimeModule/FramePointerUnwinder.swift @@ -21,6 +21,7 @@ public struct FramePointerUnwinder: Sequence, Itera public typealias Context = C public typealias MemoryReader = M public typealias Address = Context.Address + public typealias Element = RichFrame
var pc: Address var fp: Address diff --git a/stdlib/public/StringProcessing/CMakeLists.txt b/stdlib/public/StringProcessing/CMakeLists.txt index c7ad4292e2afb..bb4e1714bc4fc 100644 --- a/stdlib/public/StringProcessing/CMakeLists.txt +++ b/stdlib/public/StringProcessing/CMakeLists.txt @@ -20,6 +20,7 @@ set(swift_string_processing_compile_flags) # will be built with library evolution. list(APPEND swift_string_processing_compile_flags "-DRESILIENT_LIBRARIES") +list(APPEND swift_string_processing_compile_flags "-Xfrontend" "-disable-availability-checking") if(SWIFT_BUILD_STATIC_STDLIB) # Explicitly autolink swift_RegexParser because it's imported with @_implementationOnly diff --git a/stdlib/public/core/BorrowIteratorProtocol.swift b/stdlib/public/core/BorrowingIteratorProtocol.swift similarity index 65% rename from stdlib/public/core/BorrowIteratorProtocol.swift rename to stdlib/public/core/BorrowingIteratorProtocol.swift index 8a585b98abc21..3cee51968d04d 100644 --- a/stdlib/public/core/BorrowIteratorProtocol.swift +++ b/stdlib/public/core/BorrowingIteratorProtocol.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// @available(SwiftStdlib 6.3, *) -public protocol BorrowIteratorProtocol: ~Copyable, ~Escapable { +public protocol BorrowingIteratorProtocol: ~Copyable, ~Escapable { associatedtype Element: ~Copyable // FIXME: This ought to be a core requirement, but `Ref` is not a thing yet. @@ -90,7 +90,7 @@ public protocol BorrowIteratorProtocol: ~Copyable, ~Escapable { } @available(SwiftStdlib 6.3, *) -extension BorrowIteratorProtocol where Self: ~Copyable & ~Escapable { +extension BorrowingIteratorProtocol where Self: ~Copyable & ~Escapable { @_alwaysEmitIntoClient @_lifetime(&self) @_lifetime(self: copy self) @@ -101,7 +101,7 @@ extension BorrowIteratorProtocol where Self: ~Copyable & ~Escapable { } @available(SwiftStdlib 6.3, *) -extension BorrowIteratorProtocol where Self: ~Copyable & ~Escapable { +extension BorrowingIteratorProtocol where Self: ~Copyable & ~Escapable { @_alwaysEmitIntoClient @_lifetime(self: copy self) public mutating func skip(by offset: Int) -> Int { @@ -115,29 +115,29 @@ extension BorrowIteratorProtocol where Self: ~Copyable & ~Escapable { } } -@available(SwiftStdlib 6.3, *) -extension Span: Iterable where Element: ~Copyable { - // FIXME: This simple definition cannot also be a backward (or bidirectional) - // iterator, nor a random-access iterator. If we want to go in that direction, - // we'll need to rather introduce a type more like `RigidArray.BorrowIterator`. - public typealias BorrowIterator = Self - - @_alwaysEmitIntoClient - @_transparent - public var estimatedCount: EstimatedCount { - .exactly(count) - } - - @_alwaysEmitIntoClient - @_lifetime(copy self) - @_transparent - public func startBorrowIteration() -> Span { - self - } -} +//@available(SwiftStdlib 6.3, *) +//extension Span: Iterable where Element: ~Copyable { +// // FIXME: This simple definition cannot also be a backward (or bidirectional) +// // iterator, nor a random-access iterator. If we want to go in that direction, +// // we'll need to rather introduce a type more like `RigidArray.BorrowIterator`. +// public typealias BorrowIterator = Self +// +// @_alwaysEmitIntoClient +// @_transparent +// public var estimatedCount: EstimatedCount { +// .exactly(count) +// } +// +// @_alwaysEmitIntoClient +// @_lifetime(copy self) +// @_transparent +// public func startBorrowIteration() -> Span { +// self +// } +//} @available(SwiftStdlib 6.3, *) -extension Span: BorrowIteratorProtocol where Element: ~Copyable { +extension Span: BorrowingIteratorProtocol where Element: ~Copyable { @_alwaysEmitIntoClient @_lifetime(&self) @_lifetime(self: copy self) @@ -149,37 +149,79 @@ extension Span: BorrowIteratorProtocol where Element: ~Copyable { } @available(SwiftStdlib 6.3, *) -extension MutableSpan: Iterable where Element: ~Copyable { - public typealias BorrowIterator = Span.BorrowIterator - - @_alwaysEmitIntoClient - @_transparent - public var estimatedCount: EstimatedCount { - .exactly(count) - } - +extension Span: Sequence /* where Element: ~Copyable */ { + public typealias Iterator = NeverIterator + @_alwaysEmitIntoClient @_lifetime(borrow self) - @_transparent - public func startBorrowIteration() -> Span { - span + public func makeBorrowingIterator() -> Span { + self } } -@available(SwiftStdlib 6.3, *) -extension OutputSpan: Iterable where Element: ~Copyable { - public typealias BorrowIterator = Span.BorrowIterator - - @_alwaysEmitIntoClient - @_transparent - public var estimatedCount: EstimatedCount { - .exactly(count) +extension InlineArray: Sequence /* where Element: ~Copyable */ { + public typealias Element = Element +// public typealias Iterator = NeverIterator + public typealias BorrowingIterator = Span + + // This will NOT work once `Element: ~Copyable` is in the works + // Problematic because we can only conform to `Sequence` once, so we + // need to provide a single iterator type, but it can't handle both + // ~Copyable and copyable elements (I think) + public struct Iterator: IteratorProtocol { + var array: [count of Element] + var index: Int = 0 + + public mutating func next() -> Element? { + guard index < array.count else { return nil } + defer { index &+= 1 } + return array[index] + } } - + + public func makeIterator() -> Iterator { + .init(array: self) + } + @_alwaysEmitIntoClient @_lifetime(borrow self) - @_transparent - public func startBorrowIteration() -> Span { + public func makeBorrowingIterator() -> Span { self.span } } + +//@available(SwiftStdlib 6.3, *) +//extension MutableSpan: Iterable where Element: ~Copyable { +// public typealias BorrowIterator = Span.BorrowIterator +// +// @_alwaysEmitIntoClient +// @_transparent +// public var estimatedCount: EstimatedCount { +// .exactly(count) +// } +// +// @_alwaysEmitIntoClient +// @_lifetime(borrow self) +// @_transparent +// public func startBorrowIteration() -> Span { +// span +// } +//} +// +//@available(SwiftStdlib 6.3, *) +//extension OutputSpan: Iterable where Element: ~Copyable { +// public typealias BorrowIterator = Span.BorrowIterator +// +// @_alwaysEmitIntoClient +// @_transparent +// public var estimatedCount: EstimatedCount { +// .exactly(count) +// } +// +// @_alwaysEmitIntoClient +// @_lifetime(borrow self) +// @_transparent +// public func startBorrowIteration() -> Span { +// self.span +// } +//} diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 11d35f5699382..95e987e32d7ef 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -45,7 +45,7 @@ split_embedded_sources( EMBEDDED BidirectionalCollection.swift EMBEDDED Bitset.swift EMBEDDED Bool.swift - EMBEDDED BorrowIteratorProtocol.swift + EMBEDDED BorrowingIteratorProtocol.swift NORMAL BridgeObjectiveC.swift EMBEDDED BridgeStorage.swift NORMAL BridgingBuffer.swift @@ -340,6 +340,7 @@ endif() # STAGING: Temporarily avoids having to write #fileID in Swift.swiftinterface. list(APPEND swift_stdlib_compile_flags "-Xfrontend" "-enable-experimental-concise-pound-file") +list(APPEND swift_stdlib_compile_flags "-Xfrontend" "-disable-availability-checking") list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "Macros") list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "FreestandingMacros") diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index 2e021cc6637f4..504e4e9ed9666 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -278,7 +278,7 @@ "InlineArray.swift", "_InlineArray.swift", "Iterable.swift", - "BorrowIteratorProtocol.swift" + "BorrowingIteratorProtocol.swift" ], "Result": [ "Result.swift" diff --git a/stdlib/public/core/InlineArray.swift b/stdlib/public/core/InlineArray.swift index 5ab45e69d92df..74c7d077cefd0 100644 --- a/stdlib/public/core/InlineArray.swift +++ b/stdlib/public/core/InlineArray.swift @@ -615,20 +615,20 @@ extension InlineArray where Element: ~Copyable { } } -@available(SwiftStdlib 6.3, *) -extension InlineArray: Iterable where Element: ~Copyable { - public typealias BorrowIterator = Span.BorrowIterator - - @_alwaysEmitIntoClient - @_transparent - public var estimatedCount: EstimatedCount { - .exactly(count) - } - - @_alwaysEmitIntoClient - @_lifetime(borrow self) - @_transparent - public func startBorrowIteration() -> Span { - span - } -} +//@available(SwiftStdlib 6.3, *) +//extension InlineArray: Iterable where Element: ~Copyable { +// public typealias BorrowIterator = Span.BorrowIterator +// +// @_alwaysEmitIntoClient +// @_transparent +// public var estimatedCount: EstimatedCount { +// .exactly(count) +// } +// +// @_alwaysEmitIntoClient +// @_lifetime(borrow self) +// @_transparent +// public func startBorrowIteration() -> Span { +// span +// } +//} diff --git a/stdlib/public/core/Iterable.swift b/stdlib/public/core/Iterable.swift index 0b020293dc3f8..9d9a6643dfbbe 100644 --- a/stdlib/public/core/Iterable.swift +++ b/stdlib/public/core/Iterable.swift @@ -21,7 +21,7 @@ public enum EstimatedCount { @available(SwiftStdlib 6.3, *) public protocol Iterable: ~Copyable, ~Escapable { associatedtype Element: ~Copyable - associatedtype BorrowIterator: BorrowIteratorProtocol & ~Copyable & ~Escapable + associatedtype BorrowIterator: BorrowingIteratorProtocol & ~Copyable & ~Escapable var isEmpty: Bool { get } diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index f7614db294703..a4006055500cb 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -1016,3 +1016,22 @@ extension Optional: _ObjectiveCBridgeable { } } #endif + +extension Optional where Wrapped: ~Copyable { + @available(SwiftStdlib 6.3, *) + @_alwaysEmitIntoClient + public var _span: Span { + @_addressableSelf + @lifetime(borrow self) + get { + guard self != nil else { + return Span() + } + + let ptr = Builtin.unprotectedAddressOfBorrow(self) + return unsafe _overrideLifetime( + Span(_unsafeStart: UnsafePointer(ptr), count: 1), + borrowing: self) + } + } +} diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index 5ae2e1addeaaa..313d611c08ee5 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -322,14 +322,17 @@ public protocol IteratorProtocol { /// makes no other requirements about element access, so routines that /// traverse a sequence should be considered O(*n*) unless documented /// otherwise. -public protocol Sequence { +public protocol Sequence: ~Copyable, ~Escapable { /// A type representing the sequence's elements. associatedtype Element /// A type that provides the sequence's iteration interface and /// encapsulates its iteration state. - associatedtype Iterator: IteratorProtocol where Iterator.Element == Element + associatedtype Iterator: IteratorProtocol + @available(SwiftStdlib 6.3, *) + associatedtype BorrowingIterator: BorrowingIteratorProtocol & ~Copyable & ~Escapable = BorrowingIteratorAdapter + // FIXME: // This typealias should be removed as it predates the source compatibility // guarantees of Swift 3, but it cannot due to a bug. @@ -345,6 +348,10 @@ public protocol Sequence { /// Returns an iterator over the elements of this sequence. __consuming func makeIterator() -> Iterator + @available(SwiftStdlib 6.3, *) + @lifetime(borrow self) + func makeBorrowingIterator() -> BorrowingIterator + /// A value less than or equal to the number of elements in the sequence, /// calculated nondestructively. /// @@ -451,6 +458,47 @@ public protocol Sequence { ) rethrows -> R? } +@available(SwiftStdlib 6.3, *) +public struct BorrowingIteratorAdapter: /* & ~Copyable & ~Escapable */ + BorrowingIteratorProtocol where Iterator.Element: Copyable +{ + var iterator: Iterator + var curValue: Iterator.Element? + + public typealias Element = Iterator.Element + + @lifetime(&self) + public mutating func nextSpan(maximumCount: Int) -> Span { + curValue = iterator.next() + return curValue._span + } +} + +@available(SwiftStdlib 6.3, *) +public enum NeverIterator {} + +@available(SwiftStdlib 6.3, *) +extension NeverIterator: IteratorProtocol { /* where Element: ~Copyable */ + public typealias Element = Element + public func next() -> Element? { + Builtin.unreachable() + } +} + +@available(SwiftStdlib 6.3, *) +extension Sequence where BorrowingIterator == BorrowingIteratorAdapter { + public func makeBorrowingIterator() -> BorrowingIterator { + BorrowingIteratorAdapter(iterator: makeIterator()) + } +} + +@available(SwiftStdlib 6.3, *) +extension Sequence where Iterator == NeverIterator, Self: ~Copyable & ~Escapable { + public func makeIterator() -> Iterator { + fatalError("Attempt to iterate using a NeverIterator") + } +} + // Provides a default associated type witness for Iterator when the // Self type is both a Sequence and an Iterator. extension Sequence where Self: IteratorProtocol { @@ -663,6 +711,40 @@ extension DropWhileSequence: Sequence { // Default implementations for Sequence //===----------------------------------------------------------------------===// +extension Sequence where Self: ~Copyable & ~Escapable { + @inlinable + public var underestimatedCount: Int { + return 0 + } + + @inlinable + @inline(__always) + public func _customContainsEquatableElement( + _ element: Iterator.Element + ) -> Bool? { + return nil + } + + @inlinable + @safe + public func withContiguousStorageIfAvailable( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + return nil + } + + public __consuming func _copyContents( + initializing buffer: UnsafeMutableBufferPointer + ) -> (Iterator, UnsafeMutableBufferPointer.Index) { + fatalError("_copyContents on a ~Copyable/~Copyable sequence") + } + + @inlinable + public __consuming func _copyToContiguousArray() -> ContiguousArray { + fatalError("_copyToContiguousArray on a ~Copyable/~Escapable sequence") + } +} + extension Sequence { /// Returns an array containing the results of mapping the given closure /// over the sequence's elements. diff --git a/test/stdlib/Span/SpanTests.swift b/test/stdlib/Span/SpanTests.swift index c5e073f636cf0..a3274f58f2e43 100644 --- a/test/stdlib/Span/SpanTests.swift +++ b/test/stdlib/Span/SpanTests.swift @@ -607,3 +607,58 @@ suite.test("Span Sendability") let span = Span(_unsafeElements: buffer) send(span) } + +@available(SwiftStdlib 6.3, *) +extension Sequence where Self: ~Copyable & ~Escapable { // Copyable elements only + func collectViaBorrowing() -> [Element] { + var borrowIterator = makeBorrowingIterator() + var result: [Element] = [] + while true { + let span = borrowIterator.nextSpan(maximumCount: .max) + if span.isEmpty { break } + for i in span.indices { + result.append(span[i]) + } + } + return result + } +} + +@available(SwiftStdlib 6.3, *) +func elementsEqual( + _ lhs: borrowing S1, + _ rhs: borrowing S2 +) -> Bool + where S1.Element: Equatable, S2.Element == S1.Element, + S1: ~Escapable & ~Copyable, S2: ~Escapable & ~Copyable +{ + var iter1 = lhs.makeBorrowingIterator() + var iter2 = rhs.makeBorrowingIterator() + while true { + let el1 = iter1.nextSpan(maximumCount: 1) + let el2 = iter2.nextSpan(maximumCount: 1) + if el1.isEmpty && el2.isEmpty { return true } + if el1.isEmpty || el2.isEmpty { return false } + if el1[0] != el2[0] { return false } + } +} + +suite.test("BORROWING") +.require(.stdlib_6_3).code { + guard #available(SwiftStdlib 6.3, *) else { + expectTrue(false) + return + } + + let array = [1, 2, 3, 4, 5, 6, 7, 8] + let arrayCollected = array.collectViaBorrowing() + expectEqual(array, arrayCollected) + + let span = array.span + let spanCollected = span.collectViaBorrowing() + expectTrue(elementsEqual(span, spanCollected)) + + let inline: [8 of Int] = [1, 2, 3, 4, 5, 6, 7, 8] + let inlineCollected = inline.collectViaBorrowing() + expectTrue(elementsEqual(inline, inlineCollected)) +} From b0946b550ce1a52d0451e8429de40fbd587dffec Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Sun, 23 Nov 2025 14:59:22 -0800 Subject: [PATCH 4/7] Mark borrowing iterator types frozen and methods transparent --- stdlib/public/core/Optional.swift | 2 +- stdlib/public/core/Sequence.swift | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index a4006055500cb..9205701e21c66 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -1019,7 +1019,7 @@ extension Optional: _ObjectiveCBridgeable { extension Optional where Wrapped: ~Copyable { @available(SwiftStdlib 6.3, *) - @_alwaysEmitIntoClient + @_transparent public var _span: Span { @_addressableSelf @lifetime(borrow self) diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index 313d611c08ee5..c796fe52d8005 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -459,14 +459,24 @@ public protocol Sequence: ~Copyable, ~Escapable { } @available(SwiftStdlib 6.3, *) +@frozen public struct BorrowingIteratorAdapter: /* & ~Copyable & ~Escapable */ BorrowingIteratorProtocol where Iterator.Element: Copyable { + @usableFromInline var iterator: Iterator + @usableFromInline var curValue: Iterator.Element? - + public typealias Element = Iterator.Element + @_transparent + public init(iterator: Iterator) { + self.iterator = iterator + curValue = nil + } + + @_transparent @lifetime(&self) public mutating func nextSpan(maximumCount: Int) -> Span { curValue = iterator.next() @@ -487,6 +497,7 @@ extension NeverIterator: IteratorProtocol { /* where Element: ~Copyable */ @available(SwiftStdlib 6.3, *) extension Sequence where BorrowingIterator == BorrowingIteratorAdapter { + @_transparent public func makeBorrowingIterator() -> BorrowingIterator { BorrowingIteratorAdapter(iterator: makeIterator()) } From f02c4d6f42e18ba42cda2657541a9f18282ba378 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Sun, 23 Nov 2025 19:45:23 -0800 Subject: [PATCH 5/7] Update availability for Deque's typealias --- stdlib/public/Concurrency/Deque/Deque+Collection.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Concurrency/Deque/Deque+Collection.swift b/stdlib/public/Concurrency/Deque/Deque+Collection.swift index 144370b6845ce..4ad9584a46e78 100644 --- a/stdlib/public/Concurrency/Deque/Deque+Collection.swift +++ b/stdlib/public/Concurrency/Deque/Deque+Collection.swift @@ -19,7 +19,7 @@ extension _Deque: Sequence { // This custom implementation performs direct storage access to eliminate any // and all index validation overhead. It also optimizes away repeated // conversions from indices to storage slots. - @available(macOS 9999, *) + @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) internal typealias BorrowingIterator = BorrowingIteratorAdapter /// An iterator over the members of a deque. From 73e48b45345e689a4b158a77fba4dd68ac9efa85 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 24 Nov 2025 06:19:30 -0800 Subject: [PATCH 6/7] More typealias availability --- stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift | 2 +- stdlib/public/RuntimeModule/Base64.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift index b3cbc35f863d9..a902bc48b7b38 100644 --- a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift +++ b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift @@ -227,7 +227,7 @@ internal struct Section { /// Holds the addresses and sizes of sections related to reflection. internal struct ReflectionInfo : Sequence { internal typealias Element = Section? - @available(macOS 9999, *) + @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) internal typealias BorrowingIterator = BorrowingIteratorAdapter> /// The name of the loaded image. diff --git a/stdlib/public/RuntimeModule/Base64.swift b/stdlib/public/RuntimeModule/Base64.swift index bc32a9298edd8..0739c5480d812 100644 --- a/stdlib/public/RuntimeModule/Base64.swift +++ b/stdlib/public/RuntimeModule/Base64.swift @@ -217,7 +217,7 @@ public struct Base64Encoder: Sequence public struct Base64Decoder: Sequence where S.Element == UTF8.CodeUnit { - @available(macOS 9999, *) + @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) public typealias BorrowingIterator = BorrowingIteratorAdapter public typealias Element = UInt8 From b8ddae4159c94255108395f886e6f6f4b0e64262 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 24 Nov 2025 18:15:16 -0800 Subject: [PATCH 7/7] Add Array and Range borrowing iterator --- stdlib/public/core/Array.swift | 12 ++++++ .../core/BorrowingIteratorProtocol.swift | 4 +- stdlib/public/core/Range.swift | 37 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index 6346a7658f712..b09910730b6a5 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -511,6 +511,18 @@ extension Array: _ArrayProtocol { } } +extension Array: Sequence { + @available(SwiftStdlib 6.3, *) + public typealias BorrowingIterator = Span + + @available(SwiftStdlib 6.3, *) + @lifetime(borrow self) + @_transparent + public func makeBorrowingIterator() -> BorrowingIterator { + span + } +} + extension Array: RandomAccessCollection, MutableCollection { /// The index type for arrays, `Int`. public typealias Index = Int diff --git a/stdlib/public/core/BorrowingIteratorProtocol.swift b/stdlib/public/core/BorrowingIteratorProtocol.swift index 3cee51968d04d..3d00efafe01ea 100644 --- a/stdlib/public/core/BorrowingIteratorProtocol.swift +++ b/stdlib/public/core/BorrowingIteratorProtocol.swift @@ -94,7 +94,7 @@ extension BorrowingIteratorProtocol where Self: ~Copyable & ~Escapable { @_alwaysEmitIntoClient @_lifetime(&self) @_lifetime(self: copy self) - @_transparent + @inlinable public mutating func nextSpan() -> Span { nextSpan(maximumCount: Int.max) } @@ -141,6 +141,7 @@ extension Span: BorrowingIteratorProtocol where Element: ~Copyable { @_alwaysEmitIntoClient @_lifetime(&self) @_lifetime(self: copy self) + @inlinable public mutating func nextSpan(maximumCount: Int) -> Span { let result = extracting(first: maximumCount) self = extracting(droppingFirst: maximumCount) @@ -154,6 +155,7 @@ extension Span: Sequence /* where Element: ~Copyable */ { @_alwaysEmitIntoClient @_lifetime(borrow self) + @inlinable public func makeBorrowingIterator() -> Span { self } diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index 5f650d784ed56..10d6bc7cb35dd 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -217,6 +217,43 @@ extension Range: Sequence where Bound: Strideable, Bound.Stride: SignedInteger { public typealias Element = Bound public typealias Iterator = IndexingIterator> + + @inlinable + public func makeBorrowingIterator() -> BorrowingIterator { + .init(lower: lowerBound, upper: upperBound) + } + + @frozen + public struct BorrowingIterator: BorrowingIteratorProtocol { + @usableFromInline + var lowerBound: [1 of Bound] + @usableFromInline + let upperBound: Bound + @usableFromInline + var first: Bool = true + + + @inlinable + @usableFromInline + init(lower: Bound, upper: Bound) { + self.lowerBound = [lower.advanced(by: -1)] + self.upperBound = upper + } + + @_lifetime(&self) + @inlinable + public mutating func nextSpan(maximumCount: Int) -> Span { + if lowerBound[0] >= upperBound { + return .init() + } + if first { + first = false + return lowerBound.span + } + lowerBound[0] = lowerBound[0].advanced(by: 1) + return lowerBound.span + } + } } extension Range: Collection, BidirectionalCollection, RandomAccessCollection