Skip to content

Commit 316875e

Browse files
authored
Merge pull request #13370 from moiseev/conditional-collections-damnit
[stdlib] Utilize conditional conformances for lazy collections. Again.
2 parents 7d01093 + fd4e866 commit 316875e

File tree

11 files changed

+251
-223
lines changed

11 files changed

+251
-223
lines changed

benchmark/single-source/LazyFilter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public func run_LazilyFilteredArrays(_ N: Int) {
4747
CheckResults(res == 123)
4848
}
4949

50-
fileprivate var multiplesOfThree: LazyFilterBidirectionalCollection<Array<Int>>?
50+
fileprivate var multiplesOfThree: LazyFilterCollection<Array<Int>>?
5151

5252
fileprivate func setup_LazilyFilteredArrayContains() {
5353
multiplesOfThree = Array(1..<5_000).lazy.filter { $0 % 3 == 0 }

stdlib/public/core/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ set(SWIFTLIB_ESSENTIAL
5454
Equatable.swift
5555
ErrorType.swift
5656
Existential.swift
57-
Filter.swift.gyb
57+
Filter.swift
5858
FixedArray.swift.gyb
5959
FlatMap.swift
6060
Flatten.swift.gyb
@@ -82,7 +82,7 @@ set(SWIFTLIB_ESSENTIAL
8282
LazySequence.swift
8383
LifetimeManager.swift
8484
ManagedBuffer.swift
85-
Map.swift.gyb
85+
Map.swift
8686
MemoryLayout.swift
8787
UnicodeScalar.swift # ORDER DEPENDENCY: Must precede Mirrors.swift
8888
Mirrors.swift.gyb

stdlib/public/core/Filter.swift.gyb renamed to stdlib/public/core/Filter.swift

Lines changed: 160 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- Filter.swift.gyb -------------------------------------*- swift -*-===//
1+
//===--- Filter.swift -----------------------------------------*- swift -*-===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -10,12 +10,6 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
%{
14-
from gyb_stdlib_support import (
15-
collectionForTraversal
16-
)
17-
}%
18-
1913
/// An iterator over the elements traversed by some base iterator that also
2014
/// satisfy a given predicate.
2115
///
@@ -122,14 +116,6 @@ public struct LazyFilterSequence<Base : Sequence>
122116
@available(swift, deprecated: 3.1, obsoleted: 4.0, message: "Use Base.Index")
123117
public typealias LazyFilterIndex<Base : Collection> = Base.Index
124118

125-
// FIXME(ABI)#27 (Conditional Conformance): `LazyFilter*Collection` types should be
126-
// collapsed into one `LazyFilterCollection` using conditional conformances.
127-
// Maybe even combined with `LazyFilterSequence`.
128-
// rdar://problem/17144340
129-
130-
% for Traversal in ['Forward', 'Bidirectional']:
131-
% Self = "LazyFilter" + collectionForTraversal(Traversal)
132-
133119
/// A lazy `Collection` wrapper that includes the elements of an
134120
/// underlying collection that satisfy a predicate.
135121
///
@@ -140,17 +126,11 @@ public typealias LazyFilterIndex<Base : Collection> = Base.Index
140126
/// general operations on `LazyFilterCollection` instances may not have the
141127
/// documented complexity.
142128
@_fixed_layout // FIXME(sil-serialize-all)
143-
public struct ${Self}<
144-
Base : ${collectionForTraversal(Traversal)}
145-
> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)}
146-
{
147-
148-
/// A type that represents a valid position in the collection.
149-
///
150-
/// Valid indices consist of the position of every element and a
151-
/// "past the end" position that's not valid for use as a subscript.
152-
public typealias Index = Base.Index
153-
129+
public struct LazyFilterCollection<Base : Collection> {
130+
@_versioned // FIXME(sil-serialize-all)
131+
internal var _base: Base
132+
@_versioned // FIXME(sil-serialize-all)
133+
internal let _predicate: (Base.Element) -> Bool
154134

155135
/// Creates an instance containing the elements of `base` that
156136
/// satisfy `isIncluded`.
@@ -163,6 +143,38 @@ public struct ${Self}<
163143
self._base = _base
164144
self._predicate = isIncluded
165145
}
146+
}
147+
148+
extension LazyFilterCollection : Sequence {
149+
public typealias SubSequence = LazyFilterCollection<Base.SubSequence>
150+
public typealias Element = Base.Element
151+
152+
// Any estimate of the number of elements that pass `_predicate` requires
153+
// iterating the collection and evaluating each element, which can be costly,
154+
// is unexpected, and usually doesn't pay for itself in saving time through
155+
// preventing intermediate reallocations. (SR-4164)
156+
@_inlineable // FIXME(sil-serialize-all)
157+
public var underestimatedCount: Int { return 0 }
158+
159+
@_inlineable // FIXME(sil-serialize-all)
160+
public func _copyToContiguousArray()
161+
-> ContiguousArray<Base.Iterator.Element> {
162+
163+
// The default implementation of `_copyToContiguousArray` queries the
164+
// `count` property, which evaluates `_predicate` for every element --
165+
// see the note above `underestimatedCount`. Here we treat `self` as a
166+
// sequence and only rely on underestimated count.
167+
return _copySequenceToContiguousArray(self)
168+
}
169+
170+
/// Returns an iterator over the elements of this sequence.
171+
///
172+
/// - Complexity: O(1).
173+
@_inlineable // FIXME(sil-serialize-all)
174+
public func makeIterator() -> LazyFilterIterator<Base.Iterator> {
175+
return LazyFilterIterator(
176+
_base: _base.makeIterator(), _predicate)
177+
}
166178

167179
@_inlineable
168180
public func _customContainsEquatableElement(
@@ -176,6 +188,14 @@ public struct ${Self}<
176188
}
177189
return nil
178190
}
191+
}
192+
193+
extension LazyFilterCollection : LazyCollectionProtocol, Collection {
194+
/// A type that represents a valid position in the collection.
195+
///
196+
/// Valid indices consist of the position of every element and a
197+
/// "past the end" position that's not valid for use as a subscript.
198+
public typealias Index = Base.Index
179199

180200
/// The position of the first element in a non-empty collection.
181201
///
@@ -221,25 +241,106 @@ public struct ${Self}<
221241
i = index
222242
}
223243

224-
% if Traversal == 'Bidirectional':
244+
@inline(__always)
225245
@_inlineable // FIXME(sil-serialize-all)
226-
public func index(before i: Index) -> Index {
246+
@_versioned // FIXME(sil-serialize-all)
247+
internal func _advanceIndex(_ i: inout Index, step: Int) {
248+
repeat {
249+
_base.formIndex(&i, offsetBy: step)
250+
} while i != _base.endIndex && !_predicate(_base[i])
251+
}
252+
253+
@inline(__always)
254+
@_inlineable // FIXME(sil-serialize-all)
255+
@_versioned // FIXME(sil-serialize-all)
256+
internal func _ensureBidirectional(step: Int) {
257+
// FIXME: This seems to be the best way of checking whether _base is
258+
// forward only without adding an extra protocol requirement.
259+
// index(_:offsetBy:limitedBy:) is chosen becuase it is supposed to return
260+
// nil when the resulting index lands outside the collection boundaries,
261+
// and therefore likely does not trap in these cases.
262+
if step < 0 {
263+
_ = _base.index(
264+
_base.endIndex, offsetBy: step, limitedBy: _base.startIndex)
265+
}
266+
}
267+
268+
@_inlineable // FIXME(sil-serialize-all)
269+
public func distance(from start: Index, to end: Index) -> Int {
270+
// The following line makes sure that distance(from:to:) is invoked on the
271+
// _base at least once, to trigger a _precondition in forward only
272+
// collections.
273+
_ = _base.distance(from: start, to: end)
274+
var _start: Index
275+
let _end: Index
276+
let step: Int
277+
if start > end {
278+
_start = end
279+
_end = start
280+
step = -1
281+
}
282+
else {
283+
_start = start
284+
_end = end
285+
step = 1
286+
}
287+
var count = 0
288+
while _start != _end {
289+
count += step
290+
formIndex(after: &_start)
291+
}
292+
return count
293+
}
294+
295+
@_inlineable // FIXME(sil-serialize-all)
296+
public func index(_ i: Index, offsetBy n: Int) -> Index {
227297
var i = i
228-
formIndex(before: &i)
298+
let step = n.signum()
299+
// The following line makes sure that index(_:offsetBy:) is invoked on the
300+
// _base at least once, to trigger a _precondition in forward only
301+
// collections.
302+
_ensureBidirectional(step: step)
303+
for _ in 0 ..< abs(numericCast(n)) {
304+
_advanceIndex(&i, step: step)
305+
}
229306
return i
230307
}
231308

232309
@_inlineable // FIXME(sil-serialize-all)
233-
public func formIndex(before i: inout Index) {
234-
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
235-
var index = i
236-
_precondition(index != _base.startIndex, "Can't retreat before startIndex")
237-
repeat {
238-
_base.formIndex(before: &index)
239-
} while !_predicate(_base[index])
240-
i = index
310+
public func formIndex(_ i: inout Index, offsetBy n: Int) {
311+
i = index(i, offsetBy: n)
312+
}
313+
314+
@_inlineable // FIXME(sil-serialize-all)
315+
public func index(
316+
_ i: Index, offsetBy n: Int, limitedBy limit: Index
317+
) -> Index? {
318+
var i = i
319+
let step = n.signum()
320+
// The following line makes sure that index(_:offsetBy:limitedBy:) is
321+
// invoked on the _base at least once, to trigger a _precondition in
322+
// forward only collections.
323+
_ensureBidirectional(step: step)
324+
for _ in 0 ..< abs(numericCast(n)) {
325+
if i == limit {
326+
return nil
327+
}
328+
_advanceIndex(&i, step: step)
329+
}
330+
return i
331+
}
332+
333+
@_inlineable // FIXME(sil-serialize-all)
334+
public func formIndex(
335+
_ i: inout Index, offsetBy n: Int, limitedBy limit: Index
336+
) -> Bool {
337+
if let advancedIndex = index(i, offsetBy: n, limitedBy: limit) {
338+
i = advancedIndex
339+
return true
340+
}
341+
i = limit
342+
return false
241343
}
242-
% end
243344

244345
/// Accesses the element at `position`.
245346
///
@@ -250,48 +351,34 @@ public struct ${Self}<
250351
return _base[position]
251352
}
252353

253-
public typealias SubSequence = ${Self}<Base.SubSequence>
254-
255354
@_inlineable // FIXME(sil-serialize-all)
256355
public subscript(bounds: Range<Index>) -> SubSequence {
257356
return SubSequence(_base: _base[bounds], _predicate)
258357
}
358+
}
259359

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

267363
@_inlineable // FIXME(sil-serialize-all)
268-
public func _copyToContiguousArray()
269-
-> ContiguousArray<Base.Iterator.Element> {
270-
271-
// The default implementation of `_copyToContiguousArray` queries the
272-
// `count` property, which evaluates `_predicate` for every element --
273-
// see the note above `underestimatedCount`. Here we treat `self` as a
274-
// sequence and only rely on underestimated count.
275-
return _copySequenceToContiguousArray(self)
364+
public func index(before i: Index) -> Index {
365+
var i = i
366+
formIndex(before: &i)
367+
return i
276368
}
277369

278-
/// Returns an iterator over the elements of this sequence.
279-
///
280-
/// - Complexity: O(1).
281370
@_inlineable // FIXME(sil-serialize-all)
282-
public func makeIterator() -> LazyFilterIterator<Base.Iterator> {
283-
return LazyFilterIterator(
284-
_base: _base.makeIterator(), _predicate)
371+
public func formIndex(before i: inout Index) {
372+
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
373+
var index = i
374+
_precondition(index != _base.startIndex, "Can't retreat before startIndex")
375+
repeat {
376+
_base.formIndex(before: &index)
377+
} while !_predicate(_base[index])
378+
i = index
285379
}
286-
287-
@_versioned // FIXME(sil-serialize-all)
288-
internal var _base: Base
289-
@_versioned // FIXME(sil-serialize-all)
290-
internal let _predicate: (Base.Element) -> Bool
291380
}
292381

293-
% end
294-
295382
extension LazySequenceProtocol {
296383
/// Returns the elements of `self` that satisfy `isIncluded`.
297384
///
@@ -303,20 +390,11 @@ extension LazySequenceProtocol {
303390
public func filter(
304391
_ isIncluded: @escaping (Elements.Element) -> Bool
305392
) -> LazyFilterSequence<Self.Elements> {
306-
return LazyFilterSequence(
307-
_base: self.elements, isIncluded)
393+
return LazyFilterSequence(_base: self.elements, isIncluded)
308394
}
309395
}
310396

311-
% for Traversal in ['Forward', 'Bidirectional']:
312-
313-
extension LazyCollectionProtocol
314-
% if Traversal != 'Forward':
315-
where
316-
Self : ${collectionForTraversal(Traversal)},
317-
Elements : ${collectionForTraversal(Traversal)}
318-
% end
319-
{
397+
extension LazyCollectionProtocol {
320398
/// Returns the elements of `self` that satisfy `predicate`.
321399
///
322400
/// - Note: The elements of the result are computed on-demand, as
@@ -326,14 +404,10 @@ extension LazyCollectionProtocol
326404
@_inlineable // FIXME(sil-serialize-all)
327405
public func filter(
328406
_ isIncluded: @escaping (Elements.Element) -> Bool
329-
) -> LazyFilter${collectionForTraversal(Traversal)}<Self.Elements> {
330-
return LazyFilter${collectionForTraversal(Traversal)}(
331-
_base: self.elements, isIncluded)
407+
) -> LazyFilterCollection<Self.Elements> {
408+
return LazyFilterCollection(_base: self.elements, isIncluded)
332409
}
333410
}
334411

335-
% end
336-
337-
// ${'Local Variables'}:
338-
// eval: (read-only-mode 1)
339-
// End:
412+
@available(*, deprecated, renamed: "LazyFilterCollection")
413+
public typealias LazyFilterBidirectionalCollection<T> = LazyFilterCollection<T> where T : BidirectionalCollection

stdlib/public/core/FlatMap.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ extension LazyCollectionProtocol {
9595
extension LazyCollectionProtocol
9696
where
9797
Self : BidirectionalCollection,
98-
Elements : BidirectionalCollection
99-
{
98+
Elements : BidirectionalCollection {
10099
/// Returns the concatenated results of mapping the given transformation over
101100
/// this collection.
102101
///
@@ -111,7 +110,7 @@ extension LazyCollectionProtocol
111110
_ transform: @escaping (Elements.Element) -> SegmentOfResult
112111
) -> LazyCollection<
113112
FlattenBidirectionalCollection<
114-
LazyMapBidirectionalCollection<Elements, SegmentOfResult>>> {
113+
LazyMapCollection<Elements, SegmentOfResult>>> {
115114
return self.map(transform).joined()
116115
}
117116

@@ -128,9 +127,9 @@ extension LazyCollectionProtocol
128127
@_inlineable // FIXME(sil-serialize-all)
129128
public func flatMap<ElementOfResult>(
130129
_ transform: @escaping (Elements.Element) -> ElementOfResult?
131-
) -> LazyMapBidirectionalCollection<
132-
LazyFilterBidirectionalCollection<
133-
LazyMapBidirectionalCollection<Elements, ElementOfResult?>>,
130+
) -> LazyMapCollection<
131+
LazyFilterCollection<
132+
LazyMapCollection<Elements, ElementOfResult?>>,
134133
ElementOfResult
135134
> {
136135
return self.map(transform).filter { $0 != nil }.map { $0! }

0 commit comments

Comments
 (0)