Skip to content

Commit

Permalink
More informative error messages for InexactError
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed Jul 10, 2017
1 parent 86395fa commit 43175dd
Show file tree
Hide file tree
Showing 17 changed files with 72 additions and 45 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ Deprecated or removed
* `Bidiagonal` constructors now use a `Symbol` (`:U` or `:L`) for the upper/lower
argument, instead of a `Bool` or a `Char` ([#22703]).

* `InexactError` now takes arguments: `InexactError(func::Symbol,
type, -3)` now prints as `ERROR: InexactError: func(type, -3)`. ([#20005])

Julia v0.6.0 Release Notes
==========================
Expand Down Expand Up @@ -896,6 +898,7 @@ Command-line option changes
[#19949]: https://github.com/JuliaLang/julia/issues/19949
[#19950]: https://github.com/JuliaLang/julia/issues/19950
[#19989]: https://github.com/JuliaLang/julia/issues/19989
[#20005]: https://github.com/JuliaLang/julia/issues/20005
[#20009]: https://github.com/JuliaLang/julia/issues/20009
[#20047]: https://github.com/JuliaLang/julia/issues/20047
[#20058]: https://github.com/JuliaLang/julia/issues/20058
Expand Down
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(InexactError(:convert, Bool, x))
convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InexactError(:convert, Bool, x))

# promote Bool to any other numeric type
promote_rule(::Type{Bool}, ::Type{T}) where {T<:Number} = T
Expand Down
15 changes: 11 additions & 4 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ export
# string types
Char, DirectIndexString, AbstractString, String, IO,
# errors
ErrorException, BoundsError, DivideError, DomainError, Exception, InexactError,
InterruptException, OutOfMemoryError, ReadOnlyMemoryError, OverflowError,
StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, TypeError,
ErrorException, BoundsError, DivideError, DomainError, Exception,
InterruptException, InexactError, 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 @@ -206,7 +207,6 @@ end
struct DivideError <: Exception end
struct DomainError <: Exception end
struct OverflowError <: Exception end
struct InexactError <: Exception end
struct OutOfMemoryError <: Exception end
struct ReadOnlyMemoryError<: Exception end
struct SegmentationFault <: Exception end
Expand All @@ -222,6 +222,13 @@ mutable struct TypeError <: Exception
expected::Type
got
end
struct InexactError <: Exception
func::Symbol
T::Type
val

InexactError(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 @@ -35,7 +35,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(InexactError(:convert, T, z))

convert(::Type{Complex}, z::Complex) = z
convert(::Type{Complex}, x::Real) = Complex(x)
Expand Down
2 changes: 1 addition & 1 deletion base/dates/arithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ the `Time` are used along with the year, month, and day of the `Date` to create
Non-zero microseconds or nanoseconds in the `Time` type will result in an `InexactError` being thrown.
"""
function (+)(dt::Date, t::Time)
(microsecond(t) > 0 || nanosecond(t) > 0) && throw(InexactError())
(microsecond(t) > 0 || nanosecond(t) > 0) && throw(InexactError(:+, DateTime, t))
y, m, d = yearmonthday(dt)
return DateTime(y, m, d, hour(t), minute(t), second(t), millisecond(t))
end
Expand Down
7 changes: 5 additions & 2 deletions base/dates/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,17 @@ for (tok, fn) in zip("uUeE", [monthabbr_to_value, monthname_to_value, dayabbr_to
end
end

# 3-digit (base 10) number following a decimal point. For InexactError below.
struct Decimal3 end

@inline function tryparsenext(d::DatePart{'s'}, str, i, len)
ms, ii = tryparsenext_base10(str, i, len, min_width(d), max_width(d))
if !isnull(ms)
val = get(ms)
val0 = val = get(ms)
len = ii - i
if len > 3
val, r = divrem(val, Int64(10) ^ (len - 3))
r == 0 || throw(InexactError())
r == 0 || throw(InexactError(:convert, Decimal3, val0))
else
val *= Int64(10) ^ (3 - len)
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 @@ -399,7 +399,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(InexactError(:divexact, Int, x/y))
return q
end

Expand All @@ -414,7 +414,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(InexactError(:convert, $T, x))
return $T(value(x) * $N)
end
end
Expand All @@ -431,7 +431,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(InexactError(:convert, Month, x))
Month(value(x) * 12)
end
end
Expand Down
5 changes: 5 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,11 @@ function CartesianRange{N}(start::CartesianIndex{N}, stop::CartesianIndex{N})
inds = map((f,l)->f:l, start.I, stop.I)
depwarn("the internal representation of CartesianRange has changed, use CartesianRange($inds) (or other more approriate AbstractUnitRange type) instead.", :CartesianRange)
CartesianRange(inds)

# PR #20005
function InexactError()
depwarn("InexactError now supports arguments, use `InexactError(funcname::Symbol, ::Type, value)` instead.", :InexactError)
InexactError(:none, Any, nothing)
end

# PR #22703
Expand Down
10 changes: 5 additions & 5 deletions base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1380,16 +1380,16 @@ Convert a hexadecimal string to the floating point number it represents.
hex2num

"""
InexactError()
InexactError(name::Symbol, T, val)
Type conversion cannot be done exactly.
Cannot exactly convert `val` to type `T` in a method of function `name`.
# Examples
```jldoctest
julia> convert(Float64, 1+2im)
ERROR: InexactError()
ERROR: InexactError: convert(Float64, 1 + 2im)
Stacktrace:
[1] convert(::Type{Float64}, ::Complex{Int64}) at ./complex.jl:31
[1] convert(::Type{Float64}, ::Complex{Int64}) at ./complex.jl:37
```
"""
InexactError
Expand Down Expand Up @@ -1929,7 +1929,7 @@ julia> convert(Int, 3.0)
3
julia> convert(Int, 3.5)
ERROR: InexactError()
ERROR: InexactError: convert(Int64, 3.5)
Stacktrace:
[1] convert(::Type{Int64}, ::Float64) at ./float.jl:680
```
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(InexactError(: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(InexactError(: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(InexactError(: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(InexactError(:convert, $Ti, x))
end
end
end
Expand Down
10 changes: 5 additions & 5 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,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(InexactError(:convert, BigInt, x))
unsafe_trunc(BigInt,x)
end

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

Expand Down Expand Up @@ -319,7 +319,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(InexactError(:convert, T, x))
x % T
end
end
Expand All @@ -330,9 +330,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(InexactError(:convert, T, x))
y = x % T
ispos(x) (y > 0) && throw(InexactError()) # catch overflow
ispos(x) (y > 0) && throw(InexactError(:convert, T, x)) # catch overflow
y
end
end
Expand Down
6 changes: 3 additions & 3 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ function is_top_bit_set(x::BitInteger)
end
function check_top_bit(x::BitInteger)
@_inline_meta
is_top_bit_set(x) && throw(InexactError())
is_top_bit_set(x) && throw(InexactError(:check_top_bit, Integer, x))
x
end

Expand All @@ -411,15 +411,15 @@ function checked_trunc_sint{To,From}(::Type{To}, x::From)
@_inline_meta
y = trunc_int(To, x)
back = sext_int(From, y)
x == back || throw(InexactError())
x == back || throw(InexactError(:trunc, To, x))
y
end

function checked_trunc_uint{To,From}(::Type{To}, x::From)
@_inline_meta
y = trunc_int(To, x)
back = zext_int(From, y)
x == back || throw(InexactError())
x == back || throw(InexactError(:trunc, To, x))
y
end

Expand Down
2 changes: 1 addition & 1 deletion base/linalg/cholesky.jl
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ julia> A = [1 2; 2 50]
2 50
julia> cholfact!(A)
ERROR: InexactError()
ERROR: InexactError: convert(Int64, 6.782329983125268)
```
"""
function cholfact!(A::StridedMatrix, ::Val{false}=Val(false))
Expand Down
17 changes: 9 additions & 8 deletions base/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -202,20 +202,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(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
(typemin(T) <= x <= typemax(T)) || throw(InexactError())
(typemin(T) <= x <= typemax(T)) || throw(InexactError(: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(InexactError(: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(InexactError(: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(InexactError(:round, T, x))
unsafe_cast(T,x,ROUNDING_MODE[])
end

Expand All @@ -230,18 +230,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(InexactError(:convert, Bool, x))
function convert(::Type{BigInt},x::BigFloat)
isinteger(x) || throw(InexactError())
isinteger(x) || throw(InexactError(:convert, BigInt, x))
trunc(BigInt,x)
end

function convert(::Type{Integer}, x::BigFloat)
isinteger(x) || throw(InexactError())
isinteger(x) || throw(InexactError(:convert, Integer, x))
trunc(Integer,x)
end
function convert(::Type{T},x::BigFloat) where T<:Integer
isinteger(x) || throw(InexactError())
isinteger(x) || throw(InexactError(: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 @@ -1108,12 +1108,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(InexactError(...))
# 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(InexactError(: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
11 changes: 7 additions & 4 deletions base/rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ convert(::Type{Rational{T}}, x::Integer) where {T<:Integer} = Rational{T}(conver
convert(::Type{Rational}, x::Rational) = x
convert(::Type{Rational}, x::Integer) = convert(Rational{typeof(x)},x)

convert(::Type{Bool}, x::Rational) = x==0 ? false : x==1 ? true : throw(InexactError()) # to resolve ambiguity
convert(::Type{Integer}, x::Rational) = (isinteger(x) ? convert(Integer, x.num) : throw(InexactError()))
convert(::Type{T}, x::Rational) where {T<:Integer} = (isinteger(x) ? convert(T, x.num) : throw(InexactError()))
convert(::Type{Bool}, x::Rational) = x==0 ? false : x==1 ? true :
throw(InexactError(:convert, Bool, x)) # to resolve ambiguity
convert(::Type{Integer}, x::Rational) = (isinteger(x) ? convert(Integer, x.num) :
throw(InexactError(:convert, Integer, x)))
convert(::Type{T}, x::Rational) where {T<:Integer} = (isinteger(x) ? convert(T, x.num) :
throw(InexactError(:convert, T, x)))

convert(::Type{AbstractFloat}, x::Rational) = float(x.num)/float(x.den)
function convert(::Type{T}, x::Rational{S}) where T<:AbstractFloat where S
Expand All @@ -92,7 +95,7 @@ end

function convert(::Type{Rational{T}}, x::AbstractFloat) where T<:Integer
r = rationalize(T, x, tol=0)
x == convert(typeof(x), r) || throw(InexactError())
x == convert(typeof(x), r) || throw(InexactError(:convert, Rational{T}, x))
r
end
convert(::Type{Rational}, x::Float64) = convert(Rational{Int64}, x)
Expand Down
4 changes: 4 additions & 0 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@ function showerror(io::IO, ex::UndefVarError)
print(io, "UndefVarError: $(ex.var) not defined")
end

function showerror(io::IO, ex::InexactError)
print(io, "InexactError: ", ex.func, '(', ex.T, ", ", ex.val, ')')
end

function showerror(io::IO, ex::MethodError)
# ex.args is a tuple type if it was thrown from `invoke` and is
# a tuple of the arguments otherwise.
Expand Down

0 comments on commit 43175dd

Please sign in to comment.