Skip to content

[stdlib] Utilize conditional conformances for lazy collections. Again. #13370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 11, 2017
Merged
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
2 changes: 1 addition & 1 deletion benchmark/single-source/LazyFilter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public func run_LazilyFilteredArrays(_ N: Int) {
CheckResults(res == 123)
}

fileprivate var multiplesOfThree: LazyFilterBidirectionalCollection<Array<Int>>?
fileprivate var multiplesOfThree: LazyFilterCollection<Array<Int>>?

fileprivate func setup_LazilyFilteredArrayContains() {
multiplesOfThree = Array(1..<5_000).lazy.filter { $0 % 3 == 0 }
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ set(SWIFTLIB_ESSENTIAL
Equatable.swift
ErrorType.swift
Existential.swift
Filter.swift.gyb
Filter.swift
FixedArray.swift.gyb
FlatMap.swift
Flatten.swift.gyb
Expand Down Expand Up @@ -82,7 +82,7 @@ set(SWIFTLIB_ESSENTIAL
LazySequence.swift
LifetimeManager.swift
ManagedBuffer.swift
Map.swift.gyb
Map.swift
MemoryLayout.swift
UnicodeScalar.swift # ORDER DEPENDENCY: Must precede Mirrors.swift
Mirrors.swift.gyb
Expand Down
246 changes: 160 additions & 86 deletions stdlib/public/core/Filter.swift.gyb → stdlib/public/core/Filter.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- Filter.swift.gyb -------------------------------------*- swift -*-===//
//===--- Filter.swift -----------------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
Expand All @@ -10,12 +10,6 @@
//
//===----------------------------------------------------------------------===//

%{
from gyb_stdlib_support import (
collectionForTraversal
)
}%

/// An iterator over the elements traversed by some base iterator that also
/// satisfy a given predicate.
///
Expand Down Expand Up @@ -122,14 +116,6 @@ public struct LazyFilterSequence<Base : Sequence>
@available(swift, deprecated: 3.1, obsoleted: 4.0, message: "Use Base.Index")
public typealias LazyFilterIndex<Base : Collection> = Base.Index

// FIXME(ABI)#27 (Conditional Conformance): `LazyFilter*Collection` types should be
// collapsed into one `LazyFilterCollection` using conditional conformances.
// Maybe even combined with `LazyFilterSequence`.
// rdar://problem/17144340

% for Traversal in ['Forward', 'Bidirectional']:
% Self = "LazyFilter" + collectionForTraversal(Traversal)

/// A lazy `Collection` wrapper that includes the elements of an
/// underlying collection that satisfy a predicate.
///
Expand All @@ -140,17 +126,11 @@ public typealias LazyFilterIndex<Base : Collection> = Base.Index
/// general operations on `LazyFilterCollection` instances may not have the
/// documented complexity.
@_fixed_layout // FIXME(sil-serialize-all)
public struct ${Self}<
Base : ${collectionForTraversal(Traversal)}
> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)}
{

/// A type that represents a valid position in the collection.
///
/// Valid indices consist of the position of every element and a
/// "past the end" position that's not valid for use as a subscript.
public typealias Index = Base.Index

public struct LazyFilterCollection<Base : Collection> {
@_versioned // FIXME(sil-serialize-all)
internal var _base: Base
@_versioned // FIXME(sil-serialize-all)
internal let _predicate: (Base.Element) -> Bool

/// Creates an instance containing the elements of `base` that
/// satisfy `isIncluded`.
Expand All @@ -163,6 +143,38 @@ public struct ${Self}<
self._base = _base
self._predicate = isIncluded
}
}

extension LazyFilterCollection : Sequence {
public typealias SubSequence = LazyFilterCollection<Base.SubSequence>
public typealias Element = Base.Element

// Any estimate of the number of elements that pass `_predicate` requires
// iterating the collection and evaluating each element, which can be costly,
// is unexpected, and usually doesn't pay for itself in saving time through
// preventing intermediate reallocations. (SR-4164)
@_inlineable // FIXME(sil-serialize-all)
public var underestimatedCount: Int { return 0 }

@_inlineable // FIXME(sil-serialize-all)
public func _copyToContiguousArray()
-> ContiguousArray<Base.Iterator.Element> {

// The default implementation of `_copyToContiguousArray` queries the
// `count` property, which evaluates `_predicate` for every element --
// see the note above `underestimatedCount`. Here we treat `self` as a
// sequence and only rely on underestimated count.
return _copySequenceToContiguousArray(self)
}

/// Returns an iterator over the elements of this sequence.
///
/// - Complexity: O(1).
@_inlineable // FIXME(sil-serialize-all)
public func makeIterator() -> LazyFilterIterator<Base.Iterator> {
return LazyFilterIterator(
_base: _base.makeIterator(), _predicate)
}

@_inlineable
public func _customContainsEquatableElement(
Expand All @@ -176,6 +188,14 @@ public struct ${Self}<
}
return nil
}
}

extension LazyFilterCollection : LazyCollectionProtocol, Collection {
/// A type that represents a valid position in the collection.
///
/// Valid indices consist of the position of every element and a
/// "past the end" position that's not valid for use as a subscript.
public typealias Index = Base.Index

/// The position of the first element in a non-empty collection.
///
Expand Down Expand Up @@ -221,25 +241,106 @@ public struct ${Self}<
i = index
}

% if Traversal == 'Bidirectional':
@inline(__always)
@_inlineable // FIXME(sil-serialize-all)
public func index(before i: Index) -> Index {
@_versioned // FIXME(sil-serialize-all)
internal func _advanceIndex(_ i: inout Index, step: Int) {
repeat {
_base.formIndex(&i, offsetBy: step)
} while i != _base.endIndex && !_predicate(_base[i])
}

@inline(__always)
@_inlineable // FIXME(sil-serialize-all)
@_versioned // FIXME(sil-serialize-all)
internal func _ensureBidirectional(step: Int) {
// FIXME: This seems to be the best way of checking whether _base is
// forward only without adding an extra protocol requirement.
// index(_:offsetBy:limitedBy:) is chosen becuase it is supposed to return
// nil when the resulting index lands outside the collection boundaries,
// and therefore likely does not trap in these cases.
if step < 0 {
_ = _base.index(
_base.endIndex, offsetBy: step, limitedBy: _base.startIndex)
}
}

@_inlineable // FIXME(sil-serialize-all)
public func distance(from start: Index, to end: Index) -> Int {
// The following line makes sure that distance(from:to:) is invoked on the
// _base at least once, to trigger a _precondition in forward only
// collections.
_ = _base.distance(from: start, to: end)
var _start: Index
let _end: Index
let step: Int
if start > end {
_start = end
_end = start
step = -1
}
else {
_start = start
_end = end
step = 1
}
var count = 0
while _start != _end {
count += step
formIndex(after: &_start)
}
return count
}

@_inlineable // FIXME(sil-serialize-all)
public func index(_ i: Index, offsetBy n: Int) -> Index {
var i = i
formIndex(before: &i)
let step = n.signum()
// The following line makes sure that index(_:offsetBy:) is invoked on the
// _base at least once, to trigger a _precondition in forward only
// collections.
_ensureBidirectional(step: step)
for _ in 0 ..< abs(numericCast(n)) {
_advanceIndex(&i, step: step)
}
return i
}

@_inlineable // FIXME(sil-serialize-all)
public func formIndex(before i: inout Index) {
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
var index = i
_precondition(index != _base.startIndex, "Can't retreat before startIndex")
repeat {
_base.formIndex(before: &index)
} while !_predicate(_base[index])
i = index
public func formIndex(_ i: inout Index, offsetBy n: Int) {
i = index(i, offsetBy: n)
}

@_inlineable // FIXME(sil-serialize-all)
public func index(
_ i: Index, offsetBy n: Int, limitedBy limit: Index
) -> Index? {
var i = i
let step = n.signum()
// The following line makes sure that index(_:offsetBy:limitedBy:) is
// invoked on the _base at least once, to trigger a _precondition in
// forward only collections.
_ensureBidirectional(step: step)
for _ in 0 ..< abs(numericCast(n)) {
if i == limit {
return nil
}
_advanceIndex(&i, step: step)
}
return i
}

@_inlineable // FIXME(sil-serialize-all)
public func formIndex(
_ i: inout Index, offsetBy n: Int, limitedBy limit: Index
) -> Bool {
if let advancedIndex = index(i, offsetBy: n, limitedBy: limit) {
i = advancedIndex
return true
}
i = limit
return false
}
% end

/// Accesses the element at `position`.
///
Expand All @@ -250,48 +351,34 @@ public struct ${Self}<
return _base[position]
}

public typealias SubSequence = ${Self}<Base.SubSequence>

@_inlineable // FIXME(sil-serialize-all)
public subscript(bounds: Range<Index>) -> SubSequence {
return SubSequence(_base: _base[bounds], _predicate)
}
}

// Any estimate of the number of elements that pass `_predicate` requires
// iterating the collection and evaluating each element, which can be costly,
// is unexpected, and usually doesn't pay for itself in saving time through
// preventing intermediate reallocations. (SR-4164)
@_inlineable // FIXME(sil-serialize-all)
public var underestimatedCount: Int { return 0 }
extension LazyFilterCollection : BidirectionalCollection
where Base : BidirectionalCollection {

@_inlineable // FIXME(sil-serialize-all)
public func _copyToContiguousArray()
-> ContiguousArray<Base.Iterator.Element> {

// The default implementation of `_copyToContiguousArray` queries the
// `count` property, which evaluates `_predicate` for every element --
// see the note above `underestimatedCount`. Here we treat `self` as a
// sequence and only rely on underestimated count.
return _copySequenceToContiguousArray(self)
public func index(before i: Index) -> Index {
var i = i
formIndex(before: &i)
return i
}

/// Returns an iterator over the elements of this sequence.
///
/// - Complexity: O(1).
@_inlineable // FIXME(sil-serialize-all)
public func makeIterator() -> LazyFilterIterator<Base.Iterator> {
return LazyFilterIterator(
_base: _base.makeIterator(), _predicate)
public func formIndex(before i: inout Index) {
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
var index = i
_precondition(index != _base.startIndex, "Can't retreat before startIndex")
repeat {
_base.formIndex(before: &index)
} while !_predicate(_base[index])
i = index
}

@_versioned // FIXME(sil-serialize-all)
internal var _base: Base
@_versioned // FIXME(sil-serialize-all)
internal let _predicate: (Base.Element) -> Bool
}

% end

extension LazySequenceProtocol {
/// Returns the elements of `self` that satisfy `isIncluded`.
///
Expand All @@ -303,20 +390,11 @@ extension LazySequenceProtocol {
public func filter(
_ isIncluded: @escaping (Elements.Element) -> Bool
) -> LazyFilterSequence<Self.Elements> {
return LazyFilterSequence(
_base: self.elements, isIncluded)
return LazyFilterSequence(_base: self.elements, isIncluded)
}
}

% for Traversal in ['Forward', 'Bidirectional']:

extension LazyCollectionProtocol
% if Traversal != 'Forward':
where
Self : ${collectionForTraversal(Traversal)},
Elements : ${collectionForTraversal(Traversal)}
% end
{
extension LazyCollectionProtocol {
/// Returns the elements of `self` that satisfy `predicate`.
///
/// - Note: The elements of the result are computed on-demand, as
Expand All @@ -326,14 +404,10 @@ extension LazyCollectionProtocol
@_inlineable // FIXME(sil-serialize-all)
public func filter(
_ isIncluded: @escaping (Elements.Element) -> Bool
) -> LazyFilter${collectionForTraversal(Traversal)}<Self.Elements> {
return LazyFilter${collectionForTraversal(Traversal)}(
_base: self.elements, isIncluded)
) -> LazyFilterCollection<Self.Elements> {
return LazyFilterCollection(_base: self.elements, isIncluded)
}
}

% end

// ${'Local Variables'}:
// eval: (read-only-mode 1)
// End:
@available(*, deprecated, renamed: "LazyFilterCollection")
public typealias LazyFilterBidirectionalCollection<T> = LazyFilterCollection<T> where T : BidirectionalCollection
11 changes: 5 additions & 6 deletions stdlib/public/core/FlatMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ extension LazyCollectionProtocol {
extension LazyCollectionProtocol
where
Self : BidirectionalCollection,
Elements : BidirectionalCollection
{
Elements : BidirectionalCollection {
/// Returns the concatenated results of mapping the given transformation over
/// this collection.
///
Expand All @@ -111,7 +110,7 @@ extension LazyCollectionProtocol
_ transform: @escaping (Elements.Element) -> SegmentOfResult
) -> LazyCollection<
FlattenBidirectionalCollection<
LazyMapBidirectionalCollection<Elements, SegmentOfResult>>> {
LazyMapCollection<Elements, SegmentOfResult>>> {
return self.map(transform).joined()
}

Expand All @@ -128,9 +127,9 @@ extension LazyCollectionProtocol
@_inlineable // FIXME(sil-serialize-all)
public func flatMap<ElementOfResult>(
_ transform: @escaping (Elements.Element) -> ElementOfResult?
) -> LazyMapBidirectionalCollection<
LazyFilterBidirectionalCollection<
LazyMapBidirectionalCollection<Elements, ElementOfResult?>>,
) -> LazyMapCollection<
LazyFilterCollection<
LazyMapCollection<Elements, ElementOfResult?>>,
ElementOfResult
> {
return self.map(transform).filter { $0 != nil }.map { $0! }
Expand Down
Loading