Skip to content

Commit

Permalink
Make DomainError more informative
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed Jul 11, 2017
1 parent 090e549 commit a1405a9
Show file tree
Hide file tree
Showing 22 changed files with 133 additions and 94 deletions.
7 changes: 6 additions & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions base/combinatorics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion base/dSFMT.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 6 additions & 6 deletions base/dates/rounding.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -56,22 +56,22 @@ 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

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
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
7 changes: 4 additions & 3 deletions base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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(x<y, -1, ifelse(x>y, 1, 0))
end

function cmp(x::Real, y::AbstractFloat)
isnan(y) && throw(DomainError())
isnan(y) && throw(DomainError(y, "`y` cannot be NaN."))
ifelse(x<y, -1, ifelse(x>y, 1, 0))
end

function cmp(x::AbstractFloat, y::Real)
isnan(x) && throw(DomainError())
isnan(x) && throw(DomainError(x, "`x` cannot be NaN."))
ifelse(x<y, -1, ifelse(x>y, 1, 0))
end

Expand Down
2 changes: 1 addition & 1 deletion base/floatfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -474,15 +474,15 @@ 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)

^(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)
Expand Down Expand Up @@ -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)
Expand Down
23 changes: 15 additions & 8 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions base/libuv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 14 additions & 4 deletions base/linalg/eigen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit a1405a9

Please sign in to comment.