Skip to content

Commit

Permalink
Fix nondeterministic compiler crash in debug builds (#18)
Browse files Browse the repository at this point in the history
So as it turns out, the Swift compiler’s MergeModules phase (which SwiftPM uses in Debug builds) has a known issue with dependency cycles between separate source files — it rarely triggers, but when it does, swiftc hits an assertion complaining about a deserialization failure.

The v0.0.1 of Swift Collections happens to include such a cycle, but it only triggers the assertion on a small fraction of machines. I don’t yet know what exactly makes it nondeterministic, but it is reproducible by randomly reshuffling the source filenames of the OrderedSet implementation — a couple dozen randomizations will eventually lead to a state where it happens in every build.

The workaround is to put OrderedSet’s Sequence and RandomAccessCollection conformances in the same file. (OrderedSet uses the standard IndexingIterator, which requires a Collection conformance, while Collection itself refines Sequence — I believe this is the dependency cycle that is causing all this problem.)

Unfortunately, until we figure out what triggers the nondeterminism (or until the MergeModules action is replaced with something that actually, you know, works, this issue may keep randomly reoccurring. None of this makes much sense, but here we are. 😝
  • Loading branch information
lorentey authored Apr 10, 2021
1 parent ff73632 commit 2d719d7
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,53 @@
//
//===----------------------------------------------------------------------===//

extension OrderedSet: Sequence {
/// The type that allows iteration over an ordered set's elements.
public typealias Iterator = IndexingIterator<Self>

@inlinable
public func _customContainsEquatableElement(_ element: Element) -> Bool? {
_find(element).index != nil
}

@inlinable
public __consuming func _copyToContiguousArray() -> ContiguousArray<Element> {
_elements._copyToContiguousArray()
}

@inlinable
public __consuming func _copyContents(
initializing ptr: UnsafeMutableBufferPointer<Element>
) -> (Iterator, UnsafeMutableBufferPointer<Element>.Index) {
guard !isEmpty else { return (makeIterator(), 0) }
let copied: Int = _elements.withUnsafeBufferPointer { buffer in
guard let p = ptr.baseAddress else {
preconditionFailure("Attempt to copy contents into nil buffer pointer")
}
let c = Swift.min(buffer.count, ptr.count)
p.initialize(from: buffer.baseAddress!, count: c)
return c
}
return (Iterator(_elements: self, _position: copied), copied)
}

/// Call `body(p)`, where `p` is a buffer pointer to the collection’s
/// contiguous storage. Ordered sets always have contiguous storage.
///
/// - Parameter body: A function to call. The function must not escape its
/// unsafe buffer pointer argument.
///
/// - Returns: The value returned by `body`.
///
/// - Complexity: O(1) (ignoring time spent in `body`)
@inlinable
public func withContiguousStorageIfAvailable<R>(
_ body: (UnsafeBufferPointer<Element>) throws -> R
) rethrows -> R? {
try _elements.withContiguousStorageIfAvailable(body)
}
}

extension OrderedSet: RandomAccessCollection {
/// The index type for ordered sets, `Int`.
///
Expand Down
57 changes: 0 additions & 57 deletions Sources/OrderedCollections/OrderedSet/OrderedSet+Sequence.swift

This file was deleted.

0 comments on commit 2d719d7

Please sign in to comment.