From 4dc5ee7d16cea14636ae24f0de946daa27bd8d85 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Sat, 5 Nov 2022 19:02:25 -0400 Subject: [PATCH 01/10] Move ranges from ArrayInterface to Static --- src/Static.jl | 2 + src/ranges.jl | 358 +++++++++++++++++++++++++++++++++++++++++++++++ test/ranges.jl | 98 +++++++++++++ test/runtests.jl | 3 + 4 files changed, 461 insertions(+) create mode 100644 src/ranges.jl create mode 100644 test/ranges.jl diff --git a/src/Static.jl b/src/Static.jl index 5708589..9ce76e1 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -941,4 +941,6 @@ function Base.show(io::IO, m::MIME"text/plain", @nospecialize(x::NDIndex)) show(io, m, Tuple(x)) end +include("ranges.jl") + end diff --git a/src/ranges.jl b/src/ranges.jl new file mode 100644 index 0000000..1d2256b --- /dev/null +++ b/src/ranges.jl @@ -0,0 +1,358 @@ + +_int(x::Integer) = Int(x) +_int(@nospecialize x::Union{StaticInt,Int}) = x + +""" + OptionallyStaticUnitRange(start, stop) <: AbstractUnitRange{Int} + +Similar to `UnitRange` except each field may be an `Int` or `StaticInt`. An +`OptionallyStaticUnitRange` is intended to be constructed internally from other valid +indices. Therefore, users should not expect the same checks are used to ensure construction +of a valid `OptionallyStaticUnitRange` as a `UnitRange`. +""" +struct OptionallyStaticUnitRange{F<:Union{Int,StaticInt},L<:Union{Int,StaticInt}} <: AbstractUnitRange{Int} + start::F + stop::L + + function OptionallyStaticUnitRange(start::Union{Int,StaticInt}, stop::Union{Int,StaticInt}) + new{typeof(start),typeof(stop)}(start, stop) + end + function OptionallyStaticUnitRange(start, stop) + OptionallyStaticUnitRange(_int(start), _int(stop)) + end + OptionallyStaticUnitRange(@nospecialize x::OptionallyStaticUnitRange) = x + function OptionallyStaticUnitRange(x::AbstractRange) + step(x) == 1 && return OptionallyStaticUnitRange(static_first(x), static_last(x)) + + errmsg(x) = throw(ArgumentError("step must be 1, got $(step(x))")) # avoid GC frame + errmsg(x) + end + OptionallyStaticUnitRange{F,L}(x::AbstractRange) where {F,L} = OptionallyStaticUnitRange(x) + function OptionallyStaticUnitRange{StaticInt{F},StaticInt{L}}() where {F,L} + new{StaticInt{F},StaticInt{L}}() + end +end + +""" + OptionallyStaticStepRange(start, step, stop) <: OrdinalRange{Int,Int} + +Similarly to [`OptionallyStaticUnitRange`](@ref), `OptionallyStaticStepRange` permits +a combination of static and standard primitive `Int`s to construct a range. It +specifically enables the use of ranges without a step size of 1. It may be constructed +through the use of `OptionallyStaticStepRange` directly or using static integers with +the range operator (i.e., `:`). + +```julia +julia> using Static + +julia> x = static(2); + +julia> x:x:10 +static(2):static(2):10 + +julia> Static.OptionallyStaticStepRange(x, x, 10) +static(2):static(2):10 + +``` +""" +struct OptionallyStaticStepRange{F<:Union{Int,StaticInt},S<:Union{Int,StaticInt},L<:Union{Int,StaticInt}} <: OrdinalRange{Int,Int} + start::F + step::S + stop::L + + global function _OptionallyStaticStepRange( + @nospecialize(start::Union{Int,StaticInt}), + @nospecialize(step::Union{Int,StaticInt}), + @nospecialize(stop::Union{Int,StaticInt}) + ) + new{typeof(start),typeof(step),typeof(stop)}(start, step, stop) + end +end +@noinline function OptionallyStaticStepRange( + @nospecialize(start::Union{Int,StaticInt}), + step::StaticInt{0}, + @nospecialize(stop::Union{Int,StaticInt}) +) + throw(ArgumentError("step cannot be zero")) +end +# we don't need to check the `stop` if we know it acts like a unit range +function OptionallyStaticStepRange( + @nospecialize(start::Union{Int,StaticInt}), + step::StaticInt{1}, + @nospecialize(stop::Union{Int,StaticInt}) +) + _OptionallyStaticStepRange(start, step, stop) +end +function OptionallyStaticStepRange( + @nospecialize(start::Union{Int,StaticInt}), + @nospecialize(step::StaticInt), + @nospecialize(stop::Union{Int,StaticInt}) +) + _OptionallyStaticStepRange(start, step, _steprange_last(start, step, stop)) +end +function OptionallyStaticStepRange(start, step, stop) + OptionallyStaticStepRange(_int(start), _int(step), _int(stop)) +end +function OptionallyStaticStepRange( + @nospecialize(start::Union{Int,StaticInt}), + step::Int, + @nospecialize(stop::Union{Int,StaticInt}) +) + if step === 0 + throw(ArgumentError("step cannot be zero")) + else + _OptionallyStaticStepRange(start, step, _steprange_last(start, step, stop)) + end +end +OptionallyStaticStepRange(@nospecialize x::OptionallyStaticStepRange) = x +function OptionallyStaticStepRange(x::AbstractRange) + _OptionallyStaticStepRange(_int(first(x)), _int(step(x)), _int(last(x))) +end + +# to make StepRange constructor inlineable, so optimizer can see `step` value +@inline function _steprange_last(start::StaticInt, step::StaticInt, stop::StaticInt) + StaticInt(_steprange_last(Int(start), Int(step), Int(stop))) +end +@inline function _steprange_last( + start::Union{StaticInt,Int}, + step::Union{StaticInt,Int}, + stop::Union{StaticInt,Int} +) + _steprange_last(Int(start), Int(step), Int(stop)) +end +@inline function _steprange_last(start::Int, step::Int, stop::Int) + if stop === start + return stop + elseif step > 0 + if stop > start + return stop - rem(stop - start, step) + else + return start - 1 + end + else + if stop > start + return start + 1 + else + return stop + rem(start - stop, -step) + end + end +end + +""" + SUnitRange(start::Int, stop::Int) + +An alias for `OptionallyStaticUnitRange` where both the start and stop are known statically. +""" +const SUnitRange{F,L} = OptionallyStaticUnitRange{StaticInt{F},StaticInt{L}} +SUnitRange(start::Int, stop::Int) = SUnitRange{start,stop}() + +""" + SOneTo(n::Int) + +An alias for `OptionallyStaticUnitRange` usfeul for statically sized axes. +""" +const SOneTo{L} = SUnitRange{1,L} +SOneTo(n::Int) = SOneTo{n}() + +const OptionallyStaticRange = Union{<:OptionallyStaticUnitRange,<:OptionallyStaticStepRange} + +# these probide a generic method for extracting potentially static values. +static_first(x::Base.OneTo) = StaticInt(1) +static_first(x::Union{Base.Slice,Base.IdentityUnitRange}) = static_first(x.indices) +static_first(x::OptionallyStaticRange) = getfield(x, :start) +static_first(x) = first(x) + +static_step(x::AbstractUnitRange) = StaticInt(1) +static_step(x::OptionallyStaticStepRange) = getfield(x, :step) +static_step(x) = step(x) + +static_last(x::OptionallyStaticRange) = getfield(x, :stop) +static_last(x) = last(x) +static_last(x::Union{Base.Slice,Base.IdentityUnitRange}) = static_last(x.indices) + +Base.first(x::OptionallyStaticRange) = Int(static_first(x)) +Base.step(x::OptionallyStaticStepRange) = Int(static_step(x)) +Base.last(x::OptionallyStaticRange) = Int(static_last(x)) + +# FIXME this line causes invalidations +Base.:(:)(L::Integer, ::StaticInt{U}) where {U} = OptionallyStaticUnitRange(L, StaticInt(U)) +Base.:(:)(::StaticInt{L}, U::Integer) where {L} = OptionallyStaticUnitRange(StaticInt(L), U) +function Base.:(:)(::StaticInt{L}, ::StaticInt{U}) where {L,U} + OptionallyStaticUnitRange(StaticInt(L), StaticInt(U)) +end +function Base.:(:)(::StaticInt{F}, ::StaticInt{S}, ::StaticInt{L}) where {F,S,L} + OptionallyStaticStepRange(StaticInt(F), StaticInt(S), StaticInt(L)) +end +function Base.:(:)(start::Integer, ::StaticInt{S}, ::StaticInt{L}) where {S,L} + OptionallyStaticStepRange(start, StaticInt(S), StaticInt(L)) +end +function Base.:(:)(::StaticInt{F}, ::StaticInt{S}, stop::Integer) where {F,S} + OptionallyStaticStepRange(StaticInt(F), StaticInt(S), stop) +end +function Base.:(:)(::StaticInt{F}, step::Integer, ::StaticInt{L}) where {F,L} + OptionallyStaticStepRange(StaticInt(F), step, StaticInt(L)) +end +function Base.:(:)(start::Integer, step::Integer, ::StaticInt{L}) where {L} + OptionallyStaticStepRange(start, step, StaticInt(L)) +end +function Base.:(:)(start::Integer, ::StaticInt{S}, stop::Integer) where {S} + OptionallyStaticStepRange(start, StaticInt(S), stop) +end +function Base.:(:)(::StaticInt{F}, step::Integer, stop::Integer) where {F} + OptionallyStaticStepRange(StaticInt(F), step, stop) +end +Base.:(:)(start::StaticInt{F}, ::StaticInt{1}, stop::StaticInt{L}) where {F,L} = start:stop +Base.:(:)(start::Integer, ::StaticInt{1}, stop::StaticInt{L}) where {L} = start:stop +Base.:(:)(start::StaticInt{F}, ::StaticInt{1}, stop::Integer) where {F} = start:stop +function Base.:(:)(start::Integer, ::StaticInt{1}, stop::Integer) + OptionallyStaticUnitRange(start, stop) +end + +Base.isempty(r::OptionallyStaticUnitRange{One}) = last(r) <= 0 +Base.isempty(r::OptionallyStaticUnitRange) = first(r) > last(r) +function Base.isempty(r::OptionallyStaticStepRange) + (r.start != r.stop) & ((r.step > 0) != (r.stop > r.start)) +end + +function Base.checkindex( + ::Type{Bool}, + ::SUnitRange{F1,L1}, + ::SUnitRange{F2,L2} +) where {F1,L1,F2,L2} + + (F1::Int <= F2::Int) && (L1::Int >= L2::Int) +end + +function Base.getindex( + r::OptionallyStaticUnitRange, + s::AbstractUnitRange{<:Integer}, +) + @boundscheck checkbounds(r, s) + f = static_first(r) + fnew = f - one(f) + return (fnew+static_first(s)):(fnew+static_last(s)) +end + +function Base.getindex(x::OptionallyStaticUnitRange{StaticInt{1}}, i::Int) + @boundscheck checkbounds(x, i) + i +end +function Base.getindex(x::OptionallyStaticUnitRange, i::Int) + val = first(x) + (i - 1) + @boundscheck ((i < 1) || val > last(x)) && throw(BoundsError(x, i)) + val::Int +end + +## length +@inline function Base.length(r::OptionallyStaticUnitRange) + isempty(r) ? 0 : last(r) - first(r) + 1 +end +Base.length(r::OptionallyStaticStepRange) = _range_length(first(r), step(r), last(r)) +_range_length(start, s, stop) = nothing +@inline function _range_length(start::Int, s::Int, stop::Int) + if s > 0 + if stop < start # isempty + return 0 + else + return div(stop - start, s) + 1 + end + else + if stop > start # isempty + return 0 + else + return div(start - stop, -s) + 1 + end + end +end + +Base.AbstractUnitRange{Int}(r::OptionallyStaticUnitRange) = r +function Base.AbstractUnitRange{T}(r::OptionallyStaticUnitRange) where {T} + start = static_first(r) + if isa(start, StaticInt{1}) && T <: Integer + return Base.OneTo{T}(T(static_last(r))) + else + return UnitRange{T}(T(static_first(r)), T(static_last(r))) + end +end + +@inline function Base.iterate(r::OptionallyStaticRange) + isempty(r) && return nothing + fi = Int(first(r)); + fi, fi +end +Base.iterate(::SUnitRange{F,L}) where {F,L} = L < F ? nothing : (F, F) +function Base.iterate(::SOneTo{n}, s::Int) where {n} + if s < n::Int + s2 = s + 1 + return (s2, s2) + else + return nothing + end +end + +Base.to_shape(x::OptionallyStaticRange) = length(x) +Base.to_shape(x::Base.Slice{T}) where {T<:OptionallyStaticRange} = Base.length(x) +Base.axes(S::Base.Slice{<:OptionallyStaticUnitRange{StaticInt{1}}}) = (S.indices,) +Base.axes(S::Base.Slice{<:OptionallyStaticRange}) = (Base.IdentityUnitRange(S.indices),) + +Base.axes(x::OptionallyStaticRange) = (Base.axes1(x),) +function Base.axes1(x::OptionallyStaticUnitRange) + OptionallyStaticUnitRange(StaticInt(1), length(x)) +end +Base.axes1(x::OptionallyStaticUnitRange{StaticInt{1}}) = x +function Base.axes1(x::OptionallyStaticUnitRange{StaticInt{F},StaticInt{L}}) where {F,L} + OptionallyStaticUnitRange(StaticInt(1), StaticInt(L - F + 1)) +end +function Base.axes1(x::OptionallyStaticStepRange) + OptionallyStaticUnitRange(StaticInt(1), length(x)) +end +function Base.axes1(x::OptionallyStaticStepRange{StaticInt{F},StaticInt{S},StaticInt{L}}) where {F,S,L} + OptionallyStaticUnitRange(StaticInt(1), StaticInt(_range_length(F, S, L))) +end +Base.axes1(x::Base.Slice{<:OptionallyStaticUnitRange{One}}) = x.indices +Base.axes1(x::Base.Slice{<:OptionallyStaticRange}) = Base.IdentityUnitRange(x.indices) + +Base.:(-)(r::OptionallyStaticRange) = -static_first(r):-static_step(r):-static_last(r) + +function Base.reverse(x::OptionallyStaticUnitRange) + _OptionallyStaticStepRange(getfield(x, :stop), StaticInt(-1), getfield(x, :start)) +end +function Base.reverse(x::OptionallyStaticStepRange) + _OptionallyStaticStepRange(getfield(x, :stop), -getfield(x, :step), getfield(x, :start)) +end + +function Base.show(io::IO, ::MIME"text/plain", @nospecialize(r::OptionallyStaticUnitRange)) + print(io, "$(getfield(r, :start)):$(getfield(r, :stop))") +end +function Base.show(io::IO, ::MIME"text/plain", @nospecialize(r::OptionallyStaticStepRange)) + print(io, "$(getfield(r, :start)):$(getfield(r, :step)):$(getfield(r, :stop))") +end + +@inline function Base.getproperty(x::OptionallyStaticRange, s::Symbol) + if s === :start + return first(x) + elseif s === :step + return step(x) + elseif s === :stop + return last(x) + else + error("$x has no property $s") + end +end + +function Base.Broadcast.axistype(r::OptionallyStaticUnitRange{StaticInt{1}}, _) + Base.OneTo(last(r)) +end +function Base.Broadcast.axistype(_, r::OptionallyStaticUnitRange{StaticInt{1}}) + Base.OneTo(last(r)) +end +function Base.Broadcast.axistype(r::OptionallyStaticUnitRange{StaticInt{1}}, ::OptionallyStaticUnitRange{StaticInt{1}}) + Base.OneTo(last(r)) +end +function Base.similar(::Type{<:Array{T}}, axes::Tuple{OptionallyStaticUnitRange{StaticInt{1}},Vararg{Union{Base.OneTo,OptionallyStaticUnitRange{StaticInt{1}}}}}) where {T} + Array{T}(undef, map(last, axes)) +end +function Base.similar(::Type{<:Array{T}}, axes::Tuple{Base.OneTo,OptionallyStaticUnitRange{StaticInt{1}},Vararg{Union{Base.OneTo,OptionallyStaticUnitRange{StaticInt{1}}}}}) where {T} + Array{T}(undef, map(last, axes)) +end + diff --git a/test/ranges.jl b/test/ranges.jl new file mode 100644 index 0000000..38c42f8 --- /dev/null +++ b/test/ranges.jl @@ -0,0 +1,98 @@ + + +@testset "Range Constructors" begin + @test @inferred(static(1):static(10)) == 1:10 + @test @inferred(Static.SUnitRange{1,10}()) == 1:10 + @test @inferred(static(1):static(2):static(10)) == 1:2:10 + @test @inferred(1:static(2):static(10)) == 1:2:10 + @test @inferred(static(1):static(2):10) == 1:2:10 + @test @inferred(static(1):2:static(10)) == 1:2:10 + @test @inferred(1:2:static(10)) == 1:2:10 + @test @inferred(1:static(2):10) == 1:2:10 + @test @inferred(static(1):2:10) == 1:2:10 + @test @inferred(static(1):UInt(10)) === static(1):10 + @test @inferred(UInt(1):static(1):static(10)) === 1:static(10) + @test Static.SUnitRange(1, 10) == 1:10 + @test @inferred(Static.OptionallyStaticUnitRange{Int,Int}(1:10)) == 1:10 + @test @inferred(Static.OptionallyStaticUnitRange(1:10)) == 1:10 + + @inferred(Static.OptionallyStaticUnitRange(1:10)) + + @test @inferred(Static.OptionallyStaticStepRange(static(1), static(1), static(1))) == 1:1:1 + @test @inferred(Static.OptionallyStaticStepRange(static(1), 1, UInt(10))) == static(1):1:10 + @test @inferred(Static.OptionallyStaticStepRange(UInt(1), 1, static(10))) == static(1):1:10 + @test @inferred(Static.OptionallyStaticStepRange(1:10)) == 1:1:10 + + @test_throws ArgumentError Static.OptionallyStaticUnitRange(1:2:10) + @test_throws ArgumentError Static.OptionallyStaticUnitRange{Int,Int}(1:2:10) + @test_throws ArgumentError Static.OptionallyStaticStepRange(1, 0, 10) + + @test @inferred(static(1):static(1):static(10)) === Static.OptionallyStaticUnitRange(static(1), static(10)) + @test @inferred(static(1):static(1):10) === Static.OptionallyStaticUnitRange(static(1), 10) + @test @inferred(1:static(1):10) === Static.OptionallyStaticUnitRange(1, 10) + @test length(static(-1):static(-1):static(-10)) == 10 == lastindex(static(-1):static(-1):static(-10)) + + @test UnitRange(Static.OptionallyStaticUnitRange(static(1), static(10))) === UnitRange(1, 10) + @test UnitRange{Int}(Static.OptionallyStaticUnitRange(static(1), static(10))) === UnitRange(1, 10) + + @test AbstractUnitRange{Int}(Static.OptionallyStaticUnitRange(static(1), static(10))) isa Static.OptionallyStaticUnitRange + @test AbstractUnitRange{UInt}(Static.OptionallyStaticUnitRange(static(1), static(10))) isa Base.OneTo + @test AbstractUnitRange{UInt}(Static.OptionallyStaticUnitRange(static(2), static(10))) isa UnitRange + + @test @inferred((static(1):static(10))[static(2):static(3)]) === static(2):static(3) + @test @inferred((static(1):static(10))[static(2):3]) === static(2):3 + @test @inferred((static(1):static(10))[2:3]) === 2:3 + @test @inferred((1:static(10))[static(2):static(3)]) === 2:3 + + @test Base.checkindex(Bool, static(1):static(10), static(1):static(5)) + @test -(static(1):static(10)) === static(-1):static(-1):static(-10) + + @test reverse(static(1):static(10)) === static(10):static(-1):static(1) + @test reverse(static(1):static(2):static(9)) === static(9):static(-2):static(1) +end + +# iteration +@test iterate(static(1):static(5), 5) === nothing +@test iterate(static(2):static(5), 5) === nothing + +# CartesianIndices +CI = CartesianIndices((static(1):static(2), static(1):static(2))) + +@testset "length" begin + @test @inferred(length(Static.OptionallyStaticUnitRange(1, 0))) == 0 + @test @inferred(length(Static.OptionallyStaticUnitRange(1, 10))) == 10 + @test @inferred(length(Static.OptionallyStaticUnitRange(static(1), 10))) == 10 + @test @inferred(length(Static.OptionallyStaticUnitRange(static(0), 10))) == 11 + @test @inferred(length(Static.OptionallyStaticUnitRange(static(1), static(10)))) == 10 + @test @inferred(length(Static.OptionallyStaticUnitRange(static(0), static(10)))) == 11 + + @test @inferred(length(static(1):static(2):static(0))) == 0 + @test @inferred(length(static(0):static(-2):static(1))) == 0 + + @test @inferred(length(Static.OptionallyStaticStepRange(static(1), 2, 10))) == 5 + @test @inferred(length(Static.OptionallyStaticStepRange(static(1), static(1), static(10)))) == 10 + @test @inferred(length(Static.OptionallyStaticStepRange(static(2), static(1), static(10)))) == 9 + @test @inferred(length(Static.OptionallyStaticStepRange(static(2), static(2), static(10)))) == 5 +end + +@test @inferred(getindex(Static.OptionallyStaticUnitRange(static(1), 10), 1)) == 1 +@test @inferred(getindex(Static.OptionallyStaticUnitRange(static(0), 10), 1)) == 0 +@test_throws BoundsError getindex(Static.OptionallyStaticUnitRange(static(1), 10), 0) +@test_throws BoundsError getindex(Static.OptionallyStaticStepRange(static(1), 2, 10), 0) +@test_throws BoundsError getindex(Static.OptionallyStaticUnitRange(static(1), 10), 11) +@test_throws BoundsError getindex(Static.OptionallyStaticStepRange(static(1), 2, 10), 11) + +@test Static.static_first(Base.OneTo(one(UInt))) === static(1) +@test Static.static_step(Base.OneTo(one(UInt))) === static(1) + +@test @inferred(eachindex(static(-7):static(7))) === static(1):static(15) +@test @inferred((static(-7):static(7))[first(eachindex(static(-7):static(7)))]) == -7 + +@test @inferred(firstindex(128:static(-1):1)) == 1 + +@test identity.(static(1):5) isa Vector{Int} +@test (static(1):5) .+ (1:3)' isa Matrix{Int} +@test similar(Array{Int}, (static(1):(4),)) isa Vector{Int} +@test similar(Array{Int}, (static(1):(4), Base.OneTo(4))) isa Matrix{Int} +@test similar(Array{Int}, (Base.OneTo(4), static(1):(4))) isa Matrix{Int} + diff --git a/test/runtests.jl b/test/runtests.jl index 502ee35..d519196 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -499,3 +499,6 @@ end @test repr(static(CartesianIndex(1, 1))) == "NDIndex(static(1), static(1))" @test string(static(true)) == "static(true)" == "$(static(true))" end + +include("ranges.jl") + From 5e59011980d2a8912710df4b86551def5ffda803 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Sat, 5 Nov 2022 19:33:34 -0400 Subject: [PATCH 02/10] format --- src/Static.jl | 6 ++ src/ranges.jl | 151 ++++++++++++++++++++++++----------------------- test/ranges.jl | 50 ++++++++++------ test/runtests.jl | 1 - 4 files changed, 115 insertions(+), 93 deletions(-) diff --git a/src/Static.jl b/src/Static.jl index 9ce76e1..391f748 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -1,5 +1,10 @@ module Static +struct Foo end + +Base.:(:)(::Integer, ::Foo) = nothing + +#= import IfElse: ifelse export StaticInt, StaticFloat64, StaticSymbol, True, False, StaticBool, NDIndex @@ -942,5 +947,6 @@ function Base.show(io::IO, m::MIME"text/plain", @nospecialize(x::NDIndex)) end include("ranges.jl") +=# end diff --git a/src/ranges.jl b/src/ranges.jl index 1d2256b..3f0f077 100644 --- a/src/ranges.jl +++ b/src/ranges.jl @@ -1,6 +1,6 @@ _int(x::Integer) = Int(x) -_int(@nospecialize x::Union{StaticInt,Int}) = x +_int(@nospecialize x::Union{StaticInt, Int}) = x """ OptionallyStaticUnitRange(start, stop) <: AbstractUnitRange{Int} @@ -10,12 +10,14 @@ Similar to `UnitRange` except each field may be an `Int` or `StaticInt`. An indices. Therefore, users should not expect the same checks are used to ensure construction of a valid `OptionallyStaticUnitRange` as a `UnitRange`. """ -struct OptionallyStaticUnitRange{F<:Union{Int,StaticInt},L<:Union{Int,StaticInt}} <: AbstractUnitRange{Int} +struct OptionallyStaticUnitRange{F <: Union{Int, StaticInt}, L <: Union{Int, StaticInt}} <: + AbstractUnitRange{Int} start::F stop::L - function OptionallyStaticUnitRange(start::Union{Int,StaticInt}, stop::Union{Int,StaticInt}) - new{typeof(start),typeof(stop)}(start, stop) + function OptionallyStaticUnitRange(start::Union{Int, StaticInt}, + stop::Union{Int, StaticInt}) + new{typeof(start), typeof(stop)}(start, stop) end function OptionallyStaticUnitRange(start, stop) OptionallyStaticUnitRange(_int(start), _int(stop)) @@ -27,9 +29,11 @@ struct OptionallyStaticUnitRange{F<:Union{Int,StaticInt},L<:Union{Int,StaticInt} errmsg(x) = throw(ArgumentError("step must be 1, got $(step(x))")) # avoid GC frame errmsg(x) end - OptionallyStaticUnitRange{F,L}(x::AbstractRange) where {F,L} = OptionallyStaticUnitRange(x) - function OptionallyStaticUnitRange{StaticInt{F},StaticInt{L}}() where {F,L} - new{StaticInt{F},StaticInt{L}}() + function OptionallyStaticUnitRange{F, L}(x::AbstractRange) where {F, L} + OptionallyStaticUnitRange(x) + end + function OptionallyStaticUnitRange{StaticInt{F}, StaticInt{L}}() where {F, L} + new{StaticInt{F}, StaticInt{L}}() end end @@ -55,49 +59,40 @@ static(2):static(2):10 ``` """ -struct OptionallyStaticStepRange{F<:Union{Int,StaticInt},S<:Union{Int,StaticInt},L<:Union{Int,StaticInt}} <: OrdinalRange{Int,Int} +struct OptionallyStaticStepRange{F <: Union{Int, StaticInt}, S <: Union{Int, StaticInt}, + L <: Union{Int, StaticInt}} <: OrdinalRange{Int, Int} start::F step::S stop::L - global function _OptionallyStaticStepRange( - @nospecialize(start::Union{Int,StaticInt}), - @nospecialize(step::Union{Int,StaticInt}), - @nospecialize(stop::Union{Int,StaticInt}) - ) - new{typeof(start),typeof(step),typeof(stop)}(start, step, stop) + global function _OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), + @nospecialize(step::Union{Int, StaticInt}), + @nospecialize(stop::Union{Int, StaticInt})) + new{typeof(start), typeof(step), typeof(stop)}(start, step, stop) end end -@noinline function OptionallyStaticStepRange( - @nospecialize(start::Union{Int,StaticInt}), - step::StaticInt{0}, - @nospecialize(stop::Union{Int,StaticInt}) -) +@noinline function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), + ::StaticInt{0}, + @nospecialize(stop::Union{Int, StaticInt})) throw(ArgumentError("step cannot be zero")) end # we don't need to check the `stop` if we know it acts like a unit range -function OptionallyStaticStepRange( - @nospecialize(start::Union{Int,StaticInt}), - step::StaticInt{1}, - @nospecialize(stop::Union{Int,StaticInt}) -) +function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), + step::StaticInt{1}, + @nospecialize(stop::Union{Int, StaticInt})) _OptionallyStaticStepRange(start, step, stop) end -function OptionallyStaticStepRange( - @nospecialize(start::Union{Int,StaticInt}), - @nospecialize(step::StaticInt), - @nospecialize(stop::Union{Int,StaticInt}) -) +function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), + @nospecialize(step::StaticInt), + @nospecialize(stop::Union{Int, StaticInt})) _OptionallyStaticStepRange(start, step, _steprange_last(start, step, stop)) end function OptionallyStaticStepRange(start, step, stop) OptionallyStaticStepRange(_int(start), _int(step), _int(stop)) end -function OptionallyStaticStepRange( - @nospecialize(start::Union{Int,StaticInt}), - step::Int, - @nospecialize(stop::Union{Int,StaticInt}) -) +function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), + step::Int, + @nospecialize(stop::Union{Int, StaticInt})) if step === 0 throw(ArgumentError("step cannot be zero")) else @@ -113,11 +108,9 @@ end @inline function _steprange_last(start::StaticInt, step::StaticInt, stop::StaticInt) StaticInt(_steprange_last(Int(start), Int(step), Int(stop))) end -@inline function _steprange_last( - start::Union{StaticInt,Int}, - step::Union{StaticInt,Int}, - stop::Union{StaticInt,Int} -) +@inline function _steprange_last(start::Union{StaticInt, Int}, + step::Union{StaticInt, Int}, + stop::Union{StaticInt, Int}) _steprange_last(Int(start), Int(step), Int(stop)) end @inline function _steprange_last(start::Int, step::Int, stop::Int) @@ -143,22 +136,23 @@ end An alias for `OptionallyStaticUnitRange` where both the start and stop are known statically. """ -const SUnitRange{F,L} = OptionallyStaticUnitRange{StaticInt{F},StaticInt{L}} -SUnitRange(start::Int, stop::Int) = SUnitRange{start,stop}() +const SUnitRange{F, L} = OptionallyStaticUnitRange{StaticInt{F}, StaticInt{L}} +SUnitRange(start::Int, stop::Int) = SUnitRange{start, stop}() """ SOneTo(n::Int) An alias for `OptionallyStaticUnitRange` usfeul for statically sized axes. """ -const SOneTo{L} = SUnitRange{1,L} +const SOneTo{L} = SUnitRange{1, L} SOneTo(n::Int) = SOneTo{n}() -const OptionallyStaticRange = Union{<:OptionallyStaticUnitRange,<:OptionallyStaticStepRange} +const OptionallyStaticRange = Union{<:OptionallyStaticUnitRange, <:OptionallyStaticStepRange + } # these probide a generic method for extracting potentially static values. static_first(x::Base.OneTo) = StaticInt(1) -static_first(x::Union{Base.Slice,Base.IdentityUnitRange}) = static_first(x.indices) +static_first(x::Union{Base.Slice, Base.IdentityUnitRange}) = static_first(x.indices) static_first(x::OptionallyStaticRange) = getfield(x, :start) static_first(x) = first(x) @@ -168,7 +162,7 @@ static_step(x) = step(x) static_last(x::OptionallyStaticRange) = getfield(x, :stop) static_last(x) = last(x) -static_last(x::Union{Base.Slice,Base.IdentityUnitRange}) = static_last(x.indices) +static_last(x::Union{Base.Slice, Base.IdentityUnitRange}) = static_last(x.indices) Base.first(x::OptionallyStaticRange) = Int(static_first(x)) Base.step(x::OptionallyStaticStepRange) = Int(static_step(x)) @@ -177,19 +171,19 @@ Base.last(x::OptionallyStaticRange) = Int(static_last(x)) # FIXME this line causes invalidations Base.:(:)(L::Integer, ::StaticInt{U}) where {U} = OptionallyStaticUnitRange(L, StaticInt(U)) Base.:(:)(::StaticInt{L}, U::Integer) where {L} = OptionallyStaticUnitRange(StaticInt(L), U) -function Base.:(:)(::StaticInt{L}, ::StaticInt{U}) where {L,U} +function Base.:(:)(::StaticInt{L}, ::StaticInt{U}) where {L, U} OptionallyStaticUnitRange(StaticInt(L), StaticInt(U)) end -function Base.:(:)(::StaticInt{F}, ::StaticInt{S}, ::StaticInt{L}) where {F,S,L} +function Base.:(:)(::StaticInt{F}, ::StaticInt{S}, ::StaticInt{L}) where {F, S, L} OptionallyStaticStepRange(StaticInt(F), StaticInt(S), StaticInt(L)) end -function Base.:(:)(start::Integer, ::StaticInt{S}, ::StaticInt{L}) where {S,L} +function Base.:(:)(start::Integer, ::StaticInt{S}, ::StaticInt{L}) where {S, L} OptionallyStaticStepRange(start, StaticInt(S), StaticInt(L)) end -function Base.:(:)(::StaticInt{F}, ::StaticInt{S}, stop::Integer) where {F,S} +function Base.:(:)(::StaticInt{F}, ::StaticInt{S}, stop::Integer) where {F, S} OptionallyStaticStepRange(StaticInt(F), StaticInt(S), stop) end -function Base.:(:)(::StaticInt{F}, step::Integer, ::StaticInt{L}) where {F,L} +function Base.:(:)(::StaticInt{F}, step::Integer, ::StaticInt{L}) where {F, L} OptionallyStaticStepRange(StaticInt(F), step, StaticInt(L)) end function Base.:(:)(start::Integer, step::Integer, ::StaticInt{L}) where {L} @@ -201,7 +195,7 @@ end function Base.:(:)(::StaticInt{F}, step::Integer, stop::Integer) where {F} OptionallyStaticStepRange(StaticInt(F), step, stop) end -Base.:(:)(start::StaticInt{F}, ::StaticInt{1}, stop::StaticInt{L}) where {F,L} = start:stop +Base.:(:)(start::StaticInt{F}, ::StaticInt{1}, stop::StaticInt{L}) where {F, L} = start:stop Base.:(:)(start::Integer, ::StaticInt{1}, stop::StaticInt{L}) where {L} = start:stop Base.:(:)(start::StaticInt{F}, ::StaticInt{1}, stop::Integer) where {F} = start:stop function Base.:(:)(start::Integer, ::StaticInt{1}, stop::Integer) @@ -214,23 +208,18 @@ function Base.isempty(r::OptionallyStaticStepRange) (r.start != r.stop) & ((r.step > 0) != (r.stop > r.start)) end -function Base.checkindex( - ::Type{Bool}, - ::SUnitRange{F1,L1}, - ::SUnitRange{F2,L2} -) where {F1,L1,F2,L2} - +function Base.checkindex(::Type{Bool}, + ::SUnitRange{F1, L1}, + ::SUnitRange{F2, L2}) where {F1, L1, F2, L2} (F1::Int <= F2::Int) && (L1::Int >= L2::Int) end -function Base.getindex( - r::OptionallyStaticUnitRange, - s::AbstractUnitRange{<:Integer}, -) +function Base.getindex(r::OptionallyStaticUnitRange, + s::AbstractUnitRange{<:Integer}) @boundscheck checkbounds(r, s) f = static_first(r) fnew = f - one(f) - return (fnew+static_first(s)):(fnew+static_last(s)) + return (fnew + static_first(s)):(fnew + static_last(s)) end function Base.getindex(x::OptionallyStaticUnitRange{StaticInt{1}}, i::Int) @@ -250,7 +239,7 @@ end Base.length(r::OptionallyStaticStepRange) = _range_length(first(r), step(r), last(r)) _range_length(start, s, stop) = nothing @inline function _range_length(start::Int, s::Int, stop::Int) - if s > 0 + if s > 0 if stop < start # isempty return 0 else @@ -277,10 +266,10 @@ end @inline function Base.iterate(r::OptionallyStaticRange) isempty(r) && return nothing - fi = Int(first(r)); + fi = Int(first(r)) fi, fi end -Base.iterate(::SUnitRange{F,L}) where {F,L} = L < F ? nothing : (F, F) +Base.iterate(::SUnitRange{F, L}) where {F, L} = L < F ? nothing : (F, F) function Base.iterate(::SOneTo{n}, s::Int) where {n} if s < n::Int s2 = s + 1 @@ -291,7 +280,7 @@ function Base.iterate(::SOneTo{n}, s::Int) where {n} end Base.to_shape(x::OptionallyStaticRange) = length(x) -Base.to_shape(x::Base.Slice{T}) where {T<:OptionallyStaticRange} = Base.length(x) +Base.to_shape(x::Base.Slice{T}) where {T <: OptionallyStaticRange} = Base.length(x) Base.axes(S::Base.Slice{<:OptionallyStaticUnitRange{StaticInt{1}}}) = (S.indices,) Base.axes(S::Base.Slice{<:OptionallyStaticRange}) = (Base.IdentityUnitRange(S.indices),) @@ -300,19 +289,23 @@ function Base.axes1(x::OptionallyStaticUnitRange) OptionallyStaticUnitRange(StaticInt(1), length(x)) end Base.axes1(x::OptionallyStaticUnitRange{StaticInt{1}}) = x -function Base.axes1(x::OptionallyStaticUnitRange{StaticInt{F},StaticInt{L}}) where {F,L} +function Base.axes1(x::OptionallyStaticUnitRange{StaticInt{F}, StaticInt{L}}) where {F, L} OptionallyStaticUnitRange(StaticInt(1), StaticInt(L - F + 1)) end function Base.axes1(x::OptionallyStaticStepRange) OptionallyStaticUnitRange(StaticInt(1), length(x)) end -function Base.axes1(x::OptionallyStaticStepRange{StaticInt{F},StaticInt{S},StaticInt{L}}) where {F,S,L} +function Base.axes1(x::OptionallyStaticStepRange{StaticInt{F}, StaticInt{S}, StaticInt{L}}) where { + F, + S, + L + } OptionallyStaticUnitRange(StaticInt(1), StaticInt(_range_length(F, S, L))) end Base.axes1(x::Base.Slice{<:OptionallyStaticUnitRange{One}}) = x.indices Base.axes1(x::Base.Slice{<:OptionallyStaticRange}) = Base.IdentityUnitRange(x.indices) -Base.:(-)(r::OptionallyStaticRange) = -static_first(r):-static_step(r):-static_last(r) +Base.:(-)(r::OptionallyStaticRange) = (-static_first(r)):(-static_step(r)):(-static_last(r)) function Base.reverse(x::OptionallyStaticUnitRange) _OptionallyStaticStepRange(getfield(x, :stop), StaticInt(-1), getfield(x, :start)) @@ -346,13 +339,25 @@ end function Base.Broadcast.axistype(_, r::OptionallyStaticUnitRange{StaticInt{1}}) Base.OneTo(last(r)) end -function Base.Broadcast.axistype(r::OptionallyStaticUnitRange{StaticInt{1}}, ::OptionallyStaticUnitRange{StaticInt{1}}) +function Base.Broadcast.axistype(r::OptionallyStaticUnitRange{StaticInt{1}}, + ::OptionallyStaticUnitRange{StaticInt{1}}) Base.OneTo(last(r)) end -function Base.similar(::Type{<:Array{T}}, axes::Tuple{OptionallyStaticUnitRange{StaticInt{1}},Vararg{Union{Base.OneTo,OptionallyStaticUnitRange{StaticInt{1}}}}}) where {T} +function Base.similar(::Type{<:Array{T}}, + axes::Tuple{OptionallyStaticUnitRange{StaticInt{1}}, + Vararg{ + Union{Base.OneTo, + OptionallyStaticUnitRange{StaticInt{1}}}}}) where { + T + } Array{T}(undef, map(last, axes)) end -function Base.similar(::Type{<:Array{T}}, axes::Tuple{Base.OneTo,OptionallyStaticUnitRange{StaticInt{1}},Vararg{Union{Base.OneTo,OptionallyStaticUnitRange{StaticInt{1}}}}}) where {T} +function Base.similar(::Type{<:Array{T}}, + axes::Tuple{Base.OneTo, OptionallyStaticUnitRange{StaticInt{1}}, + Vararg{ + Union{Base.OneTo, + OptionallyStaticUnitRange{StaticInt{1}}}}}) where { + T + } Array{T}(undef, map(last, axes)) end - diff --git a/test/ranges.jl b/test/ranges.jl index 38c42f8..ca3da0b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1,8 +1,7 @@ - @testset "Range Constructors" begin @test @inferred(static(1):static(10)) == 1:10 - @test @inferred(Static.SUnitRange{1,10}()) == 1:10 + @test @inferred(Static.SUnitRange{1, 10}()) == 1:10 @test @inferred(static(1):static(2):static(10)) == 1:2:10 @test @inferred(1:static(2):static(10)) == 1:2:10 @test @inferred(static(1):static(2):10) == 1:2:10 @@ -13,31 +12,42 @@ @test @inferred(static(1):UInt(10)) === static(1):10 @test @inferred(UInt(1):static(1):static(10)) === 1:static(10) @test Static.SUnitRange(1, 10) == 1:10 - @test @inferred(Static.OptionallyStaticUnitRange{Int,Int}(1:10)) == 1:10 + @test @inferred(Static.OptionallyStaticUnitRange{Int, Int}(1:10)) == 1:10 @test @inferred(Static.OptionallyStaticUnitRange(1:10)) == 1:10 @inferred(Static.OptionallyStaticUnitRange(1:10)) - @test @inferred(Static.OptionallyStaticStepRange(static(1), static(1), static(1))) == 1:1:1 - @test @inferred(Static.OptionallyStaticStepRange(static(1), 1, UInt(10))) == static(1):1:10 - @test @inferred(Static.OptionallyStaticStepRange(UInt(1), 1, static(10))) == static(1):1:10 + @test @inferred(Static.OptionallyStaticStepRange(static(1), static(1), static(1))) == + 1:1:1 + @test @inferred(Static.OptionallyStaticStepRange(static(1), 1, UInt(10))) == + static(1):1:10 + @test @inferred(Static.OptionallyStaticStepRange(UInt(1), 1, static(10))) == + static(1):1:10 @test @inferred(Static.OptionallyStaticStepRange(1:10)) == 1:1:10 @test_throws ArgumentError Static.OptionallyStaticUnitRange(1:2:10) - @test_throws ArgumentError Static.OptionallyStaticUnitRange{Int,Int}(1:2:10) + @test_throws ArgumentError Static.OptionallyStaticUnitRange{Int, Int}(1:2:10) @test_throws ArgumentError Static.OptionallyStaticStepRange(1, 0, 10) - @test @inferred(static(1):static(1):static(10)) === Static.OptionallyStaticUnitRange(static(1), static(10)) - @test @inferred(static(1):static(1):10) === Static.OptionallyStaticUnitRange(static(1), 10) + @test @inferred(static(1):static(1):static(10)) === + Static.OptionallyStaticUnitRange(static(1), static(10)) + @test @inferred(static(1):static(1):10) === + Static.OptionallyStaticUnitRange(static(1), 10) @test @inferred(1:static(1):10) === Static.OptionallyStaticUnitRange(1, 10) - @test length(static(-1):static(-1):static(-10)) == 10 == lastindex(static(-1):static(-1):static(-10)) + @test length(static(-1):static(-1):static(-10)) == 10 == + lastindex(static(-1):static(-1):static(-10)) - @test UnitRange(Static.OptionallyStaticUnitRange(static(1), static(10))) === UnitRange(1, 10) - @test UnitRange{Int}(Static.OptionallyStaticUnitRange(static(1), static(10))) === UnitRange(1, 10) + @test UnitRange(Static.OptionallyStaticUnitRange(static(1), static(10))) === + UnitRange(1, 10) + @test UnitRange{Int}(Static.OptionallyStaticUnitRange(static(1), static(10))) === + UnitRange(1, 10) - @test AbstractUnitRange{Int}(Static.OptionallyStaticUnitRange(static(1), static(10))) isa Static.OptionallyStaticUnitRange - @test AbstractUnitRange{UInt}(Static.OptionallyStaticUnitRange(static(1), static(10))) isa Base.OneTo - @test AbstractUnitRange{UInt}(Static.OptionallyStaticUnitRange(static(2), static(10))) isa UnitRange + @test AbstractUnitRange{Int}(Static.OptionallyStaticUnitRange(static(1), static(10))) isa + Static.OptionallyStaticUnitRange + @test AbstractUnitRange{UInt}(Static.OptionallyStaticUnitRange(static(1), static(10))) isa + Base.OneTo + @test AbstractUnitRange{UInt}(Static.OptionallyStaticUnitRange(static(2), static(10))) isa + UnitRange @test @inferred((static(1):static(10))[static(2):static(3)]) === static(2):static(3) @test @inferred((static(1):static(10))[static(2):3]) === static(2):3 @@ -70,9 +80,12 @@ CI = CartesianIndices((static(1):static(2), static(1):static(2))) @test @inferred(length(static(0):static(-2):static(1))) == 0 @test @inferred(length(Static.OptionallyStaticStepRange(static(1), 2, 10))) == 5 - @test @inferred(length(Static.OptionallyStaticStepRange(static(1), static(1), static(10)))) == 10 - @test @inferred(length(Static.OptionallyStaticStepRange(static(2), static(1), static(10)))) == 9 - @test @inferred(length(Static.OptionallyStaticStepRange(static(2), static(2), static(10)))) == 5 + @test @inferred(length(Static.OptionallyStaticStepRange(static(1), static(1), + static(10)))) == 10 + @test @inferred(length(Static.OptionallyStaticStepRange(static(2), static(1), + static(10)))) == 9 + @test @inferred(length(Static.OptionallyStaticStepRange(static(2), static(2), + static(10)))) == 5 end @test @inferred(getindex(Static.OptionallyStaticUnitRange(static(1), 10), 1)) == 1 @@ -95,4 +108,3 @@ end @test similar(Array{Int}, (static(1):(4),)) isa Vector{Int} @test similar(Array{Int}, (static(1):(4), Base.OneTo(4))) isa Matrix{Int} @test similar(Array{Int}, (Base.OneTo(4), static(1):(4))) isa Matrix{Int} - diff --git a/test/runtests.jl b/test/runtests.jl index d519196..c4149cc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -501,4 +501,3 @@ end end include("ranges.jl") - From 3037df60f21fd9ba52c18503c55dbabd00780fc1 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Sat, 5 Nov 2022 22:31:06 -0400 Subject: [PATCH 03/10] Use breaking version so that ArrayInterface ranges are compatible --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e274282..acdfd74 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Static" uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" authors = ["chriselrod", "ChrisRackauckas", "Tokazama"] -version = "0.7.7" +version = "0.8" [deps] IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" From dd87fe9ff4ed1f148637ca0bacc8549598d38248 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Sat, 5 Nov 2022 22:52:51 -0400 Subject: [PATCH 04/10] Fix commenting out --- src/Static.jl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Static.jl b/src/Static.jl index ca36f81..99ed3fb 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -1,10 +1,5 @@ module Static -struct Foo end - -Base.:(:)(::Integer, ::Foo) = nothing - -#= import IfElse: ifelse export StaticInt, StaticFloat64, StaticSymbol, True, False, StaticBool, NDIndex @@ -885,6 +880,5 @@ function Base.show(io::IO, m::MIME"text/plain", @nospecialize(x::NDIndex)) end include("ranges.jl") -=# end From d48e90d616a4521d08ad111090787b2ec7f67ed2 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Sat, 5 Nov 2022 23:30:58 -0400 Subject: [PATCH 05/10] improve test coverage --- src/ranges.jl | 5 ++--- test/ranges.jl | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ranges.jl b/src/ranges.jl index 3f0f077..40a50ee 100644 --- a/src/ranges.jl +++ b/src/ranges.jl @@ -237,7 +237,6 @@ end isempty(r) ? 0 : last(r) - first(r) + 1 end Base.length(r::OptionallyStaticStepRange) = _range_length(first(r), step(r), last(r)) -_range_length(start, s, stop) = nothing @inline function _range_length(start::Int, s::Int, stop::Int) if s > 0 if stop < start # isempty @@ -270,8 +269,8 @@ end fi, fi end Base.iterate(::SUnitRange{F, L}) where {F, L} = L < F ? nothing : (F, F) -function Base.iterate(::SOneTo{n}, s::Int) where {n} - if s < n::Int +@inline function Base.iterate(::SUnitRange{F,L}, s::Int) where {F,L} + if L <= s s2 = s + 1 return (s2, s2) else diff --git a/test/ranges.jl b/test/ranges.jl index ca3da0b..4e7cbdc 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -13,14 +13,13 @@ @test @inferred(UInt(1):static(1):static(10)) === 1:static(10) @test Static.SUnitRange(1, 10) == 1:10 @test @inferred(Static.OptionallyStaticUnitRange{Int, Int}(1:10)) == 1:10 - @test @inferred(Static.OptionallyStaticUnitRange(1:10)) == 1:10 + @test @inferred(Static.OptionallyStaticUnitRange(1:10)) == 1:10 == + @inferred(Static.OptionallyStaticUnitRange(Static.OptionallyStaticUnitRange(1:10))) - @inferred(Static.OptionallyStaticUnitRange(1:10)) - - @test @inferred(Static.OptionallyStaticStepRange(static(1), static(1), static(1))) == - 1:1:1 + sr = Static.OptionallyStaticStepRange(static(1), static(1), static(1)) + @test @inferred(Static.OptionallyStaticStepRange(sr)) == sr == 1:1:1 @test @inferred(Static.OptionallyStaticStepRange(static(1), 1, UInt(10))) == - static(1):1:10 + static(1):1:10 == Static.SOneTo(10) @test @inferred(Static.OptionallyStaticStepRange(UInt(1), 1, static(10))) == static(1):1:10 @test @inferred(Static.OptionallyStaticStepRange(1:10)) == 1:1:10 @@ -28,6 +27,7 @@ @test_throws ArgumentError Static.OptionallyStaticUnitRange(1:2:10) @test_throws ArgumentError Static.OptionallyStaticUnitRange{Int, Int}(1:2:10) @test_throws ArgumentError Static.OptionallyStaticStepRange(1, 0, 10) + @test_throws ArgumentError Static.OptionallyStaticStepRange(1, StaticInt(0), 10) @test @inferred(static(1):static(1):static(10)) === Static.OptionallyStaticUnitRange(static(1), static(10)) @@ -62,6 +62,8 @@ end # iteration +@test iterate(static(1):static(5)) === (1,1) +@test iterate(static(1):static(5), 1) === (2, 2) @test iterate(static(1):static(5), 5) === nothing @test iterate(static(2):static(5), 5) === nothing @@ -108,3 +110,11 @@ end @test similar(Array{Int}, (static(1):(4),)) isa Vector{Int} @test similar(Array{Int}, (static(1):(4), Base.OneTo(4))) isa Matrix{Int} @test similar(Array{Int}, (Base.OneTo(4), static(1):(4))) isa Matrix{Int} + +@test Base.to_shape(static(1):10) == 10 +@test Base.to_shape(Base.Slice(static(1):10)) == 10 +@test axes(Base.Slice(static(1):10)) === (static(1):10,) +@test isa(axes(Base.Slice(static(0):static(1):10))[1], Base.IdentityUnitRange) + +@test Base.Broadcast.axistype(static(1):10, static(1):10) === Base.OneTo(10) +@test Base.Broadcast.axistype(Base.OneTo(10), static(1):10) === Base.OneTo(10) From 11e248a648403a0b5ef7755d77e0fad6be15f5d3 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Sun, 6 Nov 2022 01:04:41 -0400 Subject: [PATCH 06/10] static_promote for ranges --- src/Static.jl | 32 ++++++++++++++++++++++ src/ranges.jl | 72 +++++++++++++++++++++++++++----------------------- test/ranges.jl | 13 +++++++-- 3 files changed, 82 insertions(+), 35 deletions(-) diff --git a/src/Static.jl b/src/Static.jl index 99ed3fb..2079f3a 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -260,6 +260,38 @@ _static_promote(::Nothing, ::Nothing) = nothing _static_promote(x, ::Nothing) = x _static_promote(::Nothing, y) = y +""" + static_promote(x::AbstractRange{<:Integer}, y::AbstractRange{<:Integer}) + +A type stable method for combining two equal ranges into a new range that preserves static +parameters. Throws an error if `x != y`. + +# Examples + +```julia +julia> static_promote(static(1):10, 1:static(10)) +static(1):static(10) + +julia> static_promote(1:2:9, static(1):static(2):static(9)) +static(1):static(2):static(9) +``` +""" +Base.@propagate_inbounds function static_promote(x::AbstractUnitRange{<:Integer}, + y::AbstractUnitRange{<:Integer}) + @inline + fst = static_promote(static_first(x), static_first(y)) + lst = static_promote(static_last(x), static_last(y)) + return OptionallyStaticUnitRange(fst, lst) +end +Base.@propagate_inbounds function static_promote(x::AbstractRange{<:Integer}, + y::AbstractRange{<:Integer}) + @inline + fst = static_promote(static_first(x), static_first(y)) + stp = static_promote(static_step(x), static_step(y)) + lst = static_promote(static_last(x), static_last(y)) + return _OptionallyStaticStepRange(fst, stp, lst) +end + Base.@propagate_inbounds function _promote_shape(a::Tuple{A, Vararg{Any}}, b::Tuple{B, Vararg{Any}}) where {A, B} (static_promote(getfield(a, 1), getfield(b, 1)), diff --git a/src/ranges.jl b/src/ranges.jl index 40a50ee..3721dda 100644 --- a/src/ranges.jl +++ b/src/ranges.jl @@ -147,8 +147,8 @@ An alias for `OptionallyStaticUnitRange` usfeul for statically sized axes. const SOneTo{L} = SUnitRange{1, L} SOneTo(n::Int) = SOneTo{n}() -const OptionallyStaticRange = Union{<:OptionallyStaticUnitRange, <:OptionallyStaticStepRange - } +const OptionallyStaticRange{F, L} = Union{OptionallyStaticUnitRange{F, L}, + OptionallyStaticStepRange{F, <:Any, L}} # these probide a generic method for extracting potentially static values. static_first(x::Base.OneTo) = StaticInt(1) @@ -156,7 +156,7 @@ static_first(x::Union{Base.Slice, Base.IdentityUnitRange}) = static_first(x.indi static_first(x::OptionallyStaticRange) = getfield(x, :start) static_first(x) = first(x) -static_step(x::AbstractUnitRange) = StaticInt(1) +static_step(@nospecialize x::AbstractUnitRange) = StaticInt(1) static_step(x::OptionallyStaticStepRange) = getfield(x, :step) static_step(x) = step(x) @@ -164,9 +164,12 @@ static_last(x::OptionallyStaticRange) = getfield(x, :stop) static_last(x) = last(x) static_last(x::Union{Base.Slice, Base.IdentityUnitRange}) = static_last(x.indices) -Base.first(x::OptionallyStaticRange) = Int(static_first(x)) -Base.step(x::OptionallyStaticStepRange) = Int(static_step(x)) -Base.last(x::OptionallyStaticRange) = Int(static_last(x)) +Base.first(x::OptionallyStaticRange{Int}) = getfield(x, :start) +Base.first(::OptionallyStaticRange{StaticInt{F}}) where {F} = F +Base.step(x::OptionallyStaticStepRange{<:Any, Int}) = getfield(x, :step) +Base.step(::OptionallyStaticStepRange{<:Any, StaticInt{S}}) where {S} = S +Base.last(x::OptionallyStaticRange{<:Any, Int}) = getfield(x, :stop) +Base.last(::OptionallyStaticRange{<:Any, StaticInt{L}}) where {L} = L # FIXME this line causes invalidations Base.:(:)(L::Integer, ::StaticInt{U}) where {U} = OptionallyStaticUnitRange(L, StaticInt(U)) @@ -202,10 +205,16 @@ function Base.:(:)(start::Integer, ::StaticInt{1}, stop::Integer) OptionallyStaticUnitRange(start, stop) end -Base.isempty(r::OptionallyStaticUnitRange{One}) = last(r) <= 0 Base.isempty(r::OptionallyStaticUnitRange) = first(r) > last(r) -function Base.isempty(r::OptionallyStaticStepRange) - (r.start != r.stop) & ((r.step > 0) != (r.stop > r.start)) +@inline function Base.isempty(x::OptionallyStaticStepRange) + start = first(x) + stop = last(x) + if start === stop + return false + else + s = step(x) + s > 0 ? start > stop : start < stop + end end function Base.checkindex(::Type{Bool}, @@ -233,23 +242,17 @@ function Base.getindex(x::OptionallyStaticUnitRange, i::Int) end ## length -@inline function Base.length(r::OptionallyStaticUnitRange) - isempty(r) ? 0 : last(r) - first(r) + 1 +@inline function Base.length(x::OptionallyStaticUnitRange) + start = first(x) + stop = last(x) + start > stop ? 0 : stop - start + 1 end Base.length(r::OptionallyStaticStepRange) = _range_length(first(r), step(r), last(r)) @inline function _range_length(start::Int, s::Int, stop::Int) if s > 0 - if stop < start # isempty - return 0 - else - return div(stop - start, s) + 1 - end + stop < start ? 0 : div(stop - start, s) + 1 else - if stop > start # isempty - return 0 - else - return div(start - stop, -s) + 1 - end + stop > start ? 0 : div(start - stop, -s) + 1 end end @@ -263,19 +266,22 @@ function Base.AbstractUnitRange{T}(r::OptionallyStaticUnitRange) where {T} end end -@inline function Base.iterate(r::OptionallyStaticRange) - isempty(r) && return nothing - fi = Int(first(r)) - fi, fi +Base.isdone(x::OptionallyStaticRange, state::Int) = state === last(x) +function _next(x::OptionallyStaticRange) + new_state = first(x) + (new_state, new_state) end -Base.iterate(::SUnitRange{F, L}) where {F, L} = L < F ? nothing : (F, F) -@inline function Base.iterate(::SUnitRange{F,L}, s::Int) where {F,L} - if L <= s - s2 = s + 1 - return (s2, s2) - else - return nothing - end +@inline function _next(@nospecialize(x::OptionallyStaticUnitRange), state::Int) + new_state = state + 1 + (new_state, new_state) +end +@inline function _next(x::OptionallyStaticStepRange, state::Int) + new_state = state + step(x) + (new_state, new_state) +end +@inline Base.iterate(x::OptionallyStaticRange) = isempty(x) ? nothing : _next(x) +@inline function Base.iterate(x::OptionallyStaticRange, s::Int) + Base.isdone(x, s) ? nothing : _next(x, s) end Base.to_shape(x::OptionallyStaticRange) = length(x) diff --git a/test/ranges.jl b/test/ranges.jl index 4e7cbdc..7f254a0 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -14,7 +14,7 @@ @test Static.SUnitRange(1, 10) == 1:10 @test @inferred(Static.OptionallyStaticUnitRange{Int, Int}(1:10)) == 1:10 @test @inferred(Static.OptionallyStaticUnitRange(1:10)) == 1:10 == - @inferred(Static.OptionallyStaticUnitRange(Static.OptionallyStaticUnitRange(1:10))) + @inferred(Static.OptionallyStaticUnitRange(Static.OptionallyStaticUnitRange(1:10))) sr = Static.OptionallyStaticStepRange(static(1), static(1), static(1)) @test @inferred(Static.OptionallyStaticStepRange(sr)) == sr == 1:1:1 @@ -62,7 +62,7 @@ end # iteration -@test iterate(static(1):static(5)) === (1,1) +@test iterate(static(1):static(5)) === (1, 1) @test iterate(static(1):static(5), 1) === (2, 2) @test iterate(static(1):static(5), 5) === nothing @test iterate(static(2):static(5), 5) === nothing @@ -118,3 +118,12 @@ end @test Base.Broadcast.axistype(static(1):10, static(1):10) === Base.OneTo(10) @test Base.Broadcast.axistype(Base.OneTo(10), static(1):10) === Base.OneTo(10) + +@testset "static_promote(::AbstractRange, ::AbstractRange)" begin + ur1 = static(1):10 + ur2 = 1:static(10) + @test @inferred(static_promote(ur1, ur2)) === static(1):static(10) + sr1 = static(1):2:10 + sr2 = static(1):static(2):static(10) + @test @inferred(static_promote(sr1, sr2)) === sr2 +end From ff63b65aa98d7382a9dbb44c6c1fe0399fd20642 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Sun, 6 Nov 2022 01:11:08 -0400 Subject: [PATCH 07/10] Fix for v1.6 --- src/Static.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Static.jl b/src/Static.jl index 2079f3a..5dd91bd 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -276,16 +276,14 @@ julia> static_promote(1:2:9, static(1):static(2):static(9)) static(1):static(2):static(9) ``` """ -Base.@propagate_inbounds function static_promote(x::AbstractUnitRange{<:Integer}, +Base.@propagate_inbounds @inline function static_promote(x::AbstractUnitRange{<:Integer}, y::AbstractUnitRange{<:Integer}) - @inline fst = static_promote(static_first(x), static_first(y)) lst = static_promote(static_last(x), static_last(y)) return OptionallyStaticUnitRange(fst, lst) end -Base.@propagate_inbounds function static_promote(x::AbstractRange{<:Integer}, +Base.@propagate_inbounds @inline function static_promote(x::AbstractRange{<:Integer}, y::AbstractRange{<:Integer}) - @inline fst = static_promote(static_first(x), static_first(y)) stp = static_promote(static_step(x), static_step(y)) lst = static_promote(static_last(x), static_last(y)) From a55c82253e27018ddc927fc25692ff22fde257b9 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Mon, 7 Nov 2022 07:26:58 -0500 Subject: [PATCH 08/10] Full test coverage --- src/Static.jl | 4 ++-- src/ranges.jl | 3 +++ test/ranges.jl | 24 +++++++++++++++++++----- test/runtests.jl | 2 ++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/Static.jl b/src/Static.jl index 5dd91bd..bb8669a 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -277,13 +277,13 @@ static(1):static(2):static(9) ``` """ Base.@propagate_inbounds @inline function static_promote(x::AbstractUnitRange{<:Integer}, - y::AbstractUnitRange{<:Integer}) + y::AbstractUnitRange{<:Integer}) fst = static_promote(static_first(x), static_first(y)) lst = static_promote(static_last(x), static_last(y)) return OptionallyStaticUnitRange(fst, lst) end Base.@propagate_inbounds @inline function static_promote(x::AbstractRange{<:Integer}, - y::AbstractRange{<:Integer}) + y::AbstractRange{<:Integer}) fst = static_promote(static_first(x), static_first(y)) stp = static_promote(static_step(x), static_step(y)) lst = static_promote(static_last(x), static_last(y)) diff --git a/src/ranges.jl b/src/ranges.jl index 3721dda..78d1ac3 100644 --- a/src/ranges.jl +++ b/src/ranges.jl @@ -319,6 +319,7 @@ function Base.reverse(x::OptionallyStaticStepRange) _OptionallyStaticStepRange(getfield(x, :stop), -getfield(x, :step), getfield(x, :start)) end +Base.show(io::IO, @nospecialize(x::OptionallyStaticRange)) = show(io, MIME"text/plain"(), x) function Base.show(io::IO, ::MIME"text/plain", @nospecialize(r::OptionallyStaticUnitRange)) print(io, "$(getfield(r, :start)):$(getfield(r, :stop))") end @@ -326,6 +327,8 @@ function Base.show(io::IO, ::MIME"text/plain", @nospecialize(r::OptionallyStatic print(io, "$(getfield(r, :start)):$(getfield(r, :step)):$(getfield(r, :stop))") end +# we overload properties because occasionally Base assumes that abstract range types have +# the same exact same set up as native types where `x.start === first(x)` @inline function Base.getproperty(x::OptionallyStaticRange, s::Symbol) if s === :start return first(x) diff --git a/test/ranges.jl b/test/ranges.jl index 7f254a0..61d1a5b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -61,11 +61,22 @@ @test reverse(static(1):static(2):static(9)) === static(9):static(-2):static(1) end -# iteration -@test iterate(static(1):static(5)) === (1, 1) -@test iterate(static(1):static(5), 1) === (2, 2) -@test iterate(static(1):static(5), 5) === nothing -@test iterate(static(2):static(5), 5) === nothing +@testset "range properties" begin + x = static(1):static(2):static(9) + @test getproperty(x, :start) === first(x) + @test getproperty(x, :step) === step(x) + @test getproperty(x, :stop) === last(x) + @test_throws ErrorException getproperty(x, :foo) +end + +@testset "iterate" begin + @test iterate(static(1):static(5)) === (1, 1) + @test iterate(static(1):static(5), 1) === (2, 2) + @test iterate(static(1):static(5), 5) === nothing + @test iterate(static(2):static(5), 5) === nothing + @test iterate(static(1):static(2):static(9), 1) === (3, 3) + @test iterate(static(1):static(2):static(9), 9) === nothing +end # CartesianIndices CI = CartesianIndices((static(1):static(2), static(1):static(2))) @@ -90,6 +101,7 @@ CI = CartesianIndices((static(1):static(2), static(1):static(2))) static(10)))) == 5 end +@test @inferred(getindex(static(1):10, Base.Slice(static(1):10))) === static(1):10 @test @inferred(getindex(Static.OptionallyStaticUnitRange(static(1), 10), 1)) == 1 @test @inferred(getindex(Static.OptionallyStaticUnitRange(static(0), 10), 1)) == 0 @test_throws BoundsError getindex(Static.OptionallyStaticUnitRange(static(1), 10), 0) @@ -113,8 +125,10 @@ end @test Base.to_shape(static(1):10) == 10 @test Base.to_shape(Base.Slice(static(1):10)) == 10 +@test Base.axes1(Base.Slice(static(1):10)) === static(1):10 @test axes(Base.Slice(static(1):10)) === (static(1):10,) @test isa(axes(Base.Slice(static(0):static(1):10))[1], Base.IdentityUnitRange) +@test isa(Base.axes1(Base.Slice(static(0):static(1):10)), Base.IdentityUnitRange) @test Base.Broadcast.axistype(static(1):10, static(1):10) === Base.OneTo(10) @test Base.Broadcast.axistype(Base.OneTo(10), static(1):10) === Base.OneTo(10) diff --git a/test/runtests.jl b/test/runtests.jl index bbb5d26..bf6ee60 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -518,6 +518,8 @@ end @test repr(static(true)) == "static(true)" @test repr(static(CartesianIndex(1, 1))) == "NDIndex(static(1), static(1))" @test string(static(true)) == "static(true)" == "$(static(true))" + @test repr(static(1):static(10)) == "static(1):static(10)" + @test repr(static(1):static(2):static(9)) == "static(1):static(2):static(9)" end include("ranges.jl") From 6e65d8cc4a3689c47978b2a3f8f4391cc122ed2d Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Mon, 7 Nov 2022 07:45:39 -0500 Subject: [PATCH 09/10] Add `IntType` union and function This replaces the concept of "canonical" integers in ArrayInterface so we can also move that functionality here. --- src/Static.jl | 10 ++++++++++ src/ranges.jl | 41 +++++++++++++++++++---------------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Static.jl b/src/Static.jl index bb8669a..9ee9789 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -61,6 +61,16 @@ struct StaticInt{N} <: StaticInteger{N} StaticInt(::Val{N}) where {N} = StaticInt(N) end +""" + IntType(x::Integer) -> Union{Int,StaticInt} + +`IntType` is a union of `Int` and `StaticInt`. As a function, it ensures that `x` one of the +two. +""" +const IntType = Union{StaticInt,Int} +IntType(x::Integer) = Int(x) +IntType(@nospecialize x::Union{Int,StaticInt}) = x + include("float.jl") const StaticNumber{N} = Union{StaticInt{N}, StaticBool{N}, StaticFloat64{N}} diff --git a/src/ranges.jl b/src/ranges.jl index 78d1ac3..d5756c5 100644 --- a/src/ranges.jl +++ b/src/ranges.jl @@ -1,7 +1,4 @@ -_int(x::Integer) = Int(x) -_int(@nospecialize x::Union{StaticInt, Int}) = x - """ OptionallyStaticUnitRange(start, stop) <: AbstractUnitRange{Int} @@ -10,17 +7,17 @@ Similar to `UnitRange` except each field may be an `Int` or `StaticInt`. An indices. Therefore, users should not expect the same checks are used to ensure construction of a valid `OptionallyStaticUnitRange` as a `UnitRange`. """ -struct OptionallyStaticUnitRange{F <: Union{Int, StaticInt}, L <: Union{Int, StaticInt}} <: +struct OptionallyStaticUnitRange{F <: IntType, L <: IntType} <: AbstractUnitRange{Int} start::F stop::L - function OptionallyStaticUnitRange(start::Union{Int, StaticInt}, - stop::Union{Int, StaticInt}) + function OptionallyStaticUnitRange(start::IntType, + stop::IntType) new{typeof(start), typeof(stop)}(start, stop) end function OptionallyStaticUnitRange(start, stop) - OptionallyStaticUnitRange(_int(start), _int(stop)) + OptionallyStaticUnitRange(IntType(start), IntType(stop)) end OptionallyStaticUnitRange(@nospecialize x::OptionallyStaticUnitRange) = x function OptionallyStaticUnitRange(x::AbstractRange) @@ -59,40 +56,40 @@ static(2):static(2):10 ``` """ -struct OptionallyStaticStepRange{F <: Union{Int, StaticInt}, S <: Union{Int, StaticInt}, - L <: Union{Int, StaticInt}} <: OrdinalRange{Int, Int} +struct OptionallyStaticStepRange{F <: IntType, S <: IntType, + L <: IntType} <: OrdinalRange{Int, Int} start::F step::S stop::L - global function _OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), - @nospecialize(step::Union{Int, StaticInt}), - @nospecialize(stop::Union{Int, StaticInt})) + global function _OptionallyStaticStepRange(@nospecialize(start::IntType), + @nospecialize(step::IntType), + @nospecialize(stop::IntType)) new{typeof(start), typeof(step), typeof(stop)}(start, step, stop) end end -@noinline function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), +@noinline function OptionallyStaticStepRange(@nospecialize(start::IntType), ::StaticInt{0}, - @nospecialize(stop::Union{Int, StaticInt})) + @nospecialize(stop::IntType)) throw(ArgumentError("step cannot be zero")) end # we don't need to check the `stop` if we know it acts like a unit range -function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), +function OptionallyStaticStepRange(@nospecialize(start::IntType), step::StaticInt{1}, - @nospecialize(stop::Union{Int, StaticInt})) + @nospecialize(stop::IntType)) _OptionallyStaticStepRange(start, step, stop) end -function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), +function OptionallyStaticStepRange(@nospecialize(start::IntType), @nospecialize(step::StaticInt), - @nospecialize(stop::Union{Int, StaticInt})) + @nospecialize(stop::IntType)) _OptionallyStaticStepRange(start, step, _steprange_last(start, step, stop)) end function OptionallyStaticStepRange(start, step, stop) - OptionallyStaticStepRange(_int(start), _int(step), _int(stop)) + OptionallyStaticStepRange(IntType(start), IntType(step), IntType(stop)) end -function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), +function OptionallyStaticStepRange(@nospecialize(start::IntType), step::Int, - @nospecialize(stop::Union{Int, StaticInt})) + @nospecialize(stop::IntType)) if step === 0 throw(ArgumentError("step cannot be zero")) else @@ -101,7 +98,7 @@ function OptionallyStaticStepRange(@nospecialize(start::Union{Int, StaticInt}), end OptionallyStaticStepRange(@nospecialize x::OptionallyStaticStepRange) = x function OptionallyStaticStepRange(x::AbstractRange) - _OptionallyStaticStepRange(_int(first(x)), _int(step(x)), _int(last(x))) + _OptionallyStaticStepRange(IntType(first(x)), IntType(step(x)), IntType(last(x))) end # to make StepRange constructor inlineable, so optimizer can see `step` value From 92174fbaa89f0c74d2fac44cb6bc0ff2b3dc6940 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Mon, 7 Nov 2022 08:12:33 -0500 Subject: [PATCH 10/10] Fix formatting ... again --- src/Static.jl | 4 ++-- test/ranges.jl | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Static.jl b/src/Static.jl index 9ee9789..d5489ce 100644 --- a/src/Static.jl +++ b/src/Static.jl @@ -67,9 +67,9 @@ end `IntType` is a union of `Int` and `StaticInt`. As a function, it ensures that `x` one of the two. """ -const IntType = Union{StaticInt,Int} +const IntType = Union{StaticInt, Int} IntType(x::Integer) = Int(x) -IntType(@nospecialize x::Union{Int,StaticInt}) = x +IntType(@nospecialize x::Union{Int, StaticInt}) = x include("float.jl") diff --git a/test/ranges.jl b/test/ranges.jl index 61d1a5b..05670db 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -76,6 +76,10 @@ end @test iterate(static(2):static(5), 5) === nothing @test iterate(static(1):static(2):static(9), 1) === (3, 3) @test iterate(static(1):static(2):static(9), 9) === nothing + # make sure single length ranges work correctly + @test iterate(static(2):static(3):static(2))[1] === 2 === + (static(2):static(3):static(2))[1] + @test iterate(static(2):static(3):static(2), 2) === nothing end # CartesianIndices