Skip to content

StepRangeLen should try to detect and error on overflow/wrapping #59032

@mbauman

Description

@mbauman

Split from #58846; I think it's putatively a bug that we're not doing this. Constructing such a range violates so many assumptions of AbstractRange that they are quite the broken object. It feels like it should be possible to detect such a thing, but I don't know that it is (without demanding some new method implementations).

The biggest problem is that the constructor is not constrained by any particular types beyond len::Integer.

In fact, all we know is that the all currently constructable StepRangeLen(ref::T, step::S, len::Integer)s have types that support:

  • *(::S, ::Integer)::R1
  • +(::T, ::R1)::R2
  • T(::R2) (although perhaps this should be convert(::Type{T}, ::R2))

That's... it. If you do subsequent arithmetic on the range itself there are a few other conversions and arithmetic, but those aren't obvious to exercise. The above is all you need to construct, index, and collect... and that's all that underpins a lot of the high-level functionality that can go sideways (like extrema, for example).

If we define that T(::R2) constructor for Time (as in #58846), the StepRangeLen(::Time, ::Hour, ::Integer) becomes quite the adversarial case:

julia> using Dates

julia> @eval Dates Time(t::Time) = t
Time

julia> StepRangeLen(Time(0), Hour(13), 4)
Time(0):Hour(13):Time(15)

julia> println(collect(range(Time(0), step=Hour(13), length=4)))
Time[Time(0), Time(13), Time(2), Time(15)]

julia> Hour(13)*3
39 hours

julia> Time(0) + Hour(39)
15:00:00

I have absolutely no idea how we could ever generically detect this — even if we assumed the availability of more arithmetic or comparisons or typemax — unless we iterate the entire range upon construction to demand monotonicity.

We could require these types implement Base.Checked arithmetic, but I'm quite certain that would be highly breaking for existing non-broken functionality in packages like Mods and Furlongs and Unitful. Or we could alternatively take a tiered strategy:

  • We know that all the builtin integers can use Checked.mul_with_overflow and Checked.add_with_overflow
  • We know that all AbstractFloats won't wrap around (but there are indeed many other potential problems there)
  • Some ::Numbers can use widen (Unitful does define this, but Furlongs and Mods do not)
  • Everything else? Perhaps demand Checked arithmetic? Or just do a best-effort thing?

Metadata

Metadata

Assignees

No one assigned

    Labels

    rangesEverything AbstractRange

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions