Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make InexactError more informative #20005

Merged
merged 2 commits into from
Jul 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
==========================
Expand Down Expand Up @@ -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
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))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

about to be deprecated, depending on the order things get merged in

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's about to be deprecated?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ANY

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you mean ANY. Yes, very exciting.

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
6 changes: 6 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
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
9 changes: 6 additions & 3 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ const BitIntegerSmall = Union{BitIntegerSmall_types...}
const BitSigned64T = Union{Type{Int8}, Type{Int16}, Type{Int32}, Type{Int64}}
const BitUnsigned64T = Union{Type{UInt8}, Type{UInt16}, Type{UInt32}, Type{UInt64}}

throw_inexacterror(f::Symbol, ::Type{T}, val) where T =
(@_noinline_meta; throw(InexactError(f, T, val)))

## integer comparisons ##

(<)(x::T, y::T) where {T<:BitSigned} = slt_int(x, y)
Expand Down Expand Up @@ -401,7 +404,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, typeof(x), x)
x
end

Expand All @@ -411,15 +414,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