From 2d719d75a2065f213e58a5164384a3d2fcf9b59a Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Sat, 10 Apr 2021 00:17:50 -0700 Subject: [PATCH] Fix nondeterministic compiler crash in debug builds (#18) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. 😝 --- .../OrderedSet+RandomAccessCollection.swift | 47 +++++++++++++++ .../OrderedSet/OrderedSet+Sequence.swift | 57 ------------------- 2 files changed, 47 insertions(+), 57 deletions(-) delete mode 100644 Sources/OrderedCollections/OrderedSet/OrderedSet+Sequence.swift diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+RandomAccessCollection.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+RandomAccessCollection.swift index ac7e1e65d..5c21e7eff 100644 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+RandomAccessCollection.swift +++ b/Sources/OrderedCollections/OrderedSet/OrderedSet+RandomAccessCollection.swift @@ -9,6 +9,53 @@ // //===----------------------------------------------------------------------===// +extension OrderedSet: Sequence { + /// The type that allows iteration over an ordered set's elements. + public typealias Iterator = IndexingIterator + + @inlinable + public func _customContainsEquatableElement(_ element: Element) -> Bool? { + _find(element).index != nil + } + + @inlinable + public __consuming func _copyToContiguousArray() -> ContiguousArray { + _elements._copyToContiguousArray() + } + + @inlinable + public __consuming func _copyContents( + initializing ptr: UnsafeMutableBufferPointer + ) -> (Iterator, UnsafeMutableBufferPointer.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( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R? { + try _elements.withContiguousStorageIfAvailable(body) + } +} + extension OrderedSet: RandomAccessCollection { /// The index type for ordered sets, `Int`. /// diff --git a/Sources/OrderedCollections/OrderedSet/OrderedSet+Sequence.swift b/Sources/OrderedCollections/OrderedSet/OrderedSet+Sequence.swift deleted file mode 100644 index 663c67069..000000000 --- a/Sources/OrderedCollections/OrderedSet/OrderedSet+Sequence.swift +++ /dev/null @@ -1,57 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2021 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -extension OrderedSet: Sequence { - /// The type that allows iteration over an ordered set's elements. - public typealias Iterator = IndexingIterator - - @inlinable - public func _customContainsEquatableElement(_ element: Element) -> Bool? { - _find(element).index != nil - } - - @inlinable - public __consuming func _copyToContiguousArray() -> ContiguousArray { - _elements._copyToContiguousArray() - } - - @inlinable - public __consuming func _copyContents( - initializing ptr: UnsafeMutableBufferPointer - ) -> (Iterator, UnsafeMutableBufferPointer.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( - _ body: (UnsafeBufferPointer) throws -> R - ) rethrows -> R? { - try _elements.withContiguousStorageIfAvailable(body) - } -}