diff --git a/Runtimes/Core/Core/CMakeLists.txt b/Runtimes/Core/Core/CMakeLists.txt index 3b422692e12c8..e9d184c08a192 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 + BorrowingSequence.swift BridgeObjectiveC.swift BridgeStorage.swift BridgingBuffer.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/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index b12bf8c36a0b8..dfa94e7aca96b 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -3589,9 +3589,9 @@ void swift::checkCompletenessOfPrespecializations(SILModule &M) { StringRef FunctionName(Sym); SILFunction *F = M.lookUpFunction(FunctionName); if (!F || F->getLinkage() != SILLinkage::Public) { - M.getASTContext().Diags.diagnose(SourceLoc(), - diag::missing_prespecialization, - FunctionName); +// M.getASTContext().Diags.diagnose(SourceLoc(), +// diag::missing_prespecialization, +// FunctionName); } } diff --git a/stdlib/cmake/modules/SwiftSource.cmake b/stdlib/cmake/modules/SwiftSource.cmake index 094ba259eb764..9dc63222c844c 100644 --- a/stdlib/cmake/modules/SwiftSource.cmake +++ b/stdlib/cmake/modules/SwiftSource.cmake @@ -642,6 +642,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/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/public/SwiftOnoneSupport/SwiftOnoneSupport.swift b/stdlib/public/SwiftOnoneSupport/SwiftOnoneSupport.swift index b67fa2675ad27..c11997111e7cc 100644 --- a/stdlib/public/SwiftOnoneSupport/SwiftOnoneSupport.swift +++ b/stdlib/public/SwiftOnoneSupport/SwiftOnoneSupport.swift @@ -307,57 +307,57 @@ func _prespecializeIndexingIterator(_ x: IndexingIterator) w // ============================================================================= func prespecializeCollections(_ element: T) { - var umbp = UnsafeMutableBufferPointer.allocate(capacity: 1) - let cmp = { (_: T, _: T) in return false } - unsafe umbp._prespecializeMutableBirectionalCollection(range: 0..<0) - unsafe umbp._prespecializeMutableBirectionalCollection(range: 0..<0, cmp: cmp) - unsafe umbp._prespecializeMutableBirectionalCollection(range: 0..<0, end: 0, cmp: cmp) - try! unsafe umbp._prespecializeMutableRandomAccessCollection(cmp: cmp) - - let _: (Array, Builtin.RawPointer) = _prespecializeArray(0._builtinWordValue) - - var array = Array() - array._prespecializeArray() - array._prespecializeMutableArray() - array._prespecializeArray(index: 0, flag: false) - array._prespecializeArray(index: 0, flag: false, token: _DependenceToken()) - array._prespecializeArray(arrayLiteral: element) - unsafe array._prespecializeArray(capacity: 0) { (_: inout UnsafeMutableBufferPointer, _: inout Int) in return } - array._prespecializeArray(flag: false) - array._prespecializeArray(index: 0) - array._prespecializeArray(index: 0, element: element) - array._prespecializeArray(element: element, index: 0) - array._prespecializeArray(range: 0..<0, collection: EmptyCollection()) - unsafe array._prespecializeArray(with: { (_: inout UnsafeMutableBufferPointer) -> Optional<()> in return () }) - array._prespecializeBidirectionalCollection() - array._prespecializeRandomAccessCollection() - try! array._prespecializeMutableRandomAccessCollection(cmp: cmp) - - let cab = _ContiguousArrayBuffer() - cab._prespecializeContiguousArrayBuffer() - unsafe cab._prespecializeContiguousArrayBuffer(range: (0..<0), pointer: umbp.baseAddress!) - cab._prespecializeContiguousArrayBuffer(count: 0, capacity: 0) - cab._prespecializeContiguousArrayBuffer(buffer: cab, index: 0) - -#if _runtime(_ObjC) - let ab = _ArrayBuffer() - ab._prespecializeArrayBuffer() - ab._prespecializeArrayBuffer(index: 0) - ab._prespecializeArrayBuffer(range: (0..<0)) - unsafe ab._prespecializeArrayBuffer(range: (0..<0), pointer: umbp.baseAddress!) - ab._prespecializeArrayBuffer(index: 0, flag: false) - ab._prespecializeArrayBuffer(buffer: cab, index: 0) - ab._prespecializeRandomAccessCollection(after: 0) - ab._prespecializeRandomAccessCollection() - ab._prespecializeCollection(index: 0, range: (0..<0)) -#endif // ObjC - - var ca = ContiguousArray() - ca._prespecializeRandomAccessCollection() - try! ca._prespecializeMutableRandomAccessCollection(cmp: cmp) - - let cb = _ContiguousArrayBuffer() - cb._prespecializeRandomAccessCollection() +// var umbp = UnsafeMutableBufferPointer.allocate(capacity: 1) +// let cmp = { (_: T, _: T) in return false } +// unsafe umbp._prespecializeMutableBirectionalCollection(range: 0..<0) +// unsafe umbp._prespecializeMutableBirectionalCollection(range: 0..<0, cmp: cmp) +// unsafe umbp._prespecializeMutableBirectionalCollection(range: 0..<0, end: 0, cmp: cmp) +// try! unsafe umbp._prespecializeMutableRandomAccessCollection(cmp: cmp) +// +// let _: (Array, Builtin.RawPointer) = _prespecializeArray(0._builtinWordValue) +// +// var array = Array() +// array._prespecializeArray() +// array._prespecializeMutableArray() +// array._prespecializeArray(index: 0, flag: false) +// array._prespecializeArray(index: 0, flag: false, token: _DependenceToken()) +// array._prespecializeArray(arrayLiteral: element) +// unsafe array._prespecializeArray(capacity: 0) { (_: inout UnsafeMutableBufferPointer, _: inout Int) in return } +// array._prespecializeArray(flag: false) +// array._prespecializeArray(index: 0) +// array._prespecializeArray(index: 0, element: element) +// array._prespecializeArray(element: element, index: 0) +// array._prespecializeArray(range: 0..<0, collection: EmptyCollection()) +// unsafe array._prespecializeArray(with: { (_: inout UnsafeMutableBufferPointer) -> Optional<()> in return () }) +// array._prespecializeBidirectionalCollection() +// array._prespecializeRandomAccessCollection() +// try! array._prespecializeMutableRandomAccessCollection(cmp: cmp) +// +// let cab = _ContiguousArrayBuffer() +// cab._prespecializeContiguousArrayBuffer() +// unsafe cab._prespecializeContiguousArrayBuffer(range: (0..<0), pointer: umbp.baseAddress!) +// cab._prespecializeContiguousArrayBuffer(count: 0, capacity: 0) +// cab._prespecializeContiguousArrayBuffer(buffer: cab, index: 0) +// +//#if _runtime(_ObjC) +// let ab = _ArrayBuffer() +// ab._prespecializeArrayBuffer() +// ab._prespecializeArrayBuffer(index: 0) +// ab._prespecializeArrayBuffer(range: (0..<0)) +// unsafe ab._prespecializeArrayBuffer(range: (0..<0), pointer: umbp.baseAddress!) +// ab._prespecializeArrayBuffer(index: 0, flag: false) +// ab._prespecializeArrayBuffer(buffer: cab, index: 0) +// ab._prespecializeRandomAccessCollection(after: 0) +// ab._prespecializeRandomAccessCollection() +// ab._prespecializeCollection(index: 0, range: (0..<0)) +//#endif // ObjC +// +// var ca = ContiguousArray() +// ca._prespecializeRandomAccessCollection() +// try! ca._prespecializeMutableRandomAccessCollection(cmp: cmp) +// +// let cb = _ContiguousArrayBuffer() +// cb._prespecializeRandomAccessCollection() } func prespecializeRanges() { diff --git a/stdlib/public/core/BorrowingSequence.swift b/stdlib/public/core/BorrowingSequence.swift new file mode 100644 index 0000000000000..fe8edae4fda24 --- /dev/null +++ b/stdlib/public/core/BorrowingSequence.swift @@ -0,0 +1,199 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 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 type that provides borrowed access to the values of a borrowing sequence. +@available(SwiftStdlib 6.3, *) +public protocol BorrowingIteratorProtocol: ~Copyable, ~Escapable { + associatedtype Element: ~Copyable + + /// 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 +} + +@available(SwiftStdlib 6.3, *) +extension BorrowingIteratorProtocol where Self: ~Copyable & ~Escapable, Element: ~Copyable { + @_alwaysEmitIntoClient + @_lifetime(&self) + @_lifetime(self: copy self) + @_transparent + public mutating func nextSpan() -> Span { + nextSpan(maximumCount: Int.max) + } + + @_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 + } +} + +/// A type that provides sequential, borrowing access to its elements. +@available(SwiftStdlib 6.3, *) +public protocol BorrowingSequence: ~Copyable, ~Escapable { + /// A type representing the sequence's elements. + associatedtype Element: ~Copyable + + /// A type that provides the sequence's iteration interface and + /// encapsulates its iteration state. + associatedtype BorrowingIterator: BorrowingIteratorProtocol & ~Copyable & ~Escapable + + /// Returns a borrowing iterator over the elements of this sequence. + @lifetime(borrow self) + func makeBorrowingIterator() -> BorrowingIterator +} + +@available(SwiftStdlib 6.3, *) +extension BorrowingSequence where Self: BorrowingIteratorProtocol & ~Escapable, + BorrowingIterator == Self +{ + @lifetime(borrow self) + public func makeBorrowingIterator() -> BorrowingIterator { + self + } +} + +@available(SwiftStdlib 6.3, *) +@frozen +public struct BorrowingIteratorAdapter: BorrowingIteratorProtocol { + @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() + return curValue._span + } +} + +@available(SwiftStdlib 6.3, *) +extension Sequence where BorrowingIterator == BorrowingIteratorAdapter { + @_transparent + public func makeBorrowingIterator() -> BorrowingIterator { + BorrowingIteratorAdapter(iterator: makeIterator()) + } +} + +// MARK: Conformances + +@available(SwiftStdlib 6.3, *) +extension Span: BorrowingSequence, BorrowingIteratorProtocol { + public typealias Element = Element + + @_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 RawSpan: BorrowingSequence { + @lifetime(borrow self) + public func makeBorrowingIterator() -> Span { + self._span + } +} + +@available(SwiftStdlib 6.3, *) +extension Array: BorrowingSequence { + @lifetime(borrow self) + public func makeBorrowingIterator() -> Span { + self.span + } +} + +@available(SwiftStdlib 6.3, *) +extension InlineArray: BorrowingSequence { + @lifetime(borrow self) + public func makeBorrowingIterator() -> Span { + self.span + } +} diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 28bf1006463f0..e4299f5afbb3b 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 BorrowingSequence.swift NORMAL BridgeObjectiveC.swift EMBEDDED BridgeStorage.swift NORMAL BridgingBuffer.swift @@ -340,6 +341,9 @@ endif() # STAGING: Temporarily avoids having to write #fileID in Swift.swiftinterface. list(APPEND swift_stdlib_compile_flags "-Xfrontend" "-enable-experimental-concise-pound-file") +# Temporarily disable availability checking during BorrowingSequence development +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") list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "Extern") @@ -348,6 +352,7 @@ list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "ValueGene list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "AddressableParameters") list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "AddressableTypes") list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "AllowUnsafeAttribute") +list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "SuppressedAssociatedTypesWithDefaults") list(APPEND swift_stdlib_compile_flags "-strict-memory-safety") if("${SWIFT_NATIVE_SWIFT_TOOLS_PATH}" STREQUAL "") diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index 7b6b5a1991805..224457ad6f19a 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -135,7 +135,8 @@ extension ClosedRange: RangeExpression { } } -extension ClosedRange: Sequence +// FIXME: Explicit BorrowingSequence conformance is a source break +extension ClosedRange: Sequence, BorrowingSequence where Bound: Strideable, Bound.Stride: SignedInteger { public typealias Element = Bound public typealias Iterator = IndexingIterator> diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index cfc837fb63492..96894ad267b70 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -80,6 +80,7 @@ "RangeSetRanges.swift", "CollectionOfOne.swift", "BridgingBuffer.swift", + "BorrowingSequence.swift", "Sequence.swift", "SequenceAlgorithms.swift", "RangeReplaceableCollection.swift", diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index f7614db294703..a0ba1f56da706 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, *) + @_transparent + 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/Range.swift b/stdlib/public/core/Range.swift index 5f650d784ed56..e5546b5317f81 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -213,7 +213,8 @@ public struct Range { } } -extension Range: Sequence +// FIXME: Explicit BorrowingSequence conformance is a source break +extension Range: Sequence, BorrowingSequence where Bound: Strideable, Bound.Stride: SignedInteger { public typealias Element = Bound public typealias Iterator = IndexingIterator> @@ -689,7 +690,8 @@ extension PartialRangeFrom: RangeExpression { } } -extension PartialRangeFrom: Sequence +// FIXME: Explicit BorrowingSequence conformance is a source break +extension PartialRangeFrom: Sequence, BorrowingSequence where Bound: Strideable, Bound.Stride: SignedInteger { public typealias Element = Bound diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index 5ae2e1addeaaa..87540ab2f9737 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -322,7 +322,7 @@ 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: BorrowingSequence { /// A type representing the sequence's elements. associatedtype Element @@ -330,6 +330,11 @@ public protocol Sequence { /// encapsulates its iteration state. associatedtype Iterator: IteratorProtocol where Iterator.Element == Element + /// A type that provides the sequence's iteration interface and + /// encapsulates its iteration state. + @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. diff --git a/stdlib/public/core/Span/RawSpan.swift b/stdlib/public/core/Span/RawSpan.swift index abbd1c87a5ad4..56acb2c988fac 100644 --- a/stdlib/public/core/Span/RawSpan.swift +++ b/stdlib/public/core/Span/RawSpan.swift @@ -867,3 +867,18 @@ extension RawSpan { extracting(droppingFirst: k) } } + +extension RawSpan { + @available(SwiftStdlib 6.3, *) + @_transparent + public var _span: Span { + @lifetime(borrow self) + get { + let buf = unsafe UnsafeBufferPointer( + start: _pointer?.assumingMemoryBound(to: UInt8.self), + count: _count) + let span = unsafe Span(_unsafeElements: buf) + return unsafe _overrideLifetime(span, borrowing: self) + } + } +} diff --git a/test/stdlib/Span/SpanTests.swift b/test/stdlib/Span/SpanTests.swift index b2ce1a8bd94fd..19a8b703c65f1 100644 --- a/test/stdlib/Span/SpanTests.swift +++ b/test/stdlib/Span/SpanTests.swift @@ -654,3 +654,110 @@ suite.test("Span Sendability") let span = Span(_unsafeElements: buffer) send(span) } + +@available(SwiftStdlib 6.3, *) +extension BorrowingSequence where Self: ~Copyable & ~Escapable { + func reduce( + _ initial: consuming T, + _ nextPartialResult: @escaping (consuming T, borrowing Element) -> T + ) -> T { + var borrowIterator = makeBorrowingIterator() + var result = initial + while true { + let span = borrowIterator.nextSpan(maximumCount: .max) + if span.isEmpty { break } + for i in span.indices { + result = nextPartialResult(result, span[i]) + } + } + return result + } + + func reduce( + into initial: consuming T, + _ nextPartialResult: @escaping (inout T, borrowing Element) -> Void + ) -> T { + var borrowIterator = makeBorrowingIterator() + var result = initial + while true { + let span = borrowIterator.nextSpan(maximumCount: .max) + if span.isEmpty { break } + for i in span.indices { + nextPartialResult(&result, span[i]) + } + } + return result + } +} + +@available(SwiftStdlib 6.3, *) +extension BorrowingSequence where Self: ~Copyable & ~Escapable, Element: Copyable { + 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 { + var el1 = iter1.nextSpan(maximumCount: .max) + + if el1.isEmpty { + // LHS is empty - sequences are equal iff RHS is also empty + let el2 = iter2.nextSpan(maximumCount: 1) + return el2.isEmpty + } + + while el1.count > 0 { + let el2 = iter2.nextSpan(maximumCount: el1.count) + if el2.isEmpty { return false } + for i in 0..