From a1405a9a36a688ed868b36c48e1d81f85b80f8ff Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 11 Jul 2017 04:37:58 -0500 Subject: [PATCH] Make DomainError more informative --- base/boot.jl | 7 ++++++- base/combinatorics.jl | 4 ++-- base/dSFMT.jl | 2 +- base/dates/rounding.jl | 12 ++++++------ base/deprecated.jl | 6 ++++++ base/docs/helpdb/Base.jl | 7 ++++--- base/float.jl | 7 ++++--- base/floatfuncs.jl | 2 +- base/gmp.jl | 8 ++++---- base/intfuncs.jl | 23 +++++++++++++++-------- base/libuv.jl | 4 ++-- base/linalg/eigen.jl | 18 ++++++++++++++---- base/math.jl | 40 ++++++++++++++++++++++++++++++++-------- base/mpfr.jl | 25 ++++++++++++++----------- base/random.jl | 6 ++++-- base/rational.jl | 3 ++- base/replutil.jl | 27 +++------------------------ base/special/gamma.jl | 2 +- base/special/log.jl | 8 ++++---- base/special/trig.jl | 12 ++++++------ base/strings/util.jl | 2 +- test/replutil.jl | 2 +- 22 files changed, 133 insertions(+), 94 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 5ba20e39a67041..e972c7828feb93 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -205,7 +205,6 @@ struct BoundsError <: Exception BoundsError(a::ANY, i) = (@_noinline_meta; new(a,i)) end struct DivideError <: Exception end -struct DomainError <: Exception end struct OverflowError <: Exception end struct OutOfMemoryError <: Exception end struct ReadOnlyMemoryError<: Exception end @@ -216,6 +215,12 @@ struct UndefVarError <: Exception var::Symbol end struct InterruptException <: Exception end +struct DomainError <: Exception + val + msg + DomainError(val) = (@_noinline_meta; new(val)) + DomainError(val, msg) = (@_noinline_meta; new(val, msg)) +end mutable struct TypeError <: Exception func::Symbol context::AbstractString diff --git a/base/combinatorics.jl b/base/combinatorics.jl index 137fb439c6ec64..d0993cd0bc11e3 100644 --- a/base/combinatorics.jl +++ b/base/combinatorics.jl @@ -15,7 +15,7 @@ for n in 2:34 end function factorial_lookup(n::Integer, table, lim) - n < 0 && throw(DomainError()) + n < 0 && throw(DomainError(n, "`n` must not be negative.")) n > lim && throw(OverflowError()) n == 0 && return one(n) @inbounds f = table[n] @@ -34,7 +34,7 @@ else end function gamma(n::Union{Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64}) - n < 0 && throw(DomainError()) + n < 0 && throw(DomainError(n, "`n` must not be negative.")) n == 0 && return Inf n <= 2 && return 1.0 n > 20 && return gamma(Float64(n)) diff --git a/base/dSFMT.jl b/base/dSFMT.jl index bd22f22a2fa39b..8bc18f3bf68d79 100644 --- a/base/dSFMT.jl +++ b/base/dSFMT.jl @@ -25,7 +25,7 @@ mutable struct DSFMT_state val::Vector{Int32} DSFMT_state(val::Vector{Int32} = zeros(Int32, JN32)) = - new(length(val) == JN32 ? val : throw(DomainError())) + new(length(val) == JN32 ? val : throw(DomainError(length(val), string("Expected length ", JN32, '.')))) end copy!(dst::DSFMT_state, src::DSFMT_state) = (copy!(dst.val, src.val); dst) diff --git a/base/dates/rounding.jl b/base/dates/rounding.jl index 8adbf80902b57b..6a4a40d38ae13a 100644 --- a/base/dates/rounding.jl +++ b/base/dates/rounding.jl @@ -40,13 +40,13 @@ Takes the given `DateTime` and returns the number of milliseconds since the roun datetime2epochms(dt::DateTime) = value(dt) - DATETIMEEPOCH function Base.floor(dt::Date, p::Year) - value(p) < 1 && throw(DomainError()) + value(p) < 1 && throw(DomainError(p)) years = year(dt) return Date(years - mod(years, value(p))) end function Base.floor(dt::Date, p::Month) - value(p) < 1 && throw(DomainError()) + value(p) < 1 && throw(DomainError(p)) y, m = yearmonth(dt) months_since_epoch = y * 12 + m - 1 month_offset = months_since_epoch - mod(months_since_epoch, value(p)) @@ -56,14 +56,14 @@ function Base.floor(dt::Date, p::Month) end function Base.floor(dt::Date, p::Week) - value(p) < 1 && throw(DomainError()) + value(p) < 1 && throw(DomainError(p)) days = value(dt) - WEEKEPOCH days = days - mod(days, value(Day(p))) return Date(UTD(WEEKEPOCH + Int64(days))) end function Base.floor(dt::Date, p::Day) - value(p) < 1 && throw(DomainError()) + value(p) < 1 && throw(DomainError(p)) days = date2epochdays(dt) return epochdays2date(days - mod(days, value(p))) end @@ -71,7 +71,7 @@ end Base.floor(dt::DateTime, p::DatePeriod) = DateTime(Base.floor(Date(dt), p)) function Base.floor(dt::DateTime, p::TimePeriod) - value(p) < 1 && throw(DomainError()) + value(p) < 1 && throw(DomainError(p)) milliseconds = datetime2epochms(dt) return epochms2datetime(milliseconds - mod(milliseconds, value(Millisecond(p)))) end @@ -166,7 +166,7 @@ Base.round(dt::TimeType, p::Period, r::RoundingMode{:Up}) = Base.ceil(dt, p) # No implementation of other `RoundingMode`s: rounding to nearest "even" is skipped because # "even" is not defined for Period; rounding toward/away from zero is skipped because ISO # 8601's year 0000 is not really "zero". -Base.round(::TimeType, ::Period, ::RoundingMode) = throw(DomainError()) +Base.round(::TimeType, p::Period, ::RoundingMode) = throw(DomainError(p)) # Default to RoundNearestTiesUp. Base.round(dt::TimeType, p::Period) = Base.round(dt, p, RoundNearestTiesUp) diff --git a/base/deprecated.jl b/base/deprecated.jl index a619da84ad349b..8dd8da509c495d 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1561,6 +1561,12 @@ function InexactError() InexactError(:none, Any, nothing) end +# PR # ??? +function DomainError() + depwarn("DomainError now supports arguments, use `DomainError(value)` or `DomainError(value, msg)` instead.", :DomainError) + DomainError(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 a24f9a75839850..f534effd26f9b2 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -1402,14 +1402,15 @@ The highest value representable by the given (real) numeric `DataType`. typemax """ - DomainError() + DomainError(val) + DomainError(val, msg) -The arguments to a function or constructor are outside the valid domain. +The argument `val` to a function or constructor is outside the valid domain. # Examples ```jldoctest julia> sqrt(-1) -ERROR: DomainError: +ERROR: DomainError with -1: sqrt will only return a complex result if called with a complex argument. Try sqrt(complex(x)). Stacktrace: [1] sqrt(::Int64) at ./math.jl:443 diff --git a/base/float.jl b/base/float.jl index 0b7306d64fc16c..40242fab6b1121 100644 --- a/base/float.jl +++ b/base/float.jl @@ -443,17 +443,18 @@ for op in (:<, :<=, :isless) end function cmp(x::AbstractFloat, y::AbstractFloat) - (isnan(x) || isnan(y)) && throw(DomainError()) + isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) + isnan(y) && throw(DomainError(y, "`y` cannot be NaN.")) ifelse(xy, 1, 0)) end function cmp(x::Real, y::AbstractFloat) - isnan(y) && throw(DomainError()) + isnan(y) && throw(DomainError(y, "`y` cannot be NaN.")) ifelse(xy, 1, 0)) end function cmp(x::AbstractFloat, y::Real) - isnan(x) && throw(DomainError()) + isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) ifelse(xy, 1, 0)) end diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index a5973c7d99986a..340329c545806e 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -132,7 +132,7 @@ function _signif_og(x, digits, base) end function signif(x::Real, digits::Integer, base::Integer=10) - digits < 1 && throw(DomainError()) + digits < 1 && throw(DomainError(digits, "`digits` cannot be less than 1.")) x = float(x) (x == 0 || !isfinite(x)) && return x diff --git a/base/gmp.jl b/base/gmp.jl index a15cc43d03fa4d..566e8b70ec3542 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -403,7 +403,7 @@ function invmod(x::BigInt, y::BigInt) return z end if (y==0 || MPZ.invert!(z, x, ya) == 0) - throw(DomainError()) + throw(DomainError(y)) end # GMP always returns a positive inverse; we instead want to # normalize such that div(z, y) == 0, i.e. we want a negative z @@ -474,7 +474,7 @@ cmp(x::BigInt, y::CulongMax) = MPZ.cmp_ui(x, y) cmp(x::BigInt, y::Integer) = cmp(x, big(y)) cmp(x::Integer, y::BigInt) = -cmp(y, x) -cmp(x::BigInt, y::CdoubleMax) = isnan(y) ? throw(DomainError()) : MPZ.cmp_d(x, y) +cmp(x::BigInt, y::CdoubleMax) = isnan(y) ? throw(DomainError(y, "`y` cannot be NaN.")) : MPZ.cmp_d(x, y) cmp(x::CdoubleMax, y::BigInt) = -cmp(y, x) isqrt(x::BigInt) = MPZ.sqrt(x) @@ -482,7 +482,7 @@ isqrt(x::BigInt) = MPZ.sqrt(x) ^(x::BigInt, y::Culong) = MPZ.pow_ui(x, y) function bigint_pow(x::BigInt, y::Integer) - if y<0; throw(DomainError()); end + if y<0; throw(DomainError(y, "`y` cannot be negative.")); end if x== 1; return x; end if x==-1; return isodd(y) ? x : -x; end if y>typemax(Culong) @@ -607,7 +607,7 @@ function base(b::Integer, n::BigInt, pad::Integer) end function ndigits0zpb(x::BigInt, b::Integer) - b < 2 && throw(DomainError()) + b < 2 && throw(DomainError(b, "`b` cannot be less than 2.")) x.size == 0 && return 0 # for consistency with other ndigits0z methods if ispow2(b) && 2 <= b <= 62 # GMP assumes b is in this range MPZ.sizeinbase(x, b) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 0d0c2af9d5ed0a..28f915a9182a6a 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -147,7 +147,8 @@ julia> invmod(5,6) """ function invmod(n::T, m::T) where T<:Integer g, x, y = gcdx(n, m) - (g != 1 || m == 0) && throw(DomainError()) + g != 1 && throw(DomainError((n, m), "Greatest common divisor is 1.")) + m == 0 && throw(DomainError(m, "`m` must not be 0.")) # Note that m might be negative here. # For unsigned T, x might be close to typemax; add m to force a wrap-around. r = mod(x + m, m) @@ -159,6 +160,10 @@ invmod(n::Integer, m::Integer) = invmod(promote(n,m)...) # ^ for any x supporting * to_power_type(x::Number) = oftype(x*x, x) to_power_type(x) = x +@noinline throw_depbs(p) = throw(DomainError(p, + string("Cannot raise an integer x to a negative power ", p, '.', + "\nMake x a float by adding a zero decimal (e.g., 2.0^$p instead ", + "of 2^$p), or write 1/x^$(-p), float(x)^$p, or (x//1)^$p"))) function power_by_squaring(x_, p::Integer) x = to_power_type(x_) if p == 1 @@ -170,7 +175,7 @@ function power_by_squaring(x_, p::Integer) elseif p < 0 x == 1 && return copy(x) x == -1 && return iseven(p) ? one(x) : copy(x) - throw(DomainError()) + throw_depbs(p) end t = trailing_zeros(p) + 1 p >>= t @@ -190,7 +195,7 @@ function power_by_squaring(x_, p::Integer) end power_by_squaring(x::Bool, p::Unsigned) = ((p==0) | x) function power_by_squaring(x::Bool, p::Integer) - p < 0 && !x && throw(DomainError()) + p < 0 && !x && throw_depbs(p) return (p==0) | x end @@ -348,7 +353,8 @@ julia> nextpow(4, 16) See also [`prevpow`](@ref). """ function nextpow(a::Real, x::Real) - (a <= 1 || x <= 0) && throw(DomainError()) + a <= 1 && throw(DomainError(a, "`a` must be greater than 1.")) + x <= 0 && throw(DomainError(x, "`x` must be positive.")) x <= 1 && return one(a) n = ceil(Integer,log(a, x)) p = a^(n-1) @@ -379,7 +385,8 @@ julia> prevpow(4, 16) See also [`nextpow`](@ref). """ function prevpow(a::Real, x::Real) - (a <= 1 || x < 1) && throw(DomainError()) + a <= 1 && throw(DomainError(a, "`a` must be greater than 1.")) + x < 1 && throw(DomainError(x, "`x` must be ≥ 1.")) n = floor(Integer,log(a, x)) p = a^(n+1) p <= x ? p : a^n @@ -498,7 +505,7 @@ function ndigits0z(x::Integer, b::Integer) elseif b > 1 ndigits0zpb(x, b) else - throw(DomainError()) + throw(DomainError(b, "The base `b` must not be in `[-1, 0, 1]`.")) end end @@ -582,7 +589,7 @@ const base62digits = ['0':'9';'A':'Z';'a':'z'] function base(b::Int, x::Integer, pad::Int, neg::Bool) - (x >= 0) | (b < 0) || throw(DomainError()) + (x >= 0) | (b < 0) || throw(DomainError(x, "For negative `x`, `b` must be negative.")) 2 <= abs(b) <= 62 || throw(ArgumentError("base must satisfy 2 ≤ abs(base) ≤ 62, got $b")) digits = abs(b) <= 36 ? base36digits : base62digits i = neg + ndigits(x, b, pad) @@ -800,7 +807,7 @@ function isqrt(x::Union{Int64,UInt64,Int128,UInt128}) end function factorial(n::Integer) - n < 0 && throw(DomainError()) + n < 0 && throw(DomainError(n, "`n` must be nonnegative.")) local f::typeof(n*n), i::typeof(n*n) f = 1 for i = 2:n diff --git a/base/libuv.jl b/base/libuv.jl index 99bc52281c96f7..0a934484642f2f 100644 --- a/base/libuv.jl +++ b/base/libuv.jl @@ -7,14 +7,14 @@ include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "uv_constants.jl")) # convert UV handle data to julia object, checking for null function uv_sizeof_handle(handle) if !(UV_UNKNOWN_HANDLE < handle < UV_HANDLE_TYPE_MAX) - throw(DomainError()) + throw(DomainError(handle)) end ccall(:uv_handle_size,Csize_t,(Int32,),handle) end function uv_sizeof_req(req) if !(UV_UNKNOWN_REQ < req < UV_REQ_TYPE_MAX) - throw(DomainError()) + throw(DomainError(req)) end ccall(:uv_req_size,Csize_t,(Int32,),req) end diff --git a/base/linalg/eigen.jl b/base/linalg/eigen.jl index 98e4b940fc95a4..05d13632da77b2 100644 --- a/base/linalg/eigen.jl +++ b/base/linalg/eigen.jl @@ -226,7 +226,8 @@ julia> A = [0 im; -1 0] -1+0im 0+0im julia> eigmax(A) -ERROR: DomainError: +ERROR: DomainError with Complex{Int64}[0+0im 0+1im; -1+0im 0+0im]: +`A` cannot have complex eigenvalues. Stacktrace: [1] #eigmax#52(::Bool, ::Bool, ::Function, ::Array{Complex{Int64},2}) at ./linalg/eigen.jl:238 [2] eigmax(::Array{Complex{Int64},2}) at ./linalg/eigen.jl:236 @@ -235,7 +236,7 @@ Stacktrace: function eigmax(A::Union{Number, StridedMatrix}; permute::Bool=true, scale::Bool=true) v = eigvals(A, permute = permute, scale = scale) if eltype(v)<:Complex - throw(DomainError()) + throw_de_complex(A) end maximum(v) end @@ -268,7 +269,8 @@ julia> A = [0 im; -1 0] -1+0im 0+0im julia> eigmin(A) -ERROR: DomainError: +ERROR: DomainError with Complex{Int64}[0+0im 0+1im; -1+0im 0+0im]: +`A` cannot have complex eigenvalues. Stacktrace: [1] #eigmin#53(::Bool, ::Bool, ::Function, ::Array{Complex{Int64},2}) at ./linalg/eigen.jl:280 [2] eigmin(::Array{Complex{Int64},2}) at ./linalg/eigen.jl:278 @@ -277,7 +279,7 @@ Stacktrace: function eigmin(A::Union{Number, StridedMatrix}; permute::Bool=true, scale::Bool=true) v = eigvals(A, permute = permute, scale = scale) if eltype(v)<:Complex - throw(DomainError()) + throw_de_complex(A) end minimum(v) end @@ -436,6 +438,14 @@ julia> eigvecs(A, B) """ eigvecs(A::AbstractMatrix, B::AbstractMatrix) = eigvecs(eigfact(A, B)) +function throw_de_complex(A) + if length(A) < 10 + throw(DomainError(A, "`A` cannot have complex eigenvalues.")) + else + throw(DomainError(summary(A), "`A` cannot have complex eigenvalues.")) + end +end + # Conversion methods ## Can we determine the source/result is Real? This is not stored in the type Eigen diff --git a/base/math.jl b/base/math.jl index 485d24884f1807..6d22fd6bce48b8 100644 --- a/base/math.jl +++ b/base/math.jl @@ -27,6 +27,16 @@ using Core.Intrinsics: sqrt_llvm const IEEEFloat = Union{Float16, Float32, Float64} +@noinline function throw_complex_domainerror(x, f) + throw(DomainError(x, string("$f will only return a complex result if called with a ", + "complex argument. Try $f(complex(x))."))) +end +@noinline function throw_exp_domainerror(x) + throw(DomainError(x, string("Exponentiation yielding a complex result requires a ", + "complex argument.\nReplace x^y with (x+0im)^y, ", + "Complex(x)^y, or similar."))) +end + for T in (Float16, Float32, Float64) @eval significand_bits(::Type{$T}) = $(trailing_ones(significand_mask(T))) @eval exponent_bits(::Type{$T}) = $(sizeof(T)*8 - significand_bits(T) - 1) @@ -293,7 +303,7 @@ end # utility for converting NaN return to DomainError # the branch in nan_dom_err prevents its callers from inlining, so be sure to force it # until the heuristics can be improved -@inline nan_dom_err(f, x) = isnan(f) & !isnan(x) ? throw(DomainError()) : f +@inline nan_dom_err(f, x) = isnan(f) & !isnan(x) ? throw(DomainError(x, "NaN result for non-NaN input.")) : f # functions that return NaN on non-NaN argument for domain error """ @@ -426,13 +436,13 @@ Compute sine and cosine of `x`, where `x` is in radians. @inline function sincos(x) res = Base.FastMath.sincos_fast(x) if (isnan(res[1]) | isnan(res[2])) & !isnan(x) - throw(DomainError()) + throw(DomainError(x, "NaN result for non-NaN input.")) end return res end @inline function sqrt(x::Union{Float32,Float64}) - x < zero(x) && throw(DomainError()) + x < zero(x) && throw_complex_domainerror(x, :sqrt) sqrt_llvm(x) end @@ -457,7 +467,7 @@ julia> hypot(a, a) 1.4142135623730951e10 julia> √(a^2 + a^2) # a^2 overflows -ERROR: DomainError: +ERROR: DomainError with -2914184810805067776: sqrt will only return a complex result if called with a complex argument. Try sqrt(complex(x)). Stacktrace: [1] sqrt(::Int64) at ./math.jl:447 @@ -581,11 +591,13 @@ ldexp(x::Float16, q::Integer) = Float16(ldexp(Float32(x), q)) Get the exponent of a normalized floating-point number. """ function exponent(x::T) where T<:IEEEFloat + @noinline throw1(x) = throw(DomainError(x, "Cannot be NaN or Inf.")) + @noinline throw2(x) = throw(DomainError(x, "Cannot be subnormal converted to 0.")) xs = reinterpret(Unsigned, x) & ~sign_mask(T) - xs >= exponent_mask(T) && return throw(DomainError()) # NaN or Inf + xs >= exponent_mask(T) && throw1(x) k = Int(xs >> significand_bits(T)) if k == 0 # x is subnormal - xs == 0 && throw(DomainError()) + xs == 0 && throw2(x) m = leading_zeros(xs) - exponent_bits(T) k = 1 - m end @@ -707,8 +719,20 @@ function modf(x::Float64) f, _modf_temp[] end -@inline ^(x::Float64, y::Float64) = nan_dom_err(ccall("llvm.pow.f64", llvmcall, Float64, (Float64, Float64), x, y), x + y) -@inline ^(x::Float32, y::Float32) = nan_dom_err(ccall("llvm.pow.f32", llvmcall, Float32, (Float32, Float32), x, y), x + y) +@inline function ^(x::Float64, y::Float64) + z = ccall("llvm.pow.f64", llvmcall, Float64, (Float64, Float64), x, y) + if isnan(z) & !isnan(x+y) + throw_exp_domainerror(x) + end + z +end +@inline function ^(x::Float32, y::Float32) + z = ccall("llvm.pow.f32", llvmcall, Float32, (Float32, Float32), x, y) + if isnan(z) & !isnan(x+y) + throw_exp_domainerror(x) + end + z +end @inline ^(x::Float64, y::Integer) = x ^ Float64(y) @inline ^(x::Float32, y::Integer) = x ^ Float32(y) @inline ^(x::Float16, y::Integer) = Float16(Float32(x) ^ Float32(y)) diff --git a/base/mpfr.jl b/base/mpfr.jl index 0da255045918b2..8adeafd776ae56 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -485,7 +485,7 @@ function sqrt(x::BigFloat) z = BigFloat() ccall((:mpfr_sqrt, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) if isnan(z) - throw(DomainError()) + throw(DomainError(x, "NaN result for non-NaN input.")) end return z end @@ -560,7 +560,7 @@ ldexp(x::BigFloat, n::Integer) = x*exp2(BigFloat(n)) function factorial(x::BigFloat) if x < 0 || !isinteger(x) - throw(DomainError()) + throw(DomainError(x, "Must be a non-negative integer.")) end ui = convert(Culong, x) z = BigFloat() @@ -577,7 +577,8 @@ end for f in (:log, :log2, :log10) @eval function $f(x::BigFloat) if x < 0 - throw(DomainError()) + throw(DomainError(x, string($f, " will only return a complex result if called ", + "with a complex argument. Try ", $f, "(complex(x))."))) end z = BigFloat() ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) @@ -587,7 +588,8 @@ end function log1p(x::BigFloat) if x < -1 - throw(DomainError()) + throw(DomainError(x, string("log1p will only return a complex result if called ", + "with a complex argument. Try log1p(complex(x))."))) end z = BigFloat() ccall((:mpfr_log1p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) @@ -656,7 +658,7 @@ for f in (:sin,:cos,:tan,:sec,:csc, z = BigFloat() ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) if isnan(z) - throw(DomainError()) + throw(DomainError(x, "NaN result for non-NaN input.")) end return z end @@ -687,22 +689,23 @@ end >(x::BigFloat, y::BigFloat) = ccall((:mpfr_greater_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 function cmp(x::BigFloat, y::BigInt) - isnan(x) && throw(DomainError()) + isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) ccall((:mpfr_cmp_z, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigInt}), &x, &y) end function cmp(x::BigFloat, y::ClongMax) - isnan(x) && throw(DomainError()) + isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) ccall((:mpfr_cmp_si, :libmpfr), Int32, (Ptr{BigFloat}, Clong), &x, y) end function cmp(x::BigFloat, y::CulongMax) - isnan(x) && throw(DomainError()) + isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) ccall((:mpfr_cmp_ui, :libmpfr), Int32, (Ptr{BigFloat}, Culong), &x, y) end cmp(x::BigFloat, y::Integer) = cmp(x,big(y)) cmp(x::Integer, y::BigFloat) = -cmp(y,x) function cmp(x::BigFloat, y::CdoubleMax) - (isnan(x) || isnan(y)) && throw(DomainError()) + isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) + isnan(y) && throw(DomainError(y, "`y` cannot be NaN.")) ccall((:mpfr_cmp_d, :libmpfr), Int32, (Ptr{BigFloat}, Cdouble), &x, y) end cmp(x::CdoubleMax, y::BigFloat) = -cmp(y,x) @@ -742,7 +745,7 @@ Set the precision (in bits) to be used for `T` arithmetic. """ function setprecision(::Type{BigFloat}, precision::Int) if precision < 2 - throw(DomainError()) + throw(DomainError(precision, "`precision` cannot be less than 2.")) end DEFAULT_PRECISION[end] = precision end @@ -789,7 +792,7 @@ end function exponent(x::BigFloat) if x == 0 || !isfinite(x) - throw(DomainError()) + throw(DomainError(x, "`x` must be non-zero and finite.")) end # The '- 1' is to make it work as Base.exponent return ccall((:mpfr_get_exp, :libmpfr), Clong, (Ptr{BigFloat},), &x) - 1 diff --git a/base/random.jl b/base/random.jl index 3f23af00d977fd..b41ee77c0735c4 100644 --- a/base/random.jl +++ b/base/random.jl @@ -82,7 +82,9 @@ mutable struct MersenneTwister <: AbstractRNG idx::Int function MersenneTwister(seed, state, vals, idx) - length(vals) == MTCacheLength && 0 <= idx <= MTCacheLength || throw(DomainError()) + if !(length(vals) == MTCacheLength && 0 <= idx <= MTCacheLength) + throw(DomainError(idx, "`length(vals)` and `idx` must be consistent with $MTCacheLength")) + end new(seed, state, vals, idx) end end @@ -224,7 +226,7 @@ function make_seed() end function make_seed(n::Integer) - n < 0 && throw(DomainError()) + n < 0 && throw(DomainError(n, "`n` must be non-negative.")) seed = UInt32[] while true push!(seed, n & 0xffffffff) diff --git a/base/rational.jl b/base/rational.jl index b6af61273dfcec..303e07f6bf55fa 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -290,7 +290,8 @@ for rel in (:<,:<=,:cmp) for (Tx,Ty) in ((Rational,AbstractFloat), (AbstractFloat,Rational)) @eval function ($rel)(x::$Tx, y::$Ty) if isnan(x) || isnan(y) - $(rel == :cmp ? :(throw(DomainError())) : :(return false)) + $(rel == :cmp ? :(throw(DomainError((x,y), "Inputs cannot be NaN."))) : + :(return false)) end xn, xp, xd = decompose(x) diff --git a/base/replutil.jl b/base/replutil.jl index 5a07e8a45d1a4b..feb9d13e0ec973 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -232,31 +232,10 @@ end showerror(io::IO, ex::InitError) = showerror(io, ex, []) function showerror(io::IO, ex::DomainError, bt; backtrace=true) - print(io, "DomainError:") - for b in bt - for code in StackTraces.lookup(b) - if code.from_c - continue - elseif code.func === :nan_dom_err - continue - elseif code.func in (:log, :log2, :log10, :sqrt) - print(io, "\n$(code.func) will only return a complex result if called ", - "with a complex argument. Try $(string(code.func))(complex(x)).") - elseif (code.func === :^ && - (code.file === Symbol("intfuncs.jl") || code.file === Symbol(joinpath(".", "intfuncs.jl")))) || - code.func === :power_by_squaring #3024 - print(io, "\nCannot raise an integer x to a negative power -n. ", - "\nMake x a float by adding a zero decimal (e.g. 2.0^-n instead ", - "of 2^-n), or write 1/x^n, float(x)^-n, or (x//1)^-n.") - elseif code.func === :^ && - (code.file === Symbol("math.jl") || code.file === Symbol(joinpath(".", "math.jl"))) - print(io, "\nExponentiation yielding a complex result requires a complex ", - "argument.\nReplace x^y with (x+0im)^y, Complex(x)^y, or similar.") - end - @goto showbacktrace - end + print(io, "DomainError with $(ex.val):") + if isdefined(ex, :msg) + print(io, '\n', ex.msg) end - @label showbacktrace backtrace && show_backtrace(io, bt) nothing end diff --git a/base/special/gamma.jl b/base/special/gamma.jl index 7415200b409ce4..b6b4539fb46fc3 100644 --- a/base/special/gamma.jl +++ b/base/special/gamma.jl @@ -31,7 +31,7 @@ Compute the logarithmic factorial of a nonnegative integer `x`. Equivalent to [`lgamma`](@ref) of `x + 1`, but `lgamma` extends this function to non-integer `x`. """ -lfact(x::Integer) = x < 0 ? throw(DomainError()) : lgamma(x + oneunit(x)) +lfact(x::Integer) = x < 0 ? throw(DomainError(x, "`x` must be non-negative.")) : lgamma(x + oneunit(x)) """ lgamma(x) diff --git a/base/special/log.jl b/base/special/log.jl index d317101794155c..e62ddf541705e4 100644 --- a/base/special/log.jl +++ b/base/special/log.jl @@ -282,7 +282,7 @@ function log(x::Float64) elseif isnan(x) NaN else - throw(DomainError()) + throw_complex_domainerror(x, :log) end end @@ -318,7 +318,7 @@ function log(x::Float32) elseif isnan(x) NaN32 else - throw(DomainError()) + throw_complex_domainerror(x, :log) end end @@ -353,7 +353,7 @@ function log1p(x::Float64) elseif isnan(x) NaN else - throw(DomainError()) + throw_complex_domainerror(x, :log1p) end end @@ -386,7 +386,7 @@ function log1p(x::Float32) elseif isnan(x) NaN32 else - throw(DomainError()) + throw_complex_domainerror(x, :log1p) end end diff --git a/base/special/trig.jl b/base/special/trig.jl index e563edce0955f5..73d94cc0d669bb 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -106,7 +106,7 @@ Compute ``\\sin(\\pi x)`` more accurately than `sin(pi*x)`, especially for large function sinpi(x::T) where T<:AbstractFloat if !isfinite(x) isnan(x) && return x - throw(DomainError()) + throw(DomainError(x, "`x` cannot be infinite.")) end ax = abs(x) @@ -136,7 +136,7 @@ end function sinpi(x::T) where T<:Union{Integer,Rational} Tf = float(T) if !isfinite(x) - throw(DomainError()) + throw(DomainError(x, "`x` must be finite.")) end # until we get an IEEE remainder function (#9283) @@ -169,7 +169,7 @@ Compute ``\\cos(\\pi x)`` more accurately than `cos(pi*x)`, especially for large function cospi(x::T) where T<:AbstractFloat if !isfinite(x) isnan(x) && return x - throw(DomainError()) + throw(DomainError(x, "`x` must be finite.")) end ax = abs(x) @@ -194,7 +194,7 @@ end # Integers and Rationals function cospi(x::T) where T<:Union{Integer,Rational} if !isfinite(x) - throw(DomainError()) + throw(DomainError(x, "`x` must be finite.")) end ax = abs(x) @@ -371,7 +371,7 @@ deg2rad_ext(x::Real) = deg2rad(x) # Fallback function sind(x::Real) if isinf(x) - return throw(DomainError()) + return throw(DomainError(x, "`x` cannot be infinite.")) elseif isnan(x) return oftype(x,NaN) end @@ -402,7 +402,7 @@ end function cosd(x::Real) if isinf(x) - return throw(DomainError()) + return throw(DomainError(x, "`x` cannot be infinite.")) elseif isnan(x) return oftype(x,NaN) end diff --git a/base/strings/util.jl b/base/strings/util.jl index f7707c3fc88668..ccee670bdc8a37 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -362,7 +362,7 @@ _replace(io, repl::Function, str, r, pattern) = # TODO: rename to `replace` when `replace` is removed from deprecated.jl function replace_new(str::String, pattern, repl, count::Integer) count == 0 && return str - count < 0 && throw(DomainError()) + count < 0 && throw(DomainError(count, "`count` must be non-negative.")) n = 1 e = endof(str) i = a = start(str) diff --git a/test/replutil.jl b/test/replutil.jl index d8c63b3b276bc6..142d5c103d1810 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -256,7 +256,7 @@ let undefvar err_str = @except_strbt sqrt(-1) DomainError @test contains(err_str, "Try sqrt(complex(x)).") err_str = @except_strbt 2^(-1) DomainError - @test contains(err_str, "Cannot raise an integer x to a negative power -n") + @test contains(err_str, "Cannot raise an integer x to a negative power -1") err_str = @except_strbt (-1)^0.25 DomainError @test contains(err_str, "Exponentiation yielding a complex result requires a complex argument")