Skip to content

[Stdlib] Rationalize the SubSequence type of a filtered Collection. #8898

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

Closed
wants to merge 1 commit into from
Closed
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
103 changes: 21 additions & 82 deletions stdlib/public/core/Filter.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -97,65 +97,8 @@ public struct LazyFilterSequence<Base : Sequence>
}

/// The `Index` used for subscripting a `LazyFilterCollection`.
///
/// The positions of a `LazyFilterIndex` correspond to those positions
/// `p` in its underlying collection `c` such that `c[p]`
/// satisfies the predicate with which the `LazyFilterIndex` was
/// initialized.
///
/// - Note: The performance of advancing a `LazyFilterIndex`
/// depends on how sparsely the filtering predicate is satisfied,
/// and may not offer the usual performance given by models of
/// `Collection`.
public struct LazyFilterIndex<Base : Collection> {

/// The position corresponding to `self` in the underlying collection.
public let base: Base.Index
}

extension LazyFilterIndex : Comparable {
public static func == (
lhs: LazyFilterIndex<Base>,
rhs: LazyFilterIndex<Base>
) -> Bool {
return lhs.base == rhs.base
}

public static func != (
lhs: LazyFilterIndex<Base>,
rhs: LazyFilterIndex<Base>
) -> Bool {
return lhs.base != rhs.base
}

public static func < (
lhs: LazyFilterIndex<Base>,
rhs: LazyFilterIndex<Base>
) -> Bool {
return lhs.base < rhs.base
}

public static func <= (
lhs: LazyFilterIndex<Base>,
rhs: LazyFilterIndex<Base>
) -> Bool {
return lhs.base <= rhs.base
}

public static func >= (
lhs: LazyFilterIndex<Base>,
rhs: LazyFilterIndex<Base>
) -> Bool {
return lhs.base >= rhs.base
}

public static func > (
lhs: LazyFilterIndex<Base>,
rhs: LazyFilterIndex<Base>
) -> Bool {
return lhs.base > rhs.base
}
}
@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.
Expand All @@ -170,20 +113,23 @@ extension LazyFilterIndex : Comparable {
/// underlying collection that satisfy a predicate.
///
/// - Note: The performance of accessing `startIndex`, `first`, any methods
/// that depend on `startIndex`, or of advancing a `LazyFilterIndex` depends
/// that depend on `startIndex`, or of advancing an index depends
/// on how sparsely the filtering predicate is satisfied, and may not offer
/// the usual performance given by `Collection`. Be aware, therefore, that
/// general operations on `LazyFilterCollection` instances may not have the
/// documented complexity.
public struct ${Self}<
Base : ${collectionForTraversal(Traversal)}
> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)} {
> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)}
// FIXME(ABI): Recursive protocol conformances
where Base.SubSequence: ${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 = LazyFilterIndex<Base>
public typealias Index = Base.Index

public typealias IndexDistance = Base.IndexDistance

Expand All @@ -209,7 +155,7 @@ public struct ${Self}<
while index != _base.endIndex && !_predicate(_base[index]) {
_base.formIndex(after: &index)
}
return LazyFilterIndex(base: index)
return index
}

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

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

public func formIndex(after i: inout Index) {
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
var index = i.base
var index = i
_precondition(index != _base.endIndex, "can't advance past endIndex")
repeat {
_base.formIndex(after: &index)
} while index != _base.endIndex && !_predicate(_base[index])
i = LazyFilterIndex(base: index)
i = index
}

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

public func formIndex(before i: inout Index) {
// TODO: swift-3-indexing-model: _failEarlyRangeCheck i?
var index = i.base
var index = i
_precondition(index != _base.startIndex, "can't retreat before startIndex")
repeat {
_base.formIndex(before: &index)
} while !_predicate(_base[index])
i = LazyFilterIndex(base: index)
i = index
}
% end

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

public subscript(bounds: Range<Index>) -> ${Slice}<${Self}<Base>> {
return ${Slice}(base: self, bounds: bounds)
public subscript(bounds: Range<Index>) -> ${Self}<Base.SubSequence> {
return ${Self}<Base.SubSequence>(_base: _base[bounds], _predicate)
}

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

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

extension LazyCollectionProtocol
% if Traversal != 'Forward':
where
% if Traversal != 'Forward':
Self : ${collectionForTraversal(Traversal)},
Elements : ${collectionForTraversal(Traversal)}
Elements : ${collectionForTraversal(Traversal)},
% end
Self.Elements.SubSequence : ${collectionForTraversal(Traversal)}
{
/// Returns the elements of `self` that satisfy `predicate`.
///
Expand Down
6 changes: 0 additions & 6 deletions test/stdlib/Filter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ extension LazyFilterSequence where Base : TestProtocol1 {
}
}

extension LazyFilterIndex where Base : TestProtocol1 {
var _baseIsTestProtocol1: Bool {
fatalError("not implemented")
}
}

extension LazyFilterCollection where Base : TestProtocol1 {
var _baseIsTestProtocol1: Bool {
fatalError("not implemented")
Expand Down
2 changes: 1 addition & 1 deletion test/stdlib/Renames.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func _Filter<S : Sequence>(s: S) {
func _Filter<S>(s: LazyFilterSequence<S>) {
_ = s.generate() // expected-error {{'generate()' has been renamed to 'makeIterator()'}} {{9-17=makeIterator}} {{none}}
}
func _Filter<C : Collection>(c: C) {
func _Filter<C : Collection>(c: C) where C.SubSequence: Collection {
_ = LazyFilterCollection(c) { _ in true} // expected-error {{'init(_:whereElementsSatisfy:)' is unavailable: use '.lazy.filter' on the collection}}
}
func _Filter<C>(c: LazyFilterCollection<C>) {
Expand Down
24 changes: 10 additions & 14 deletions validation-test/stdlib/Lazy.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -638,10 +638,10 @@ tests.test("LazySequence/Sequence") {
}

func expectSequencePassthrough<
S : Sequence,
S : LazySequenceProtocol,
Base : Sequence
>(_ s: S, base: Base, arbitraryElement: S.Iterator.Element, count: Int)
where S : LazySequenceProtocol, Base : LoggingType,
where Base : LoggingType,
Base.Iterator.Element == S.Iterator.Element {
let baseType = type(of: base)

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

expectEqual(base.startIndex, evens.startIndex.base)
expectEqual(base.index(after: base.startIndex), odds.startIndex.base)
expectEqual(base.startIndex, evens.startIndex)
expectEqual(base.index(after: base.startIndex), odds.startIndex)

expectEqual(
base.index(after: base.index(after: base.startIndex)),
evens.index(after: evens.startIndex).base)
evens.index(after: evens.startIndex))

expectEqual(
base.index(after: base.index(after: base.index(after: base.startIndex))),
odds.index(after: odds.startIndex).base)
odds.index(after: odds.startIndex))
}

tests.test("LazyFilterCollection") {
Expand Down Expand Up @@ -1157,8 +1155,7 @@ tests.test("LazyFilterCollection/AssociatedTypes") {
expectCollectionAssociatedTypes(
collectionType: Subject.self,
iteratorType: LazyFilterIterator<Base.Iterator>.self,
// FIXME(ABI)#80 (Associated Types with where clauses): SubSequence should be `LazyFilterCollection<Base.Slice>`.
subSequenceType: Slice<Subject>.self,
subSequenceType: LazyFilterCollection<Base.SubSequence>.self,
indexType: LazyFilterIndex<Base>.self,
indexDistanceType: Base.IndexDistance.self,
indicesType: DefaultIndices<Subject>.self)
Expand All @@ -1170,8 +1167,7 @@ tests.test("LazyFilterBidirectionalCollection/AssociatedTypes") {
expectBidirectionalCollectionAssociatedTypes(
collectionType: Subject.self,
iteratorType: LazyFilterIterator<Base.Iterator>.self,
// FIXME(ABI)#81 (Associated Types with where clauses): SubSequence should be `LazyFilterBidirectionalCollection<Base.Slice>`.
subSequenceType: BidirectionalSlice<Subject>.self,
subSequenceType: LazyFilterBidirectionalCollection<Base.SubSequence>.self,
indexType: LazyFilterIndex<Base>.self,
indexDistanceType: Base.IndexDistance.self,
indicesType: DefaultBidirectionalIndices<Subject>.self)
Expand Down