I've been wondering about whether or not is generally safe to ignore overflow when incrementing/decrementing Collection indexes. I'm quite sure that it is, but I'd like to check.
My understanding has been that Collection doesn't require invalid operations to necessarily trap at runtime: only to produce predictable results. So incrementing a Collection's endIndex
is allowed to keep returning endIndex
forever, or it might return some sentinel "invalid" index, or it might trap, or loop back to startIndex
... if that's what the implementors want to do. By extension, no part of Collection's documented requirements require a conformer to trap if subscripted with an invalid index; a custom Collection of UInt8s might decide to return 0, 1, 2, or any arbitrary value in that case.
I've seen this come up in various threads over the years, and the prevailing wisdom is always that Collection doesn't require you to trigger runtime errors.
The caveat to all of this is that you must not compromise memory safety: so if you use the index as, say, an offset in to an UnsafeBufferPointer
, you must bounds-check that access first. That's the thing that provides safety; not overflow checks.
If we look at how Array is implemented, we see that it does something similar to this: index(after:)
will keep returning ever-greater integers, even beyond the bounds of the Array - which does surprise some users, but is not considered unsafe because all accesses are bounds-checked.
So am I correct in saying that if Array (or UnsafeBufferPointer, or any custom Collection) were to use wrapping addition for its indexes, and not fail on overflow, that it would not compromise memory safety as long as accesses are bounds-checked? Is that a principle which can scale to the general case?
The "Advanced Operators" section of TSPL appears to indicate that signed integer overflow is defined in Swift - so AFAICT there should be no problem with this. Such a Collection would not invoke undefined behaviour, not produce unpredictable results, and not access any memory it shouldn't. Seems okay to me.
My next question is: assuming that this behaviour is safe, why don't any types in the standard library do it? Array, and even the UnsafeBufferPointer types, all use addition which can trap on overflow.