Skip to content

Commit d728d7f

Browse files
committed
fixup! deprecate unsafe_length for length
1 parent efede49 commit d728d7f

File tree

4 files changed

+98
-39
lines changed

4 files changed

+98
-39
lines changed

base/checked.jl

+10-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ module Checked
66

77
export checked_neg, checked_abs, checked_add, checked_sub, checked_mul,
88
checked_div, checked_rem, checked_fld, checked_mod, checked_cld,
9-
add_with_overflow, sub_with_overflow, mul_with_overflow
9+
checked_length, add_with_overflow, sub_with_overflow, mul_with_overflow
1010

1111
import Core.Intrinsics:
1212
checked_sadd_int, checked_ssub_int, checked_smul_int, checked_sdiv_int,
1313
checked_srem_int,
1414
checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int,
1515
checked_urem_int
16-
import ..no_op_err, ..@_inline_meta, ..@_noinline_meta
16+
import ..no_op_err, ..@_inline_meta, ..@_noinline_meta, ..checked_length
1717

1818
# define promotion behavior for checked operations
1919
checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...)
@@ -349,4 +349,12 @@ The overflow protection may impose a perceptible performance penalty.
349349
"""
350350
checked_cld(x::T, y::T) where {T<:Integer} = cld(x, y) # Base.cld already checks
351351

352+
"""
353+
Base.checked_length(r)
354+
355+
Calculates `length(r)`, but may check for overflow errors where applicable when
356+
the result doesn't fit into `Union{eltype(r),Int}`.
357+
"""
358+
checked_length(r) = length(r) # for most things, length doesn't error
359+
352360
end

base/deprecated.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ end
240240
@deprecate cat_shape(dims, shape::Tuple{}, shapes::Tuple...) cat_shape(dims, shapes) false
241241
cat_shape(dims, shape::Tuple{}) = () # make sure `cat_shape(dims, ())` do not recursively calls itself
242242

243-
@deprecate unsafe_indices(A) axes(A)
244-
@deprecate unsafe_length(r) length(r)
243+
@deprecate unsafe_indices(A) axes(A) false
244+
@deprecate unsafe_length(r) length(r) false
245245

246246
# END 1.6 deprecations
247247

base/range.jl

+80-26
Original file line numberDiff line numberDiff line change
@@ -631,46 +631,100 @@ firstindex(::UnitRange) = 1
631631
firstindex(::StepRange) = 1
632632
firstindex(::LinRange) = 1
633633

634-
length(r::AbstractUnitRange) = Integer(last(r) - first(r) + step(r))
634+
# n.b. checked_length for these is defined iff checked_add and checked_sub are
635+
# defined between the relevant types
636+
function checked_length(r::OrdinalRange{T}) where T
637+
s = step(r)
638+
# s != 0, by construction, but avoids the division error later
639+
start = first(r)
640+
if s == zero(s) || isempty(r)
641+
return Integer(start - start + zero(s))
642+
end
643+
return Integer(div(checked_add(checked_sub(last(r), start), s, s)))
644+
end
645+
646+
function checked_length(r::AbstractUnitRange{T}) where T
647+
# compiler optimization: remove dead cases from above
648+
if isempty(r)
649+
return Integer(first(r) - first(r))
650+
end
651+
a = Integer(checked_add(checked_sub(last(r), first(r))))
652+
return a + one(a)
653+
end
654+
655+
function length(r::OrdinalRange{T}) where T
656+
s = step(r)
657+
# s != 0, by construction, but avoids the division error later
658+
start = first(r)
659+
if s == zero(s) || isempty(r)
660+
return Integer(start - start + zero(s))
661+
end
662+
return Integer(div(last(r) - start + s, s))
663+
end
664+
665+
function length(r::AbstractUnitRange{T}) where T
666+
@_inline_meta
667+
a = Integer(last(r) - first(r)) # even when isempty, by construction (with overflow)
668+
return a + one(a)
669+
end
670+
635671
length(r::OneTo) = Integer(r.stop - zero(r.stop))
636672
length(r::StepRangeLen) = r.len
637673
length(r::LinRange) = r.len
638-
length(r::StepRange) = Integer(div(r.stop - r.start + r.step, r.step))
639674

640-
# compile optimizations for which promote_type(T, Int) = T
641-
let bigint = Union{Int,UInt,Int64,UInt64,Int128,UInt128}
675+
let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128}
642676
global length
643-
function length(r::StepRange{T}) where T<:bigint
644-
step = r.step
645-
diff = r.stop - r.start
646-
step == 0 && return zero(T) # unreachable, by construction, but avoids the error case here later
677+
# compile optimization for which promote_type(T, Int) == T
678+
length(r::OneTo{T}) where {T<:bigints} = r.stop
679+
# slightly more accurate length and checked_length in extreme cases
680+
# (near typemax) for types with known `unsigned` functions
681+
function length(r::OrdinalRange{T}) where T<:bigints
682+
s = step(r)
683+
s == zero(s) && return zero(T) # unreachable, by construction, but avoids the error case here later
647684
isempty(r) && return zero(T)
648-
if -1 <= step <= 1 || step == -step || step isa Unsigned # n.b. !(step isa T)
649-
# if |step| > 1, diff might have overflowed, but unsigned(diff)÷step should
650-
# therefore still be valid (if the result is representable at all)
651-
return div(diff, step) % T + one(T)
652-
elseif step < 0
653-
return div(unsigned(-diff), -step) % T + one(T)
685+
diff = last(r) - first(r)
686+
# if |s| > 1, diff might have overflowed, but unsigned(diff)÷s should
687+
# therefore still be valid (if the result is representable at all)
688+
# n.b. !(s isa T)
689+
if s isa Unsigned || -1 <= s <= 1 || s == -s
690+
a = div(diff, s)
691+
elseif s < 0
692+
a = div(unsigned(-diff), -s) % typeof(diff)
654693
else
655-
return div(unsigned(diff), step) % T + one(T)
694+
a = div(unsigned(diff), s) % typeof(diff)
656695
end
696+
return Integer(a) + one(a)
657697
end
658-
659-
function length(r::AbstractUnitRange{T}) where T<:bigint
660-
@_inline_meta
661-
return last(r) - first(r) + one(T) # even when isempty, by construction (with overflow)
698+
function checked_length(r::OrdinalRange{T}) where T<:bigints
699+
s = step(r)
700+
s == zero(s) && return zero(T) # unreachable, by construction, but avoids the error case here later
701+
isempty(r) && return zero(T)
702+
stop, start = last(r), first(r)
703+
# n.b. !(s isa T)
704+
if s > 1
705+
diff = stop - start
706+
a = convert(T, div(unsigned(diff), s))
707+
elseif s < -1
708+
diff = start - stop
709+
a = convert(T, div(unsigned(diff), -s))
710+
elseif s > 0
711+
a = div(checked_sub(stop, start), s)
712+
else
713+
a = div(checked_sub(start, stop), -s)
714+
end
715+
return checked_add(a, one(a))
662716
end
663-
length(r::OneTo{T}) where {T<:bigint} = r.stop
664717
end
665718

666719
# some special cases to favor default Int type
667-
let smallint = (Int === Int64 ?
668-
Union{Int8,UInt8,Int16,UInt16,Int32,UInt32} :
669-
Union{Int8,UInt8,Int16,UInt16})
720+
let smallints = (Int === Int64 ?
721+
Union{Int8, UInt8, Int16, UInt16, Int32, UInt32} :
722+
Union{Int8, UInt8, Int16, UInt16})
670723
global length
671-
length(r::StepRange{<:smallint}) = div(Int(r.stop) - Int(r.start) + r.step, r.step) # n.b. !(step isa T)
672-
length(r::AbstractUnitRange{<:smallint}) = Int(last(r)) - Int(first(r)) + 1
673-
length(r::OneTo{<:smallint}) = Int(r.stop)
724+
# n.b. !(step isa T)
725+
length(r::OrdinalRange{<:smallints}) = div(Int(last(r)) - Int(first(r)) + step(r), step(r))
726+
length(r::AbstractUnitRange{<:smallints}) = Int(last(r)) - Int(first(r)) + 1
727+
length(r::OneTo{<:smallints}) = Int(r.stop)
674728
end
675729

676730
first(r::OrdinalRange{T}) where {T} = convert(T, r.start)

test/testhelpers/Furlongs.jl

+6-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Furlong{p}(x::Furlong{q}) where {p,q} = (@assert(p==q); Furlong{p,typeof(x.val)}
2020
Furlong{p,T}(x::Furlong{q}) where {T,p,q} = (@assert(p==q); Furlong{p,T}(T(x.val)))
2121

2222
Base.promote_type(::Type{Furlong{p,T}}, ::Type{Furlong{p,S}}) where {p,T,S} =
23-
(Base.@_pure_meta; Furlong{p,promote_type(T,S)})
23+
Furlong{p,promote_type(T,S)}
2424

2525
Base.one(x::Furlong{p,T}) where {p,T} = one(T)
2626
Base.one(::Type{Furlong{p,T}}) where {p,T} = one(T)
@@ -38,14 +38,12 @@ Base.floatmax(::Type{Furlong{p,T}}) where {p,T<:AbstractFloat} = Furlong{p}(floa
3838
Base.floatmax(::Furlong{p,T}) where {p,T<:AbstractFloat} = floatmax(Furlong{p,T})
3939
Base.conj(x::Furlong{p,T}) where {p,T} = Furlong{p,T}(conj(x.val))
4040

41-
# convert Furlong exponent p to a canonical form. This
42-
# is not type stable, but it doesn't matter since it is used
43-
# at compile time (in generated functions), not runtime
41+
# convert Furlong exponent p to a canonical form
4442
canonical_p(p) = isinteger(p) ? Int(p) : Rational{Int}(p)
4543

4644
Base.abs(x::Furlong{p}) where {p} = Furlong{p}(abs(x.val))
47-
@generated Base.abs2(x::Furlong{p}) where {p} = :(Furlong{$(canonical_p(2p))}(abs2(x.val)))
48-
@generated Base.inv(x::Furlong{p}) where {p} = :(Furlong{$(canonical_p(-p))}(inv(x.val)))
45+
Base.abs2(x::Furlong{p}) where {p} = Furlong{canonical_p(2p)}(abs2(x.val))
46+
Base.inv(x::Furlong{p}) where {p} = Furlong{canonical_p(-p)}(inv(x.val))
4947

5048
for f in (:isfinite, :isnan, :isreal, :isinf)
5149
@eval Base.$f(x::Furlong) = $f(x.val)
@@ -64,11 +62,10 @@ end
6462
for op in (:(==), :(!=), :<, :<=, :isless, :isequal)
6563
@eval $op(x::Furlong{p}, y::Furlong{p}) where {p} = $op(x.val, y.val)
6664
end
67-
# generated functions to allow type inference of the value of the exponent:
6865
for (f,op) in ((:_plus,:+),(:_minus,:-),(:_times,:*),(:_div,://))
69-
@eval @generated function $f(v::T, ::Furlong{p}, ::Union{Furlong{q},Val{q}}) where {T,p,q}
66+
@eval function $f(v::T, ::Furlong{p}, ::Union{Furlong{q},Val{q}}) where {T,p,q}
7067
s = $op(p, q)
71-
:(Furlong{$(canonical_p(s)),$T}(v))
68+
Furlong{canonical_p(s),T}(v)
7269
end
7370
end
7471
for (op,eop) in ((:*, :_plus), (:/, :_minus), (://, :_minus), (:div, :_minus))

0 commit comments

Comments
 (0)