Skip to content

[stdlib] Collapse sequence and collection wrappers #20221

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 4 commits into from
Nov 14, 2018
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
Original file line number Diff line number Diff line change
Expand Up @@ -87,32 +87,6 @@ public func checkSequence<
expectGE(
expectedCount, sequence.underestimatedCount, message(),
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))

// Test `_copyContents(initializing:)` if we can do so without destroying the
// sequence.
_ = sequence._preprocessingPass { () -> Void in
var count = 0
for _ in sequence { count += 1 }
let ptr = UnsafeMutablePointer<S.Element>.allocate(capacity: count)
let buf = UnsafeMutableBufferPointer(start: ptr, count: count)
var (remainders,writtenUpTo) = sequence._copyContents(initializing: buf)
expectTrue(remainders.next() == nil,
"_copyContents returned unwritten elements")
expectTrue(writtenUpTo == buf.endIndex,
"_copyContents failed to use entire buffer")
expectEqualSequence(expected, buf, message(),
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue)
ptr.deinitialize(count: count)
ptr.deallocate()
}

// Test `_copyToContiguousArray()` if we can do so
// without destroying the sequence.
_ = sequence._preprocessingPass { () -> Void in
let copy = sequence._copyToContiguousArray()
expectEqualSequence(expected, copy, message(),
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue)
}
}

public func checkSequence<
Expand Down Expand Up @@ -206,32 +180,6 @@ public func checkSequence<
expectGE(
expectedCount, sequence.underestimatedCount, message(),
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))

// Test `_copyContents(initializing:)` if we can do so without destroying the
// sequence.
_ = sequence._preprocessingPass { () -> Void in
var count = 0
for _ in sequence { count += 1 }
let ptr = UnsafeMutablePointer<S.Element>.allocate(capacity: count)
let buf = UnsafeMutableBufferPointer(start: ptr, count: count)
var (remainders,writtenUpTo) = sequence._copyContents(initializing: buf)
expectTrue(remainders.next() == nil,
"_copyContents returned unwritten elements")
expectTrue(writtenUpTo == buf.endIndex,
"_copyContents failed to use entire buffer")
expectEqualSequence(expected, buf, message(),
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue)
ptr.deinitialize(count: count)
ptr.deallocate()
}

// Test `_copyToContiguousArray()` if we can do so
// without destroying the sequence.
_ = sequence._preprocessingPass { () -> Void in
let copy = sequence._copyToContiguousArray()
expectEqualSequence(expected, copy, message(),
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line), sameValue: sameValue)
}
}

public func checkSequence<
Expand Down
60 changes: 0 additions & 60 deletions stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1643,14 +1643,6 @@ extension TestSuite {

testNamePrefix += String(describing: S.Type.self)

let isMultiPass = makeSequence([])
._preprocessingPass { true } ?? false
let isEquatableMultiPass = makeSequenceOfEquatable([])
._preprocessingPass { true } ?? false
expectEqual(
isMultiPass, isEquatableMultiPass,
"Two sequence types are of different kinds?")

// FIXME: swift-3-indexing-model: add tests for `underestimatedCount`
// Check that it is non-negative, and an underestimate of the actual
// element count.
Expand All @@ -1667,12 +1659,6 @@ self.test("\(testNamePrefix).contains()/WhereElementIsEquatable/semantics") {
test.expected != nil,
s.contains(wrapValueIntoEquatable(test.element)),
stackTrace: SourceLocStack().with(test.loc))

if !isMultiPass {
expectEqualSequence(
test.expectedLeftoverSequence, s.map(extractValueFromEquatable),
stackTrace: SourceLocStack().with(test.loc))
}
}
}

Expand Down Expand Up @@ -1992,52 +1978,6 @@ self.test("\(testNamePrefix).first(where:)/semantics") {
}
}

//===----------------------------------------------------------------------===//
// _preprocessingPass()
//===----------------------------------------------------------------------===//

self.test("\(testNamePrefix)._preprocessingPass/semantics") {
for test in forEachTests {
let s = makeWrappedSequence(test.sequence.map(OpaqueValue.init))
var wasInvoked = false
let result = s._preprocessingPass {
() -> OpaqueValue<Int> in
wasInvoked = true

expectEqualSequence(
test.sequence,
s.map { extractValue($0).value })

return OpaqueValue(42)
}
if wasInvoked {
expectEqual(42, result?.value)
} else {
expectNil(result)
}
}

for test in forEachTests {
let s = makeWrappedSequence(test.sequence.map(OpaqueValue.init))
var wasInvoked = false
var caughtError: Error?
var result: OpaqueValue<Int>?
do {
result = try s._preprocessingPass {
() -> OpaqueValue<Int> in
wasInvoked = true
throw TestError.error2
}
} catch {
caughtError = error
}
expectNil(result)
if wasInvoked {
expectEqual(TestError.error2, caughtError as? TestError)
}
}
}

//===----------------------------------------------------------------------===//
} // addSequenceTests
}
60 changes: 1 addition & 59 deletions stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,7 @@ public class SequenceLog {
public static var prefixWhile = TypeIndexed(0)
public static var prefixMaxLength = TypeIndexed(0)
public static var suffixMaxLength = TypeIndexed(0)
public static var split = TypeIndexed(0)
public static var _customContainsEquatableElement = TypeIndexed(0)
public static var _preprocessingPass = TypeIndexed(0)
public static var _copyToContiguousArray = TypeIndexed(0)
public static var _copyContents = TypeIndexed(0)
// Collection
Expand Down Expand Up @@ -202,7 +200,6 @@ extension LoggingSequence: LoggingType {
extension LoggingSequence: Sequence {
public typealias Element = Base.Element
public typealias Iterator = LoggingIterator<Base.Iterator>
public typealias SubSequence = Base.SubSequence

public func makeIterator() -> Iterator {
SequenceLog.makeIterator[selfType] += 1
Expand All @@ -214,67 +211,11 @@ extension LoggingSequence: Sequence {
return base.underestimatedCount
}

public func dropFirst(_ n: Int) -> SubSequence {
SequenceLog.dropFirst[selfType] += 1
return base.dropFirst(n)
}

public func dropLast(_ n: Int) -> SubSequence {
SequenceLog.dropLast[selfType] += 1
return base.dropLast(n)
}

public func drop(
while predicate: (Element) throws -> Bool
) rethrows -> SubSequence {
SequenceLog.dropWhile[selfType] += 1
return try base.drop(while: predicate)
}

public func prefix(_ maxLength: Int) -> SubSequence {
SequenceLog.prefixMaxLength[selfType] += 1
return base.prefix(maxLength)
}

public func prefix(
while predicate: (Element) throws -> Bool
) rethrows -> SubSequence {
SequenceLog.prefixWhile[selfType] += 1
return try base.prefix(while: predicate)
}

public func suffix(_ maxLength: Int) -> SubSequence {
SequenceLog.suffixMaxLength[selfType] += 1
return base.suffix(maxLength)
}

public func split(
maxSplits: Int = Int.max,
omittingEmptySubsequences: Bool = true,
whereSeparator isSeparator: (Element) throws -> Bool
) rethrows -> [SubSequence] {
SequenceLog.split[selfType] += 1
return try base.split(
maxSplits: maxSplits,
omittingEmptySubsequences: omittingEmptySubsequences,
whereSeparator: isSeparator)
}

public func _customContainsEquatableElement(_ element: Element) -> Bool? {
SequenceLog._customContainsEquatableElement[selfType] += 1
return base._customContainsEquatableElement(element)
}

/// If `self` is multi-pass (i.e., a `Collection`), invoke
/// `preprocess` on `self` and return its result. Otherwise, return
/// `nil`.
public func _preprocessingPass<R>(
_ preprocess: () throws -> R
) rethrows -> R? {
SequenceLog._preprocessingPass[selfType] += 1
return try base._preprocessingPass(preprocess)
}

/// Create a native array buffer containing the elements of `self`,
/// in the same order.
public func _copyToContiguousArray() -> ContiguousArray<Element> {
Expand All @@ -297,6 +238,7 @@ public typealias LoggingCollection<Base: Collection> = LoggingSequence<Base>
extension LoggingCollection: Collection {
public typealias Index = Base.Index
public typealias Indices = Base.Indices
public typealias SubSequence = Base.SubSequence

public var startIndex: Index {
CollectionLog.startIndex[selfType] += 1
Expand Down
3 changes: 1 addition & 2 deletions stdlib/private/StdlibUnittest/StdlibUnittest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,7 @@ public func expectMutableSliceType<X : MutableCollection>(
/// to be.
public func expectSequenceAssociatedTypes<X : Sequence>(
sequenceType: X.Type,
iteratorType: X.Iterator.Type,
subSequenceType: X.SubSequence.Type
iteratorType: X.Iterator.Type
) {}

/// Check that all associated types of a `Collection` are what we expect them
Expand Down
1 change: 0 additions & 1 deletion stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ set(SWIFTLIB_ESSENTIAL
SipHash.swift
Sequence.swift
SequenceAlgorithms.swift
SequenceWrapper.swift
Set.swift
SetAlgebra.swift
SetAnyHashableExtensions.swift
Expand Down
23 changes: 8 additions & 15 deletions stdlib/public/core/Collection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ extension IndexingIterator: IteratorProtocol, Sequence {
/// or bidirectional collection must traverse the entire collection to count
/// the number of contained elements, accessing its `count` property is an
/// O(*n*) operation.
public protocol Collection: Sequence where SubSequence: Collection {
public protocol Collection: Sequence {
// FIXME: ideally this would be in MigrationSupport.swift, but it needs
// to be on the protocol instead of as an extension
@available(*, deprecated/*, obsoleted: 5.0*/, message: "all index distances are now of type Int")
Expand Down Expand Up @@ -390,7 +390,10 @@ public protocol Collection: Sequence where SubSequence: Collection {
/// This associated type appears as a requirement in the `Sequence`
/// protocol, but it is restated here with stricter constraints. In a
/// collection, the subsequence should also conform to `Collection`.
associatedtype SubSequence = Slice<Self> where SubSequence.Index == Index
associatedtype SubSequence: Collection = Slice<Self>
where SubSequence.Index == Index,
Element == SubSequence.Element,
SubSequence.SubSequence == SubSequence

/// Accesses the element at the specified position.
///
Expand Down Expand Up @@ -1223,7 +1226,7 @@ extension Collection {
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the number of
/// elements to drop from the beginning of the collection.
@inlinable
public __consuming func dropFirst(_ k: Int) -> SubSequence {
public __consuming func dropFirst(_ k: Int = 1) -> SubSequence {
_precondition(k >= 0, "Can't drop a negative number of elements from a collection")
let start = index(startIndex, offsetBy: k, limitedBy: endIndex) ?? endIndex
return self[start..<endIndex]
Expand All @@ -1250,15 +1253,15 @@ extension Collection {
/// `RandomAccessCollection`; otherwise, O(*n*), where *n* is the length of
/// the collection.
@inlinable
public __consuming func dropLast(_ k: Int) -> SubSequence {
public __consuming func dropLast(_ k: Int = 1) -> SubSequence {
_precondition(
k >= 0, "Can't drop a negative number of elements from a collection")
let amount = Swift.max(0, count - k)
let end = index(startIndex,
offsetBy: amount, limitedBy: endIndex) ?? endIndex
return self[startIndex..<end]
}

/// Returns a subsequence by skipping elements while `predicate` returns
/// `true` and returning the remaining elements.
///
Expand Down Expand Up @@ -1665,13 +1668,3 @@ extension Collection where SubSequence == Self {
self = self[index(startIndex, offsetBy: k)..<endIndex]
}
}

extension Collection {
@inlinable
@inline(__always)
public func _preprocessingPass<R>(
_ preprocess: () throws -> R
) rethrows -> R? {
return try preprocess()
}
}
3 changes: 3 additions & 0 deletions stdlib/public/core/Dictionary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,8 @@ extension Dictionary {
}

extension Dictionary: Collection {
public typealias SubSequence = Slice<Dictionary>

/// The position of the first element in a nonempty dictionary.
///
/// If the collection is empty, `startIndex` is equal to `endIndex`.
Expand Down Expand Up @@ -1321,6 +1323,7 @@ extension Dictionary {
: Collection, Equatable,
CustomStringConvertible, CustomDebugStringConvertible {
public typealias Element = Key
public typealias SubSequence = Slice<Dictionary.Keys>

@usableFromInline
internal var _variant: Dictionary<Key, Value>._Variant
Expand Down
Loading