diff --git a/Runtimes/Core/Core/CMakeLists.txt b/Runtimes/Core/Core/CMakeLists.txt index bf549e65af664..07d257abe8137 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/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/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/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/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..a902bc48b7b38 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, iOS 9999, watchOS 9999, tvOS 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..4ad9584a46e78 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, iOS 9999, watchOS 9999, tvOS 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..0739c5480d812 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, iOS 9999, watchOS 9999, tvOS 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/Array.swift b/stdlib/public/core/Array.swift index a590a5ba2782c..b09910730b6a5 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 } } @@ -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 @@ -814,7 +826,7 @@ extension Array: RandomAccessCollection, MutableCollection { } } } - + /// The number of elements in the array. @inlinable @_semantics("array.get_count") @@ -1249,14 +1261,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 +1452,7 @@ extension Array: RangeReplaceableCollection { return try unsafe body(bufferPointer) } } - + @inlinable public __consuming func _copyToContiguousArray() -> ContiguousArray { if let n = _buffer.requestNativeBuffer() { @@ -1799,7 +1811,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 +2172,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/BorrowingIteratorProtocol.swift b/stdlib/public/core/BorrowingIteratorProtocol.swift new file mode 100644 index 0000000000000..3d00efafe01ea --- /dev/null +++ b/stdlib/public/core/BorrowingIteratorProtocol.swift @@ -0,0 +1,229 @@ +//===----------------------------------------------------------------------===// +// +// 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 BorrowingIteratorProtocol: ~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 BorrowingIteratorProtocol where Self: ~Copyable & ~Escapable { + @_alwaysEmitIntoClient + @_lifetime(&self) + @_lifetime(self: copy self) + @inlinable + public mutating func nextSpan() -> Span { + nextSpan(maximumCount: Int.max) + } +} + +@available(SwiftStdlib 6.3, *) +extension BorrowingIteratorProtocol 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: 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) + return result + } +} + +@available(SwiftStdlib 6.3, *) +extension Span: Sequence /* where Element: ~Copyable */ { + public typealias Iterator = NeverIterator + + @_alwaysEmitIntoClient + @_lifetime(borrow self) + @inlinable + public func makeBorrowingIterator() -> Span { + self + } +} + +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) + 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 b2238632d4e66..95e987e32d7ef 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 BorrowingIteratorProtocol.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 @@ -338,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") @@ -530,7 +533,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..504e4e9ed9666 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", + "BorrowingIteratorProtocol.swift" ], "Result": [ "Result.swift" diff --git a/stdlib/public/core/InlineArray.swift b/stdlib/public/core/InlineArray.swift index 4224eed6a1356..74c7d077cefd0 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] @@ -614,3 +614,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 +// } +//} diff --git a/stdlib/public/core/Iterable.swift b/stdlib/public/core/Iterable.swift new file mode 100644 index 0000000000000..9d9a6643dfbbe --- /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: BorrowingIteratorProtocol & ~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 + } +} diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index f7614db294703..9205701e21c66 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..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 diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index 5ae2e1addeaaa..c796fe52d8005 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,58 @@ public protocol Sequence { ) rethrows -> R? } +@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() + 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 { + @_transparent + 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 +722,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)) +}