Skip to content

Commit

Permalink
InexactError->InvalidValueError, with more informative backtraces
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed May 31, 2017
1 parent f988566 commit 71da8c7
Show file tree
Hide file tree
Showing 44 changed files with 212 additions and 161 deletions.
4 changes: 2 additions & 2 deletions base/bool.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
## boolean conversions ##

convert(::Type{Bool}, x::Bool) = x
convert(::Type{Bool}, x::Float16) = x==0 ? false : x==1 ? true : throw(InexactError())
convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InexactError())
convert(::Type{Bool}, x::Float16) = x==0 ? false : x==1 ? true : throw(InvalidValueError(:convert, Bool, x))
convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InvalidValueError(:convert, Bool, x))

# promote Bool to any other numeric type
promote_rule(::Type{Bool}, ::Type{T}) where {T<:Number} = T
Expand Down
12 changes: 10 additions & 2 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,9 @@ export
Char, DirectIndexString, AbstractString, String, IO,
# errors
ErrorException, BoundsError, DivideError, DomainError, Exception, InexactError,
InterruptException, OutOfMemoryError, ReadOnlyMemoryError, OverflowError,
StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, TypeError,
InterruptException, InvalidValueError, OutOfMemoryError, ReadOnlyMemoryError,
OverflowError, StackOverflowError, SegmentationFault, UndefRefError, UndefVarError,
TypeError,
# AST representation
Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode,
GlobalRef, NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot,
Expand Down Expand Up @@ -221,6 +222,13 @@ mutable struct TypeError <: Exception
expected::Type
got
end
struct InvalidValueError <: Exception
func::Symbol
T::Type
val

InvalidValueError(f::Symbol, T::ANY, val::ANY) = (@_noinline_meta; new(f, T, val))
end

abstract type DirectIndexString <: AbstractString end

Expand Down
2 changes: 1 addition & 1 deletion base/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const Complex32 = Complex{Float16}
convert(::Type{Complex{T}}, x::Real) where {T<:Real} = Complex{T}(x,0)
convert(::Type{Complex{T}}, z::Complex) where {T<:Real} = Complex{T}(real(z),imag(z))
convert(::Type{T}, z::Complex) where {T<:Real} =
isreal(z) ? convert(T,real(z)) : throw(InexactError())
isreal(z) ? convert(T,real(z)) : throw(InvalidValueError(:convert, T, z))

convert(::Type{Complex}, z::Complex) = z
convert(::Type{Complex}, x::Real) = Complex(x)
Expand Down
4 changes: 2 additions & 2 deletions base/dates/arithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
The addition of a `Date` with a `Time` produces a `DateTime`. The hour, minute, second, and millisecond parts of
the `Time` are used along with the year, month, and day of the `Date` to create the new `DateTime`.
Non-zero microseconds or nanoseconds in the `Time` type will result in an `InexactError` being thrown.
Non-zero microseconds or nanoseconds in the `Time` type will result in an `InvalidValueError` being thrown.
"""
function (+)(dt::Date, t::Time)
(microsecond(t) > 0 || nanosecond(t) > 0) && throw(InexactError())
(microsecond(t) > 0 || nanosecond(t) > 0) && throw(InvalidValueError(:+, DateTime, t))
y, m, d = yearmonthday(dt)
return DateTime(y, m, d, hour(t), minute(t), second(t), millisecond(t))
end
Expand Down
6 changes: 3 additions & 3 deletions base/dates/periods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ const FixedPeriod = Union{Week, Day, Hour, Minute, Second, Millisecond, Microsec
# like div but throw an error if remainder is nonzero
function divexact(x, y)
q, r = divrem(x, y)
r == 0 || throw(InexactError())
r == 0 || throw(InvalidValueError(:divexact, Int, x/y))
return q
end

Expand All @@ -415,7 +415,7 @@ for i = 1:length(fixedperiod_conversions)
vmax = typemax(Int64) ÷ N
vmin = typemin(Int64) ÷ N
@eval function Base.convert(::Type{$T}, x::$Tc)
$vmin value(x) $vmax || throw(InexactError())
$vmin value(x) $vmax || throw(InvalidValueError(:convert, $T, x))
return $T(value(x) * $N)
end
end
Expand All @@ -432,7 +432,7 @@ end
const OtherPeriod = Union{Month, Year}
let vmax = typemax(Int64) ÷ 12, vmin = typemin(Int64) ÷ 12
@eval function Base.convert(::Type{Month}, x::Year)
$vmin value(x) $vmax || throw(InexactError())
$vmin value(x) $vmax || throw(InvalidValueError(:convert, Month, x))
Month(value(x) * 12)
end
end
Expand Down
6 changes: 3 additions & 3 deletions base/distributed/pmap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ Example: On errors, retry `f` on an element a maximum of 3 times without any del
pmap(f, c; retry_delays = zeros(3))
```
Example: Retry `f` only if the exception is not of type `InexactError`, with exponentially increasing
delays up to 3 times. Return a `NaN` in place for all `InexactError` occurrences.
Example: Retry `f` only if the exception is not of type `InvalidValueError`, with exponentially increasing
delays up to 3 times. Return a `NaN` in place for all `InvalidValueError` occurrences.
```julia
pmap(f, c; on_error = e->(isa(e, InexactError) ? NaN : rethrow(e)), retry_delays = ExponentialBackOff(n = 3))
pmap(f, c; on_error = e->(isa(e, InvalidValueError) ? NaN : rethrow(e)), retry_delays = ExponentialBackOff(n = 3))
```
"""
function pmap(p::AbstractWorkerPool, f, c; distributed=true, batch_size=1, on_error=nothing,
Expand Down
19 changes: 13 additions & 6 deletions base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ ReadOnlyMemoryError
`ceil(x)` returns the nearest integral value of the same type as `x` that is greater than or
equal to `x`.
`ceil(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is not
`ceil(T, x)` converts the result to type `T`, throwing an `InvalidValueError` if the value is not
representable.
`digits` and `base` work as for [`round`](@ref).
Expand Down Expand Up @@ -646,7 +646,7 @@ Mmap.Anonymous
`floor(x)` returns the nearest integral value of the same type as `x` that is less than or
equal to `x`.
`floor(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is
`floor(T, x)` converts the result to type `T`, throwing an `InvalidValueError` if the value is
not representable.
`digits` and `base` work as for [`round`](@ref).
Expand Down Expand Up @@ -878,7 +878,7 @@ typeof
`trunc(x)` returns the nearest integral value of the same type as `x` whose absolute value
is less than or equal to `x`.
`trunc(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is
`trunc(T, x)` converts the result to type `T`, throwing an `InvalidValueError` if the value is
not representable.
`digits` and `base` work as for [`round`](@ref).
Expand Down Expand Up @@ -1374,6 +1374,13 @@ Type conversion cannot be done exactly.
"""
InexactError

"""
InvalidValueError(name::Symbol, T, val)
Cannot convert `val` to type `T` in a method of function `name`.
"""
InvalidValueError

"""
typemax(T)
Expand Down Expand Up @@ -1946,7 +1953,7 @@ done
Convert `x` to a value of type `T`.
If `T` is an `Integer` type, an [`InexactError`](@ref) will be raised if `x`
If `T` is an `Integer` type, an [`InvalidValueError`](@ref) will be raised if `x`
is not representable by `T`, for example if `x` is not integer-valued, or is outside the
range supported by `T`.
Expand All @@ -1955,9 +1962,9 @@ julia> convert(Int, 3.0)
3
julia> convert(Int, 3.5)
ERROR: InexactError()
ERROR: InvalidValueError: convert(Int64, 3.5::Float64)
Stacktrace:
[1] convert(::Type{Int64}, ::Float64) at ./float.jl:679
[1] convert(::Type{Int64}, ::Float64) at ./float.jl:680
```
If `T` is a `AbstractFloat` or `Rational` type,
Expand Down
8 changes: 4 additions & 4 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -649,14 +649,14 @@ for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UIn
if $(Tf(typemin(Ti))-one(Tf)) < x < $(Tf(typemax(Ti))+one(Tf))
return unsafe_trunc($Ti,x)
else
throw(InexactError())
throw(InvalidValueError(:trunc, $Ti, x))
end
end
function convert(::Type{$Ti}, x::$Tf)
if ($(Tf(typemin(Ti))) <= x <= $(Tf(typemax(Ti)))) && (trunc(x) == x)
return unsafe_trunc($Ti,x)
else
throw(InexactError())
throw(InvalidValueError(:convert, $Ti, x))
end
end
end
Expand All @@ -670,14 +670,14 @@ for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UIn
if $(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti)))
return unsafe_trunc($Ti,x)
else
throw(InexactError())
throw(InvalidValueError(:trunc, $Ti, x))
end
end
function convert(::Type{$Ti}, x::$Tf)
if ($(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti)))) && (trunc(x) == x)
return unsafe_trunc($Ti,x)
else
throw(InexactError())
throw(InvalidValueError(:convert, $Ti, x))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion base/floatfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ The optional [`RoundingMode`](@ref) argument will change how the number gets
rounded.
`round(T, x, [r::RoundingMode])` converts the result to type `T`, throwing an
[`InexactError`](@ref) if the value is not representable.
[`InvalidValueError`](@ref) if the value is not representable.
`round(x, digits)` rounds to the specified number of digits after the decimal place (or
before if negative). `round(x, digits, base)` rounds using a base other than 10.
Expand Down
10 changes: 5 additions & 5 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,12 @@ convert(::Type{BigInt}, x::Bool) = BigInt(UInt(x))
unsafe_trunc(::Type{BigInt}, x::Union{Float32,Float64}) = MPZ.set_d(x)

function convert(::Type{BigInt}, x::Union{Float32,Float64})
isinteger(x) || throw(InexactError())
isinteger(x) || throw(InvalidValueError(:convert, BigInt, x))
unsafe_trunc(BigInt,x)
end

function trunc(::Type{BigInt}, x::Union{Float32,Float64})
isfinite(x) || throw(InexactError())
isfinite(x) || throw(InvalidValueError(:trunc, BigInt, x))
unsafe_trunc(BigInt,x)
end

Expand Down Expand Up @@ -287,7 +287,7 @@ function convert(::Type{T}, x::BigInt) where T<:Unsigned
if sizeof(T) < sizeof(Limb)
convert(T, convert(Limb,x))
else
0 <= x.size <= cld(sizeof(T),sizeof(Limb)) || throw(InexactError())
0 <= x.size <= cld(sizeof(T),sizeof(Limb)) || throw(InvalidValueError(:convert, T, x))
x % T
end
end
Expand All @@ -298,9 +298,9 @@ function convert(::Type{T}, x::BigInt) where T<:Signed
SLimb = typeof(Signed(one(Limb)))
convert(T, convert(SLimb, x))
else
0 <= n <= cld(sizeof(T),sizeof(Limb)) || throw(InexactError())
0 <= n <= cld(sizeof(T),sizeof(Limb)) || throw(InvalidValueError(:convert, T, x))
y = x % T
ispos(x) (y > 0) && throw(InexactError()) # catch overflow
ispos(x) (y > 0) && throw(InvalidValueError(:convert, T, x)) # catch overflow
y
end
end
Expand Down
2 changes: 1 addition & 1 deletion base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ for to in BitInteger_types, from in (BitInteger_types..., Bool)
end
else
if !(issubtype(from, Signed) === issubtype(to, Signed))
# raise InexactError if x's top bit is set
# raise InvalidValueError if x's top bit is set
@eval convert(::Type{$to}, x::($from)) = bitcast($to, check_top_bit(x))
else
@eval convert(::Type{$to}, x::($from)) = bitcast($to, x)
Expand Down
2 changes: 1 addition & 1 deletion base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function gcd(a::T, b::T) where T<:Union{Int64,UInt64,Int128,UInt128}
v >>= trailing_zeros(v)
end
r = u << k
# T(r) would throw InexactError; we want OverflowError instead
# T(r) would throw InvalidValueError; we want OverflowError instead
r > typemax(T) && throw(OverflowError())
r % T
end
Expand Down
6 changes: 3 additions & 3 deletions base/linalg/cholesky.jl
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ end
cholfact!(A, [uplo::Symbol,] Val{false}) -> Cholesky
The same as [`cholfact`](@ref), but saves space by overwriting the input `A`,
instead of creating a copy. An [`InexactError`](@ref) exception is thrown if
instead of creating a copy. An [`InvalidValueError`](@ref) exception is thrown if
the factorization produces a number not representable by the element type of
`A`, e.g. for integer types.
Expand All @@ -217,7 +217,7 @@ julia> A = [1 2; 2 50]
2 50
julia> cholfact!(A)
ERROR: InexactError()
ERROR: InvalidValueError: convert(Int64, 6.782329983125268::Float64)
```
"""
function cholfact!(A::StridedMatrix, uplo::Symbol, ::Type{Val{false}})
Expand Down Expand Up @@ -254,7 +254,7 @@ cholfact!(A::RealHermSymComplexHerm{<:Real}, ::Type{Val{true}};
cholfact!(A, [uplo::Symbol,] Val{true}; tol = 0.0) -> CholeskyPivoted
The same as [`cholfact`](@ref), but saves space by overwriting the input `A`,
instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the
instead of creating a copy. An [`InvalidValueError`](@ref) exception is thrown if the
factorization produces a number not representable by the element type of `A`,
e.g. for integer types.
"""
Expand Down
2 changes: 1 addition & 1 deletion base/linalg/generic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Scale an array `A` by a scalar `b` overwriting `A` in-place.
If `A` is a matrix and `b` is a vector, then `scale!(A,b)` scales each column `i` of `A` by
`b[i]` (similar to `A*Diagonal(b)`), while `scale!(b,A)` scales each row `i` of `A` by `b[i]`
(similar to `Diagonal(b)*A`), again operating in-place on `A`. An `InexactError` exception is
(similar to `Diagonal(b)*A`), again operating in-place on `A`. An `InvalidValueError` exception is
thrown if the scaling produces a number not representable by the element type of `A`,
e.g. for integer types.
Expand Down
2 changes: 1 addition & 1 deletion base/linalg/lu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ end
lufact!(A, pivot=Val{true}) -> LU
`lufact!` is the same as [`lufact`](@ref), but saves space by overwriting the
input `A`, instead of creating a copy. An [`InexactError`](@ref)
input `A`, instead of creating a copy. An [`InvalidValueError`](@ref)
exception is thrown if the factorization produces a number not representable by the
element type of `A`, e.g. for integer types.
"""
Expand Down
2 changes: 1 addition & 1 deletion base/linalg/qr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ qrfact!(A::StridedMatrix{<:BlasFloat}) = qrfact!(A, Val{false})
`qrfact!` is the same as [`qrfact`](@ref) when `A` is a subtype of
`StridedMatrix`, but saves space by overwriting the input `A`, instead of creating a copy.
An [`InexactError`](@ref) exception is thrown if the factorization produces a number not
An [`InvalidValueError`](@ref) exception is thrown if the factorization produces a number not
representable by the element type of `A`, e.g. for integer types.
"""
qrfact!(A::StridedMatrix, ::Type{Val{false}}) = qrfactUnblocked!(A)
Expand Down
17 changes: 9 additions & 8 deletions base/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,20 @@ unsafe_cast(::Type{T}, x::BigFloat, r::RoundingMode) where {T<:Integer} = unsafe
unsafe_trunc(::Type{T}, x::BigFloat) where {T<:Integer} = unsafe_cast(T,x,RoundToZero)

function trunc{T<:Union{Signed,Unsigned}}(::Type{T}, x::BigFloat)
(typemin(T) <= x <= typemax(T)) || throw(InexactError())
(typemin(T) <= x <= typemax(T)) || throw(InvalidValueError(:trunc, T, x))
unsafe_cast(T,x,RoundToZero)
end
function floor(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
(typemin(T) <= x <= typemax(T)) || throw(InexactError())
(typemin(T) <= x <= typemax(T)) || throw(InvalidValueError(:floor, T, x))
unsafe_cast(T,x,RoundDown)
end
function ceil(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
(typemin(T) <= x <= typemax(T)) || throw(InexactError())
(typemin(T) <= x <= typemax(T)) || throw(InvalidValueError(:ceil, T, x))
unsafe_cast(T,x,RoundUp)
end

function round(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
(typemin(T) <= x <= typemax(T)) || throw(InexactError())
(typemin(T) <= x <= typemax(T)) || throw(InvalidValueError(:round, T, x))
unsafe_cast(T,x,ROUNDING_MODE[])
end

Expand All @@ -219,18 +219,19 @@ floor(::Type{Integer}, x::BigFloat) = floor(BigInt, x)
ceil(::Type{Integer}, x::BigFloat) = ceil(BigInt, x)
round(::Type{Integer}, x::BigFloat) = round(BigInt, x)

convert(::Type{Bool}, x::BigFloat) = x==0 ? false : x==1 ? true : throw(InexactError())
convert(::Type{Bool}, x::BigFloat) = x==0 ? false : x==1 ? true :
throw(InvalidValueError(:convert, Bool, x))
function convert(::Type{BigInt},x::BigFloat)
isinteger(x) || throw(InexactError())
isinteger(x) || throw(InvalidValueError(:convert, BigInt, x))
trunc(BigInt,x)
end

function convert(::Type{Integer}, x::BigFloat)
isinteger(x) || throw(InexactError())
isinteger(x) || throw(InvalidValueError(:convert, Integer, x))
trunc(Integer,x)
end
function convert(::Type{T},x::BigFloat) where T<:Integer
isinteger(x) || throw(InexactError())
isinteger(x) || throw(InvalidValueError(:convert, T, x))
trunc(T,x)
end

Expand Down
5 changes: 3 additions & 2 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1042,12 +1042,13 @@ function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArra
end

# Note: the next two functions rely on the following definition of the conversion to Bool:
# convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InexactError())
# convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InvalidValueError(...))
# they're used to pre-emptively check in bulk when possible, which is much faster.
# Also, the functions can be overloaded for custom types T<:Real :
# a) in the unlikely eventuality that they use a different logic for Bool conversion
# b) to skip the check if not necessary
@inline try_bool_conversion(x::Real) = x == 0 || x == 1 || throw(InexactError())
@inline try_bool_conversion(x::Real) =
x == 0 || x == 1 || throw(InvalidValueError(:try_bool_conversion, Bool, x))
@inline unchecked_bool_convert(x::Real) = x == 1

function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArray{<:Real}, pos_s::Int, numbits::Int)
Expand Down
2 changes: 1 addition & 1 deletion base/nullable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ null_safe_op(::typeof(!), ::Type{Bool}) = true

# Note this list does not include ^, ÷ and %
# Operations between signed and unsigned types are not safe: promotion to unsigned
# gives an InexactError for negative numbers
# gives an InvalidValueError for negative numbers
for op in (+, -, *, /, &, |, <<, >>, >>>,
scalarmin, scalarmax)
# to fix ambiguities
Expand Down
Loading

0 comments on commit 71da8c7

Please sign in to comment.