Skip to content

Commit b7a2d83

Browse files
committed
[Stdlib] Rationalize the SubSequence type of a filtered Collection.
Address ABI FIXMEs swiftlang#28, swiftlang#80, swiftlang#81 by making the SubSequence of a filtered collection into be a filtered collection of the base's SubSequence rather than a slice of this filtered collection, e.g., replace `Slice<LazyFilterCollection<Array<T>>>` with `LazyFilterCollection<ArraySlice<T>>`, which allows slicing optimizations of the base type to kick in. While here, eliminate the vestigial type `LazyFilterIndex`, which was necessary pre-Swift-3 to allow the index to move. Swift 3's indexing model means that the movement of indices is on the collection itself, so we no longer need `LazyFilterIndex`: instead, the `Index` type of the lazy filtered collection is simply the `Index` type of the base collection, which is a nice convenience: it means you can take indices from a lazy wrapper around a given collection C and use them with the collection C (and, with care, vice-versa) without jumping through extra hoops.
1 parent 13f6c79 commit b7a2d83

File tree

4 files changed

+32
-103
lines changed

4 files changed

+32
-103
lines changed

stdlib/public/core/Filter.swift.gyb

+21-82
Original file line numberDiff line numberDiff line change
@@ -97,65 +97,8 @@ public struct LazyFilterSequence<Base : Sequence>
9797
}
9898

9999
/// The `Index` used for subscripting a `LazyFilterCollection`.
100-
///
101-
/// The positions of a `LazyFilterIndex` correspond to those positions
102-
/// `p` in its underlying collection `c` such that `c[p]`
103-
/// satisfies the predicate with which the `LazyFilterIndex` was
104-
/// initialized.
105-
///
106-
/// - Note: The performance of advancing a `LazyFilterIndex`
107-
/// depends on how sparsely the filtering predicate is satisfied,
108-
/// and may not offer the usual performance given by models of
109-
/// `Collection`.
110-
public struct LazyFilterIndex<Base : Collection> {
111-
112-
/// The position corresponding to `self` in the underlying collection.
113-
public let base: Base.Index
114-
}
115-
116-
extension LazyFilterIndex : Comparable {
117-
public static func == (
118-
lhs: LazyFilterIndex<Base>,
119-
rhs: LazyFilterIndex<Base>
120-
) -> Bool {
121-
return lhs.base == rhs.base
122-
}
123-
124-
public static func != (
125-
lhs: LazyFilterIndex<Base>,
126-
rhs: LazyFilterIndex<Base>
127-
) -> Bool {
128-
return lhs.base != rhs.base
129-
}
130-
131-
public static func < (
132-
lhs: LazyFilterIndex<Base>,
133-
rhs: LazyFilterIndex<Base>
134-
) -> Bool {
135-
return lhs.base < rhs.base
136-
}
137-
138-
public static func <= (
139-
lhs: LazyFilterIndex<Base>,
140-
rhs: LazyFilterIndex<Base>
141-
) -> Bool {
142-
return lhs.base <= rhs.base
143-
}
144-
145-
public static func >= (
146-
lhs: LazyFilterIndex<Base>,
147-
rhs: LazyFilterIndex<Base>
148-
) -> Bool {
149-
return lhs.base >= rhs.base
150-
}
151-
152-
public static func > (
153-
lhs: LazyFilterIndex<Base>,
154-
rhs: LazyFilterIndex<Base>
155-
) -> Bool {
156-
return lhs.base > rhs.base
157-
}
158-
}
100+
@available(swift, deprecated: 3.1, obsoleted: 4.0, message: "Use Base.Index")
101+
public typealias LazyFilterIndex<Base : Collection> = Base.Index
159102

160103
// FIXME(ABI)#27 (Conditional Conformance): `LazyFilter*Collection` types should be
161104
// collapsed into one `LazyFilterCollection` using conditional conformances.
@@ -170,20 +113,23 @@ extension LazyFilterIndex : Comparable {
170113
/// underlying collection that satisfy a predicate.
171114
///
172115
/// - Note: The performance of accessing `startIndex`, `first`, any methods
173-
/// that depend on `startIndex`, or of advancing a `LazyFilterIndex` depends
116+
/// that depend on `startIndex`, or of advancing an index depends
174117
/// on how sparsely the filtering predicate is satisfied, and may not offer
175118
/// the usual performance given by `Collection`. Be aware, therefore, that
176119
/// general operations on `LazyFilterCollection` instances may not have the
177120
/// documented complexity.
178121
public struct ${Self}<
179122
Base : ${collectionForTraversal(Traversal)}
180-
> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)} {
123+
> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)}
124+
// FIXME(ABI): Recursive protocol conformances
125+
where Base.SubSequence: ${collectionForTraversal(Traversal)}
126+
{
181127

182128
/// A type that represents a valid position in the collection.
183129
///
184130
/// Valid indices consist of the position of every element and a
185131
/// "past the end" position that's not valid for use as a subscript.
186-
public typealias Index = LazyFilterIndex<Base>
132+
public typealias Index = Base.Index
187133

188134
public typealias IndexDistance = Base.IndexDistance
189135

@@ -209,7 +155,7 @@ public struct ${Self}<
209155
while index != _base.endIndex && !_predicate(_base[index]) {
210156
_base.formIndex(after: &index)
211157
}
212-
return LazyFilterIndex(base: index)
158+
return index
213159
}
214160

215161
/// The collection's "past the end" position---that is, the position one
@@ -218,7 +164,7 @@ public struct ${Self}<
218164
/// `endIndex` is always reachable from `startIndex` by zero or more
219165
/// applications of `index(after:)`.
220166
public var endIndex: Index {
221-
return LazyFilterIndex(base: _base.endIndex)
167+
return _base.endIndex
222168
}
223169

224170
// TODO: swift-3-indexing-model - add docs
@@ -230,12 +176,12 @@ public struct ${Self}<
230176

231177
public func formIndex(after i: inout Index) {
232178
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
233-
var index = i.base
179+
var index = i
234180
_precondition(index != _base.endIndex, "can't advance past endIndex")
235181
repeat {
236182
_base.formIndex(after: &index)
237183
} while index != _base.endIndex && !_predicate(_base[index])
238-
i = LazyFilterIndex(base: index)
184+
i = index
239185
}
240186

241187
% if Traversal == 'Bidirectional':
@@ -247,12 +193,12 @@ public struct ${Self}<
247193

248194
public func formIndex(before i: inout Index) {
249195
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
250-
var index = i.base
196+
var index = i
251197
_precondition(index != _base.startIndex, "can't retreat before startIndex")
252198
repeat {
253199
_base.formIndex(before: &index)
254200
} while !_predicate(_base[index])
255-
i = LazyFilterIndex(base: index)
201+
i = index
256202
}
257203
% end
258204

@@ -261,22 +207,14 @@ public struct ${Self}<
261207
/// - Precondition: `position` is a valid position in `self` and
262208
/// `position != endIndex`.
263209
public subscript(position: Index) -> Base.Iterator.Element {
264-
return _base[position.base]
210+
return _base[position]
265211
}
266212

267-
public subscript(bounds: Range<Index>) -> ${Slice}<${Self}<Base>> {
268-
return ${Slice}(base: self, bounds: bounds)
213+
public subscript(bounds: Range<Index>) -> ${Self}<Base.SubSequence> {
214+
return ${Self}<Base.SubSequence>(_base: _base[bounds], _predicate)
269215
}
270216

271-
// FIXME(ABI)#28 (Associated Types with where clauses): we actually want to add:
272-
//
273-
// typealias SubSequence = ${Self}<Base.SubSequence>
274-
//
275-
// so that all slicing optimizations of the base collection can kick in.
276-
//
277-
// We can't do that right now though, because that would force a lot of
278-
// constraints on `Base.SubSequence`, limiting the possible contexts where
279-
// the `.lazy.filter` API can be used.
217+
public typealias SubSequence = ${Self}<Base.SubSequence>
280218

281219
/// Returns an iterator over the elements of this sequence.
282220
///
@@ -310,11 +248,12 @@ extension LazySequenceProtocol {
310248
% for Traversal in ['Forward', 'Bidirectional']:
311249

312250
extension LazyCollectionProtocol
313-
% if Traversal != 'Forward':
314251
where
252+
% if Traversal != 'Forward':
315253
Self : ${collectionForTraversal(Traversal)},
316-
Elements : ${collectionForTraversal(Traversal)}
254+
Elements : ${collectionForTraversal(Traversal)},
317255
% end
256+
Self.Elements.SubSequence : ${collectionForTraversal(Traversal)}
318257
{
319258
/// Returns the elements of `self` that satisfy `predicate`.
320259
///

test/stdlib/Filter.swift

-6
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ extension LazyFilterSequence where Base : TestProtocol1 {
3232
}
3333
}
3434

35-
extension LazyFilterIndex where Base : TestProtocol1 {
36-
var _baseIsTestProtocol1: Bool {
37-
fatalError("not implemented")
38-
}
39-
}
40-
4135
extension LazyFilterCollection where Base : TestProtocol1 {
4236
var _baseIsTestProtocol1: Bool {
4337
fatalError("not implemented")

test/stdlib/Renames.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func _Filter<S : Sequence>(s: S) {
171171
func _Filter<S>(s: LazyFilterSequence<S>) {
172172
_ = s.generate() // expected-error {{'generate()' has been renamed to 'makeIterator()'}} {{9-17=makeIterator}} {{none}}
173173
}
174-
func _Filter<C : Collection>(c: C) {
174+
func _Filter<C : Collection>(c: C) where C.SubSequence: Collection {
175175
_ = LazyFilterCollection(c) { _ in true} // expected-error {{'init(_:whereElementsSatisfy:)' is unavailable: use '.lazy.filter' on the collection}}
176176
}
177177
func _Filter<C>(c: LazyFilterCollection<C>) {

validation-test/stdlib/Lazy.swift.gyb

+10-14
Original file line numberDiff line numberDiff line change
@@ -638,10 +638,10 @@ tests.test("LazySequence/Sequence") {
638638
}
639639

640640
func expectSequencePassthrough<
641-
S : Sequence,
641+
S : LazySequenceProtocol,
642642
Base : Sequence
643643
>(_ s: S, base: Base, arbitraryElement: S.Iterator.Element, count: Int)
644-
where S : LazySequenceProtocol, Base : LoggingType,
644+
where Base : LoggingType,
645645
Base.Iterator.Element == S.Iterator.Element {
646646
let baseType = type(of: base)
647647

@@ -825,7 +825,7 @@ tests.test("LazyMapCollection/Passthrough") {
825825
let startIndex = CollectionLog.startIndex.expectIncrement(type(of: base)) {
826826
mapped.startIndex
827827
}
828-
let endIndex = CollectionLog.endIndex.expectIncrement(type(of: base)) {
828+
_ = CollectionLog.endIndex.expectIncrement(type(of: base)) {
829829
mapped.endIndex
830830
}
831831
// Not exactly passthrough, because mapping transforms the result
@@ -1023,9 +1023,7 @@ tests.test("ReversedCollection/Lazy") {
10231023
// Given a couple of sequences backed by FilterGenerator's, check that
10241024
// the first selects even numbers and the second selects odd numbers,
10251025
// both from an underlying sequence of whole numbers.
1026-
func checkFilterIteratorBase<
1027-
S : Sequence, I : IteratorProtocol
1028-
>(_ s1: S, _ s2: S)
1026+
func checkFilterIteratorBase< S : Sequence, I>(_ s1: S, _ s2: S)
10291027
where S.Iterator == LazyFilterIterator<I>, I.Element == OpaqueValue<Int> {
10301028
var iter1 = s1.makeIterator()
10311029
expectEqual(0, iter1.next()!.value)
@@ -1098,16 +1096,16 @@ tests.test("LazyFilterIndex/base") {
10981096
let evens = base.lazy.filter { $0.value % 2 == 0 }
10991097
let odds = base.lazy.filter { $0.value % 2 != 0 }
11001098

1101-
expectEqual(base.startIndex, evens.startIndex.base)
1102-
expectEqual(base.index(after: base.startIndex), odds.startIndex.base)
1099+
expectEqual(base.startIndex, evens.startIndex)
1100+
expectEqual(base.index(after: base.startIndex), odds.startIndex)
11031101

11041102
expectEqual(
11051103
base.index(after: base.index(after: base.startIndex)),
1106-
evens.index(after: evens.startIndex).base)
1104+
evens.index(after: evens.startIndex))
11071105

11081106
expectEqual(
11091107
base.index(after: base.index(after: base.index(after: base.startIndex))),
1110-
odds.index(after: odds.startIndex).base)
1108+
odds.index(after: odds.startIndex))
11111109
}
11121110

11131111
tests.test("LazyFilterCollection") {
@@ -1157,8 +1155,7 @@ tests.test("LazyFilterCollection/AssociatedTypes") {
11571155
expectCollectionAssociatedTypes(
11581156
collectionType: Subject.self,
11591157
iteratorType: LazyFilterIterator<Base.Iterator>.self,
1160-
// FIXME(ABI)#80 (Associated Types with where clauses): SubSequence should be `LazyFilterCollection<Base.Slice>`.
1161-
subSequenceType: Slice<Subject>.self,
1158+
subSequenceType: LazyFilterCollection<Base.SubSequence>.self,
11621159
indexType: LazyFilterIndex<Base>.self,
11631160
indexDistanceType: Base.IndexDistance.self,
11641161
indicesType: DefaultIndices<Subject>.self)
@@ -1170,8 +1167,7 @@ tests.test("LazyFilterBidirectionalCollection/AssociatedTypes") {
11701167
expectBidirectionalCollectionAssociatedTypes(
11711168
collectionType: Subject.self,
11721169
iteratorType: LazyFilterIterator<Base.Iterator>.self,
1173-
// FIXME(ABI)#81 (Associated Types with where clauses): SubSequence should be `LazyFilterBidirectionalCollection<Base.Slice>`.
1174-
subSequenceType: BidirectionalSlice<Subject>.self,
1170+
subSequenceType: LazyFilterBidirectionalCollection<Base.SubSequence>.self,
11751171
indexType: LazyFilterIndex<Base>.self,
11761172
indexDistanceType: Base.IndexDistance.self,
11771173
indicesType: DefaultBidirectionalIndices<Subject>.self)

0 commit comments

Comments
 (0)