Unify and expand Range and its likes

PartialRangeFrom is not a BidirectionalCollection, so calling reversed() on it does not produce a ReversedCollection.

Instead it produces an Array.

The program hangs while attempting to materialize that array. This is exactly the same behavior you would see when converting any other very long (or endless) sequence into an array.

• • •

To “fix” the behavior, we could introduce a deprecated overload:

extension PartialRangeFrom where Bound: Strideable, Bound.Stride: SignedInteger {
  @available(*, deprecated, message: "Cannot reverse a PartialRangeFrom")
  func reversed() -> [Bound] { fatalError() }
}

That way attempting to call reversed on a PartialRangeFrom would give a compile-time warning and trap at runtime, instead of hanging.

The hang would still happen if reversed was called on such a value in a generic context, but such is life.

• • •

Interestingly, this code traps at runtime for me (Xcode 11.3.1, Swift 5.1.3):

let a = (1 as Int8)...
let b = a.reversed()  // Execution was interrupted, reason: EXC_BAD_INSTRUCTION 
print(b.count)

And indeed so does this:

let a = Array((1 as Int8)...)

Or even this:

let a = ((1 as Int8)...).max()

The implication is clearly that partial ranges in fact model truly unbounded, one-sided intervals, not restricted to the set of representable values for the element type.

8 Likes