From 25d3c3cd7810d3325c2c5ca6276470247a6c06fb Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 8 Jul 2017 05:19:16 -0500 Subject: [PATCH] More informative error messages for InexactError --- NEWS.md | 3 +++ base/bool.jl | 4 ++-- base/boot.jl | 15 +++++++++++---- base/complex.jl | 2 +- base/dates/arithmetic.jl | 2 +- base/dates/io.jl | 7 +++++-- base/dates/periods.jl | 6 +++--- base/deprecated.jl | 6 ++++++ base/docs/helpdb/Base.jl | 10 +++++----- base/float.jl | 8 ++++---- base/gmp.jl | 10 +++++----- base/int.jl | 6 +++--- base/linalg/cholesky.jl | 2 +- base/mpfr.jl | 17 +++++++++-------- base/multidimensional.jl | 5 +++-- base/rational.jl | 11 +++++++---- base/replutil.jl | 4 ++++ 17 files changed, 73 insertions(+), 45 deletions(-) diff --git a/NEWS.md b/NEWS.md index bc64b9b43e7eb..f96740958f968 100644 --- a/NEWS.md +++ b/NEWS.md @@ -157,6 +157,8 @@ Deprecated or removed * `fieldnames` now operates only on types. To get the names of fields in an object, use `fieldnames(typeof(x))` ([#22350]). + * `InexactError` now takes arguments: `InexactError(func::Symbol, + type, -3)` now prints as `ERROR: InexactError: func(type, -3)`. ([#20005]) Julia v0.6.0 Release Notes ========================== @@ -902,6 +904,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 diff --git a/base/bool.jl b/base/bool.jl index f2c48bbd6266f..a73ecad6a5fc1 100644 --- a/base/bool.jl +++ b/base/bool.jl @@ -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 diff --git a/base/boot.jl b/base/boot.jl index 8066388cf30f2..5ba20e39a6704 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -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, @@ -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 @@ -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 diff --git a/base/complex.jl b/base/complex.jl index 2759b4b7799a2..a51c32895ed5d 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -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) diff --git a/base/dates/arithmetic.jl b/base/dates/arithmetic.jl index 05071369670e5..93d22c5f72119 100644 --- a/base/dates/arithmetic.jl +++ b/base/dates/arithmetic.jl @@ -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 diff --git a/base/dates/io.jl b/base/dates/io.jl index f588cfd0d4137..2005b941effab 100644 --- a/base/dates/io.jl +++ b/base/dates/io.jl @@ -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 diff --git a/base/dates/periods.jl b/base/dates/periods.jl index 628e077b1aec8..91391421c9b19 100644 --- a/base/dates/periods.jl +++ b/base/dates/periods.jl @@ -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 @@ -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 @@ -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 diff --git a/base/deprecated.jl b/base/deprecated.jl index 42cc9f63fe6f5..a619da84ad349 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1555,6 +1555,12 @@ function CartesianRange{N}(start::CartesianIndex{N}, stop::CartesianIndex{N}) CartesianRange(inds) end +# PR #20005 +function InexactError() + depwarn("InexactError now supports arguments, use `InexactError(funcname::Symbol, ::Type, value)` instead.", :InexactError) + InexactError(:none, Any, nothing) +end + # PR #22703 @deprecate Bidiagonal(dv::AbstractVector, ev::AbstractVector, isupper::Bool) Bidiagonal(dv, ev, ifelse(isupper, :U, :L)) @deprecate Bidiagonal(dv::AbstractVector, ev::AbstractVector, uplo::Char) Bidiagonal(dv, ev, ifelse(uplo == 'U', :U, :L)) diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index 67f2a08b950eb..a24f9a7583985 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -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 @@ -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 ``` diff --git a/base/float.jl b/base/float.jl index 6eece89522c61..0b7306d64fc16 100644 --- a/base/float.jl +++ b/base/float.jl @@ -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 @@ -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 diff --git a/base/gmp.jl b/base/gmp.jl index 54eee8340a590..a15cc43d03fa4 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -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 @@ -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 @@ -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 diff --git a/base/int.jl b/base/int.jl index e6567a9e673a7..9edfacff6c6ee 100644 --- a/base/int.jl +++ b/base/int.jl @@ -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 @@ -411,7 +411,7 @@ 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 @@ -419,7 +419,7 @@ 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 diff --git a/base/linalg/cholesky.jl b/base/linalg/cholesky.jl index c19d710067cc7..59360c0d3d5e1 100644 --- a/base/linalg/cholesky.jl +++ b/base/linalg/cholesky.jl @@ -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)) diff --git a/base/mpfr.jl b/base/mpfr.jl index d37061d35a1d0..0da255045918b 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -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 @@ -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 diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 40d7cc225cf28..9f17229f31d25 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -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) diff --git a/base/rational.jl b/base/rational.jl index c9113fa672ba8..b6af61273dfce 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -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 @@ -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) diff --git a/base/replutil.jl b/base/replutil.jl index 519eb9066fd93..5a07e8a45d1a4 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -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.