From d5cb6d18225bbec2746a53891741eca54518bc86 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 8 Aug 2017 02:21:40 -0500 Subject: [PATCH] Make OverflowError more informative --- NEWS.md | 10 ++++++---- base/boot.jl | 4 +++- base/checked.jl | 19 ++++++++++++------- base/combinatorics.jl | 2 +- base/deprecated.jl | 6 ++++++ base/docs/helpdb/Base.jl | 2 +- base/gmp.jl | 4 +++- base/intfuncs.jl | 6 ++++-- base/parse.jl | 2 +- base/rational.jl | 4 ++-- base/replutil.jl | 1 + src/codegen.cpp | 1 - src/datatype.c | 2 +- src/init.c | 1 - src/julia.h | 1 - 15 files changed, 41 insertions(+), 24 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1b7ed22f5b9e4..9a95ad0f1f10f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -242,11 +242,12 @@ Deprecated or removed * `fieldnames` now operates only on types. To get the names of fields in an object, use `fieldnames(typeof(x))` ([#22350]). - * `InexactError` and `DomainError` now take + * `InexactError`, `DomainError`, and `OverflowError` now take arguments. `InexactError(func::Symbol, type, -3)` now prints as - `ERROR: InexactError: func(type, -3)`, and `DomainError(val, - [msg])` prints as `ERROR: DomainError with val:\nmsg`. ([#20005], - [#22751]) + "ERROR: InexactError: func(type, -3)", `DomainError(val, + [msg])` prints as "ERROR: DomainError with val:\nmsg", + and `OverflowError(msg)` prints as "ERROR: OverflowError: msg". + ([#20005], [#22751], [#22761]) * The operating system identification functions: `is_linux`, `is_bsd`, `is_apple`, `is_unix`, and `is_windows`, have been deprecated in favor of `Sys.islinux`, `Sys.isbsd`, `Sys.isapple`, @@ -1118,6 +1119,7 @@ Command-line option changes [#22723]: https://github.com/JuliaLang/julia/issues/22723 [#22732]: https://github.com/JuliaLang/julia/issues/22732 [#22751]: https://github.com/JuliaLang/julia/issues/22751 +[#22761]: https://github.com/JuliaLang/julia/issues/22761 [#22762]: https://github.com/JuliaLang/julia/issues/22762 [#22793]: https://github.com/JuliaLang/julia/issues/22793 [#22796]: https://github.com/JuliaLang/julia/issues/22796 diff --git a/base/boot.jl b/base/boot.jl index fd714d4c9addf..db7cdcbd1159e 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -212,7 +212,6 @@ struct BoundsError <: Exception BoundsError(@nospecialize(a), i) = (@_noinline_meta; new(a,i)) end struct DivideError <: Exception end -struct OverflowError <: Exception end struct OutOfMemoryError <: Exception end struct ReadOnlyMemoryError<: Exception end struct SegmentationFault <: Exception end @@ -241,6 +240,9 @@ struct InexactError <: Exception InexactError(f::Symbol, @nospecialize(T), @nospecialize(val)) = (@_noinline_meta; new(f, T, val)) end +struct OverflowError <: Exception + msg +end abstract type DirectIndexString <: AbstractString end diff --git a/base/checked.jl b/base/checked.jl index 215f12a62b52e..2870461a43205 100644 --- a/base/checked.jl +++ b/base/checked.jl @@ -13,7 +13,7 @@ import Core.Intrinsics: checked_srem_int, checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int, checked_urem_int -import Base: no_op_err, @_inline_meta +import Base: no_op_err, @_inline_meta, @_noinline_meta # define promotion behavior for checked operations checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...) @@ -90,16 +90,18 @@ The overflow protection may impose a perceptible performance penalty. function checked_neg(x::T) where T<:Integer checked_sub(T(0), x) end +throw_overflowerr_negation(x) = (@_noinline_meta; + throw(OverflowError("checked arithmetic: cannot compute -x for x = $x::$(typeof(x))"))) if BrokenSignedInt != Union{} function checked_neg(x::BrokenSignedInt) r = -x - (x<0) & (r<0) && throw(OverflowError()) + (x<0) & (r<0) && throw_overflowerr_negation(x) r end end if BrokenUnsignedInt != Union{} function checked_neg(x::T) where T<:BrokenUnsignedInt - x != 0 && throw(OverflowError()) + x != 0 && throw_overflowerr_negation(x) T(0) end end @@ -117,7 +119,7 @@ function checked_abs end function checked_abs(x::SignedInt) r = ifelse(x<0, -x, x) - r<0 && throw(OverflowError()) + r<0 && throw(OverflowError(string("checked arithmetic: cannot compute |x| for x = ", x, "::", typeof(x)))) r end checked_abs(x::UnsignedInt) = x @@ -152,6 +154,9 @@ end end +throw_overflowerr_binaryop(op, x, y) = (@_noinline_meta; + throw(OverflowError("$x $op $y overflowed for type $(typeof(x))"))) + """ Base.checked_add(x, y) @@ -162,7 +167,7 @@ The overflow protection may impose a perceptible performance penalty. function checked_add(x::T, y::T) where T<:Integer @_inline_meta z, b = add_with_overflow(x, y) - b && throw(OverflowError()) + b && throw_overflowerr_binaryop(:+, x, y) z end @@ -219,7 +224,7 @@ The overflow protection may impose a perceptible performance penalty. function checked_sub(x::T, y::T) where T<:Integer @_inline_meta z, b = sub_with_overflow(x, y) - b && throw(OverflowError()) + b && throw_overflowerr_binaryop(:-, x, y) z end @@ -284,7 +289,7 @@ The overflow protection may impose a perceptible performance penalty. function checked_mul(x::T, y::T) where T<:Integer @_inline_meta z, b = mul_with_overflow(x, y) - b && throw(OverflowError()) + b && throw_overflowerr_binaryop(:*, x, y) z end diff --git a/base/combinatorics.jl b/base/combinatorics.jl index 51f7cb1d57957..28e0e451b89b7 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -16,7 +16,7 @@ end function factorial_lookup(n::Integer, table, lim) n < 0 && throw(DomainError(n, "`n` must not be negative.")) - n > lim && throw(OverflowError()) + n > lim && throw(OverflowError(string(n, " is too large to look up in the table"))) n == 0 && return one(n) @inbounds f = table[n] return oftype(n, f) diff --git a/base/deprecated.jl b/base/deprecated.jl index 747be5381fcfa..61fd06f90cd89 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1549,6 +1549,12 @@ function DomainError() DomainError(nothing) end +# PR #22761 +function OverflowError() + depwarn("OverflowError now supports a message string, use `OverflowError(msg)` instead.", :OverflowError) + OverflowError("") +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 494242bf2e336..6329665c0e3b5 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -1509,7 +1509,7 @@ used only with extreme caution, as it can cause memory use to grow without bound gc_enable """ - OverflowError() + OverflowError(msg) The result of an expression is too large for the specified type and will cause a wraparound. """ diff --git a/base/gmp.jl b/base/gmp.jl index f5cfa8c75e08b..6ae010c511521 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -494,6 +494,8 @@ isqrt(x::BigInt) = MPZ.sqrt(x) function bigint_pow(x::BigInt, y::Integer) if y<0; throw(DomainError(y, "`y` cannot be negative.")); end + @noinline throw1(y) = + throw(OverflowError("exponent $y is too large and computation will overflow")) if x== 1; return x; end if x==-1; return isodd(y) ? x : -x; end if y>typemax(Culong) @@ -507,7 +509,7 @@ function bigint_pow(x::BigInt, y::Integer) # #Assume that the answer will definitely overflow. - throw(OverflowError()) + throw1(y) end return x^convert(Culong, y) end diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 875e4f34309fc..b95c54ef6f9ee 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -28,6 +28,7 @@ end # binary GCD (aka Stein's) algorithm # about 1.7x (2.1x) faster for random Int64s (Int128s) function gcd(a::T, b::T) where T<:Union{Int64,UInt64,Int128,UInt128} + @noinline throw1(a, b) = throw(OverflowError("gcd($a, $b) overflows")) a == 0 && return abs(b) b == 0 && return abs(a) za = trailing_zeros(a) @@ -44,7 +45,7 @@ function gcd(a::T, b::T) where T<:Union{Int64,UInt64,Int128,UInt128} end r = u << k # T(r) would throw InexactError; we want OverflowError instead - r > typemax(T) && throw(OverflowError()) + r > typemax(T) && throw1(a, b) r % T end @@ -841,6 +842,7 @@ julia> factorial(5) รท (factorial(5-3) * factorial(3)) ``` """ function binomial(n::T, k::T) where T<:Integer + n0, k0 = n, k k < 0 && return zero(T) sgn = one(T) if n < 0 @@ -861,7 +863,7 @@ function binomial(n::T, k::T) where T<:Integer while rr <= k xt = div(widemul(x, nn), rr) x = xt - x == xt || throw(OverflowError()) + x == xt || throw(OverflowError("binomial($n0, $k0) overflows")) rr += 1 nn += 1 end diff --git a/base/parse.jl b/base/parse.jl index 543429ce142d9..87b7c6aed6c92 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -112,7 +112,7 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: n, ov_mul = mul_with_overflow(n, base) n, ov_add = add_with_overflow(n, d) if ov_mul | ov_add - raise && throw(OverflowError()) + raise && throw(OverflowError("overflow parsing $(repr(SubString(s,startpos,endpos)))")) return _n end (i > endpos) && return Nullable{T}(n) diff --git a/base/rational.jl b/base/rational.jl index 775fde17793ae..87c0c2cb0cdd9 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -233,11 +233,11 @@ isinteger(x::Rational) = x.den == 1 -(x::Rational) = (-x.num) // x.den function -(x::Rational{T}) where T<:Signed - x.num == typemin(T) && throw(OverflowError()) + x.num == typemin(T) && throw(OverflowError("rational numerator is typemin(T)")) (-x.num) // x.den end function -(x::Rational{T}) where T<:Unsigned - x.num != zero(T) && throw(OverflowError()) + x.num != zero(T) && throw(OverflowError("cannot negate unsigned number")) x end diff --git a/base/replutil.jl b/base/replutil.jl index 5e4c9e7611aa4..d5329602aacc0 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -275,6 +275,7 @@ showerror(io::IO, ex::KeyError) = print(io, "KeyError: key $(repr(ex.key)) not f showerror(io::IO, ex::InterruptException) = print(io, "InterruptException:") showerror(io::IO, ex::ArgumentError) = print(io, "ArgumentError: $(ex.msg)") showerror(io::IO, ex::AssertionError) = print(io, "AssertionError: $(ex.msg)") +showerror(io::IO, ex::OverflowError) = print(io, "OverflowError: $(ex.msg)") function showerror(io::IO, ex::UndefVarError) if ex.var in [:UTF16String, :UTF32String, :WString, :utf16, :utf32, :wstring, :RepString] diff --git a/src/codegen.cpp b/src/codegen.cpp index 5da15c3b05337..5bfb87e9cda61 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6015,7 +6015,6 @@ static void init_julia_llvm_env(Module *m) global_jlvalue_to_llvm("jl_emptytuple", &jl_emptytuple, m); global_jlvalue_to_llvm("jl_diverror_exception", &jl_diverror_exception, m); global_jlvalue_to_llvm("jl_undefref_exception", &jl_undefref_exception, m); - global_jlvalue_to_llvm("jl_overflow_exception", &jl_overflow_exception, m); jlRTLD_DEFAULT_var = new GlobalVariable(*m, T_pint8, diff --git a/src/datatype.c b/src/datatype.c index e97a26ad53bf1..8be34ce935d80 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -347,7 +347,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) return; throw_ovf: if (descsz >= jl_page_size) free(desc); - jl_throw(jl_overflow_exception); + jl_errorf("type %s has field offset %d that exceeds the page size", jl_symbol_name(st->name), descsz); } extern int jl_boot_file_loaded; diff --git a/src/init.c b/src/init.c index 89169eecc2374..9d9ef5273e1bc 100644 --- a/src/init.c +++ b/src/init.c @@ -785,7 +785,6 @@ void jl_get_builtin_hooks(void) jl_errorexception_type = (jl_datatype_t*)core("ErrorException"); jl_stackovf_exception = jl_new_struct_uninit((jl_datatype_t*)core("StackOverflowError")); jl_diverror_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideError")); - jl_overflow_exception = jl_new_struct_uninit((jl_datatype_t*)core("OverflowError")); jl_undefref_exception = jl_new_struct_uninit((jl_datatype_t*)core("UndefRefError")); jl_undefvarerror_type = (jl_datatype_t*)core("UndefVarError"); jl_interrupt_exception = jl_new_struct_uninit((jl_datatype_t*)core("InterruptException")); diff --git a/src/julia.h b/src/julia.h index c1225481aa0ca..b099cf7d68599 100644 --- a/src/julia.h +++ b/src/julia.h @@ -529,7 +529,6 @@ extern JL_DLLEXPORT jl_value_t *jl_stackovf_exception; extern JL_DLLEXPORT jl_value_t *jl_memory_exception; extern JL_DLLEXPORT jl_value_t *jl_readonlymemory_exception; extern JL_DLLEXPORT jl_value_t *jl_diverror_exception; -extern JL_DLLEXPORT jl_value_t *jl_overflow_exception; extern JL_DLLEXPORT jl_value_t *jl_undefref_exception; extern JL_DLLEXPORT jl_value_t *jl_interrupt_exception; extern JL_DLLEXPORT jl_datatype_t *jl_boundserror_type;