Fatal error: Only BidirectionalCollections can be advanced by a negative amount

I'm at a loss here:

let arr = Array(0..<1000)
var s = Set(arr)

in an Xcode-beta (Swift 4.2) playground gives me

file:///Users/me/dev/swift/MyPlayground.playground: error: Playground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

Fatal error: Only BidirectionalCollections can be advanced by a negative amount

What am I doing wrong?

PS: it seems that it may not be 100% reproduceable, but adding .shuffled() to arr seems to trigger it.

1 Like

I think this is a bug that we've seen with certain kinds of linkage and how the conditionally-bidirectional version of advance works. @moiseev or @lorentey might know where we stand with investigating it.

1 Like

Is there a reasonable workaround?

Simple workaround:

let arr = Array(0 ..< 1000)
var s: Set<Int> = []
arr.forEach { s.insert($0) }

Playgrounds in the current Xcode beta build have an issue where they can't print forward-only collections with more than 100 elements. I know of no easy workaround; the playground has to be reorganized to not produce such results. This can be tricky, but @Alejandro's solution can be adapted to cover most cases.

Thanks. The workaround won’t work either, though, when the the array is 10 million integers. Because the playground insists on updating the value of the incrementer in real time, the code eventually takes up several dozen gigs of memory and pegs the cpu.

One trick to work around that is to define a utility function in a separate source file in the playground's Sources folder:

public func assign<T>(_ value: T, to target: inout T) -> String {
  target = value
  return "" // Dummy value to display as result in playground

Then this will work:

let arr = Array(0 ..< 10_000_000)
var s: Set<Int> = []
assign(Set(arr), to: &s)

Obviously this isn't very convenient. A better approach may be to customize Set's playground description:

extension Set: CustomPlaygroundDisplayConvertible {
    public var playgroundDescription: Any {
        return "Set with \(count) elements"

let arr = Array(0 ..< 10_000_000)
let s = Set(arr) // Won't trap, prints "Set with 10000000 elements"

(Conforming stdlib types to stdlib protocols outside the stdlib is usually not a good idea -- a future version of the stdlib may add the conformance itself. However, it's okay to do it to temporarily work around an Xcode issue, as long as we won't forget to remove the workaround when the bug gets fixed.)


Thank you, @lorentey - that second option is perfect.

Do I need to report this bug anywhere? (Sounds like it's already known.)

It never hurts to file a radar! Even if it ends up as a duplicate, you'll get to see the status of the original issue.