Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Runtimes/Core/cmake/modules/ExperimentalFeatures.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ add_compile_options(
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature BitwiseCopyable>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature Extern>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature AllowUnsafeAttribute>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature ValueGenerics>")
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature ValueGenerics>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-enable-experimental-feature Lifetimes>")
2 changes: 2 additions & 0 deletions Runtimes/Core/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ add_library(swiftCore
BidirectionalCollection.swift
Bitset.swift
Bool.swift
BorrowIteratorProtocol.swift
BridgeObjectiveC.swift
BridgeStorage.swift
BridgingBuffer.swift
Expand Down Expand Up @@ -93,6 +94,7 @@ add_library(swiftCore
InputStream.swift
IntegerParsing.swift
Integers.swift
Iterable.swift
Join.swift
KeyPath.swift
KeyValuePairs.swift
Expand Down
9 changes: 5 additions & 4 deletions stdlib/cmake/modules/SwiftSource.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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")

Expand Down
36 changes: 29 additions & 7 deletions stdlib/public/core/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ extension Array: _ArrayProtocol {
@inlinable // FIXME(inline-always)
@inline(__always)
get {
return _buffer.owner
return _buffer.owner
}
}

Expand Down Expand Up @@ -814,7 +814,7 @@ extension Array: RandomAccessCollection, MutableCollection {
}
}
}

/// The number of elements in the array.
@inlinable
@_semantics("array.get_count")
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -1440,7 +1440,7 @@ extension Array: RangeReplaceableCollection {
return try unsafe body(bufferPointer)
}
}

@inlinable
public __consuming func _copyToContiguousArray() -> ContiguousArray<Element> {
if let n = _buffer.requestNativeBuffer() {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -2160,3 +2160,25 @@ internal struct _ArrayAnyHashableBox<Element: Hashable>
}

extension Array: @unchecked Sendable where Element: Sendable { }

// FIXME: This should be accepted, but we get the following:
// error: type 'Array<Element>' does not conform to protocol 'Iterable'
// note: multiple matching functions named '_customContainsEquatableElement' with type '(borrowing Array<Element>.Element) -> Bool?' (aka '(borrowing Element) -> Optional<Bool>')

// @available(SwiftStdlib 6.3, *)
// extension Array: Iterable {
// public typealias BorrowIterator = Span<Element>.BorrowIterator

// @_alwaysEmitIntoClient
// @_transparent
// public var estimatedCount: EstimatedCount {
// .exactly(count)
// }

// @_alwaysEmitIntoClient
// @_lifetime(borrow self)
// @_transparent
// public func startBorrowIteration() -> Span<Element> {
// self.span
// }
// }
185 changes: 185 additions & 0 deletions stdlib/public/core/BorrowIteratorProtocol.swift
Original file line number Diff line number Diff line change
@@ -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<Element>: ~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<Element>?

/// 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<Element>

/// 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<Element> {
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<Element> {
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<Element> {
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<Element>.BorrowIterator

@_alwaysEmitIntoClient
@_transparent
public var estimatedCount: EstimatedCount {
.exactly(count)
}

@_alwaysEmitIntoClient
@_lifetime(borrow self)
@_transparent
public func startBorrowIteration() -> Span<Element> {
span
}
}

@available(SwiftStdlib 6.3, *)
extension OutputSpan: Iterable where Element: ~Copyable {
public typealias BorrowIterator = Span<Element>.BorrowIterator

@_alwaysEmitIntoClient
@_transparent
public var estimatedCount: EstimatedCount {
.exactly(count)
}

@_alwaysEmitIntoClient
@_lifetime(borrow self)
@_transparent
public func startBorrowIteration() -> Span<Element> {
self.span
}
}
6 changes: 4 additions & 2 deletions stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}")
Expand Down
6 changes: 4 additions & 2 deletions stdlib/public/core/GroupInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
"RawSpan.swift",
"Span.swift"
],
"UTF8Span": [
"UTF8Span": [
"UTF8EncodingError.swift",
"UTF8Span.swift",
"UTF8SpanBits.swift",
Expand Down Expand Up @@ -276,7 +276,9 @@
"EmbeddedStubs.swift",
"EmbeddedPrint.swift",
"InlineArray.swift",
"_InlineArray.swift"
"_InlineArray.swift",
"Iterable.swift",
"BorrowIteratorProtocol.swift"
],
"Result": [
"Result.swift"
Expand Down
Loading