Skip to content

Commit

Permalink
Make InexactError more informative
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy committed Jan 13, 2017
1 parent 7b7b4e1 commit 0c0782c
Show file tree
Hide file tree
Showing 20 changed files with 102 additions and 45 deletions.
2 changes: 1 addition & 1 deletion base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ end
# 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!{T<:Real}(Bc::Vector{UInt64}, pos_d::Int, C::Array{T}, pos_s::Int, numbits::Int)
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{T<:Number}(::Type{Bool}, ::Type{T}) = T
Expand Down
7 changes: 6 additions & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,17 @@ end
immutable DivideError <: Exception end
immutable DomainError <: Exception end
immutable OverflowError <: Exception end
immutable InexactError <: Exception end
immutable OutOfMemoryError <: Exception end
immutable ReadOnlyMemoryError<: Exception end
immutable SegmentationFault <: Exception end
immutable StackOverflowError <: Exception end
immutable UndefRefError <: Exception end
immutable InexactError <: Exception
f::Any
T::Type
val::Any
InexactError(f::ANY, T::ANY, val) = (@_noinline_meta; new(f, T, val))
end
immutable UndefVarError <: Exception
var::Symbol
end
Expand Down
2 changes: 1 addition & 1 deletion base/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ typealias Complex32 Complex{Float16}
convert{T<:Real}(::Type{Complex{T}}, x::Real) = Complex{T}(x,0)
convert{T<:Real}(::Type{Complex{T}}, z::Complex) = Complex{T}(real(z),imag(z))
convert{T<:Real}(::Type{T}, z::Complex) =
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
6 changes: 3 additions & 3 deletions base/dates/periods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ typealias FixedPeriod Union{Week,Day,Hour,Minute,Second,Millisecond}
# 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 @@ -402,7 +402,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 @@ -422,7 +422,7 @@ Base.isless{T<:FixedPeriod,S<:FixedPeriod}(x::T,y::S) = isless(promote(x,y)...)
typealias 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
5 changes: 5 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1638,4 +1638,9 @@ iteratoreltype(::Type{Task}) = EltypeUnknown()

isempty(::Task) = error("isempty not defined for Tasks")

function InexactError()
depwarn("InexactError() is deprecated, supply the calling function, type, and value (see help)", :InexactError)
InexactError(nothing, Any, nothing)
end

# End deprecations scheduled for 0.6
4 changes: 2 additions & 2 deletions base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1405,9 +1405,9 @@ Convert a hexadecimal string to the floating point number it represents.
hex2num

"""
InexactError()
InexactError(f, T, x)
Type conversion cannot be done exactly.
Conversion of `x` to type `T` in function `f` cannot be done exactly.
"""
InexactError

Expand Down
8 changes: 4 additions & 4 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -643,14 +643,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 @@ -664,14 +664,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 @@ -128,12 +128,12 @@ function unsafe_trunc(::Type{BigInt}, x::Union{Float32,Float64})
end

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 @@ -182,7 +182,7 @@ function convert{T<:Unsigned}(::Type{T}, x::BigInt)
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 @@ -193,9 +193,9 @@ function convert{T<:Signed}(::Type{T}, x::BigInt)
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
(x.size > 0) (y > 0) && throw(InexactError()) # catch overflow
(x.size > 0) (y > 0) && throw(InexactError(convert, T, x)) # catch overflow
y
end
end
Expand Down
14 changes: 7 additions & 7 deletions base/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,20 @@ unsafe_cast{T<:Integer}(::Type{T}, x::BigFloat, r::RoundingMode) = unsafe_cast(T
unsafe_trunc{T<:Integer}(::Type{T}, x::BigFloat) = unsafe_cast(T,x,RoundToZero)

function trunc{T<:Union{Signed,Unsigned}}(::Type{T}, x::BigFloat)
(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{T<:Union{Signed,Unsigned}}(::Type{T}, x::BigFloat)
(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{T<:Union{Signed,Unsigned}}(::Type{T}, x::BigFloat)
(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{T<:Union{Signed,Unsigned}}(::Type{T}, x::BigFloat)
(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 @@ -219,14 +219,14 @@ 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{T<:Integer}(::Type{T},x::BigFloat)
isinteger(x) || throw(InexactError())
isinteger(x) || throw(InexactError(convert, T, x))
trunc(T,x)
end

Expand Down
6 changes: 3 additions & 3 deletions base/rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ convert{T<:Integer}(::Type{Rational{T}}, x::Integer) = Rational{T}(convert(T,x),
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{T<:Integer}(::Type{T}, x::Rational) = (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{T<:Integer}(::Type{T}, x::Rational) = (isinteger(x) ? convert(T, x.num) : throw(InexactError(convert, T, x)))

convert(::Type{AbstractFloat}, x::Rational) = float(x.num)/float(x.den)
function convert{T<:AbstractFloat,S}(::Type{T}, x::Rational{S})
Expand All @@ -78,7 +78,7 @@ end

function convert{T<:Integer}(::Type{Rational{T}}, x::AbstractFloat)
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 @@ -192,6 +192,10 @@ function showerror(io::IO, ex::TypeError)
end
end

function showerror(io::IO, ex::InexactError)
print(io, "InexactError: ", ex.f, '(', ex.T, ", ", ex.val, "::", typeof(ex.val), ')')
end

function showerror(io::IO, ex, bt; backtrace=true)
try
with_output_color(have_color ? error_color() : :nothing, io) do io
Expand Down
2 changes: 1 addition & 1 deletion src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ jl_value_t *jl_segv_exception;
JL_DLLEXPORT jl_value_t *jl_diverror_exception;
JL_DLLEXPORT jl_value_t *jl_domain_exception;
JL_DLLEXPORT jl_value_t *jl_overflow_exception;
JL_DLLEXPORT jl_value_t *jl_inexact_exception;
JL_DLLEXPORT jl_value_t *jl_undefref_exception;
jl_datatype_t *jl_inexacterror_type;
jl_value_t *jl_interrupt_exception;
jl_datatype_t *jl_boundserror_type;
jl_value_t *jl_memory_exception;
Expand Down
6 changes: 6 additions & 0 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ JL_DLLEXPORT void JL_NORETURN jl_undefined_var_error(jl_sym_t *var)
jl_throw(jl_new_struct(jl_undefvarerror_type, var));
}

JL_DLLEXPORT void JL_NORETURN jl_inexact_error(jl_value_t *f, jl_value_t *ty, jl_value_t *x)
{
JL_GC_PUSH3(&f, &ty, &x); // root arguments so the caller doesn't need to
jl_throw(jl_new_struct(jl_inexacterror_type, f, ty, x));
}

JL_DLLEXPORT void JL_NORETURN jl_bounds_error(jl_value_t *v, jl_value_t *t)
{
JL_GC_PUSH2(&v, &t); // root arguments so the caller doesn't need to
Expand Down
25 changes: 25 additions & 0 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,31 @@ static Value *emit_bounds_check(const jl_cgval_t &ainfo, jl_value_t *ty, Value *
return im1;
}

static void emit_inexacterror_unless(Value *cond, jl_value_t *type, const jl_cgval_t &x, jl_codectx_t *ctx)
{
BasicBlock *failBB = BasicBlock::Create(jl_LLVMContext,"fail",ctx->f);
BasicBlock *passBB = BasicBlock::Create(jl_LLVMContext,"pass");
builder.CreateCondBr(cond, passBB, failBB);
builder.SetInsertPoint(failBB);
Value *fname_val = stringConstPtr(ctx->funcName);
#if JL_LLVM_VERSION >= 30700
builder.CreateCall(prepare_call(jlinexacterror_func),
{ fname_val, literal_pointer_val(type),
boxed(x, ctx, false)});
#else
builder.CreateCall3(prepare_call(jlinexacterror_func),
fname_val, literal_pointer_val(type),
boxed(x, ctx, false));
#endif
return;
}

static void emit_inexacterror_if(Value *cond, jl_value_t *type, const jl_cgval_t &x, jl_codectx_t *ctx)
{
emit_inexacterror_unless(builder.CreateXor(cond, ConstantInt::get(T_int1,-1)),
type, x, ctx);
}

// --- loading and storing ---

// If given alignment is 0 and LLVM's assumed alignment for a load/store via ptr
Expand Down
13 changes: 12 additions & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ static Function *jlerror_func;
static Function *jltypeerror_func;
static Function *jlundefvarerror_func;
static Function *jlboundserror_func;
static Function *jlinexacterror_func;
static Function *jluboundserror_func;
static Function *jlvboundserror_func;
static Function *jlboundserrorv_func;
Expand Down Expand Up @@ -5529,7 +5530,6 @@ static void init_julia_llvm_env(Module *m)
global_jlvalue_to_llvm("jl_undefref_exception", &jl_undefref_exception, m);
global_jlvalue_to_llvm("jl_domain_exception", &jl_domain_exception, m);
global_jlvalue_to_llvm("jl_overflow_exception", &jl_overflow_exception, m);
global_jlvalue_to_llvm("jl_inexact_exception", &jl_inexact_exception, m);

jlRTLD_DEFAULT_var =
new GlobalVariable(*m, T_pint8,
Expand Down Expand Up @@ -5610,6 +5610,17 @@ static void init_julia_llvm_env(Module *m)
jlundefvarerror_func->setDoesNotReturn();
add_named_global(jlundefvarerror_func, &jl_undefined_var_error);

std::vector<Type*> args3_inexacterror(0);
args3_inexacterror.push_back(T_pjlvalue);
args3_inexacterror.push_back(T_psize);
args3_inexacterror.push_back(T_size);
jlinexacterror_func =
Function::Create(FunctionType::get(T_void, args3_inexacterror, false),
Function::ExternalLinkage,
"jl_inexact_error", m);
jlundefvarerror_func->setDoesNotReturn();
add_named_global(jlinexacterror_func, &jl_inexact_error);

std::vector<Type*> args2_boundserrorv(0);
args2_boundserrorv.push_back(T_pjlvalue);
args2_boundserrorv.push_back(T_psize);
Expand Down
2 changes: 1 addition & 1 deletion src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ void jl_get_builtin_hooks(void)
jl_diverror_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideError"));
jl_domain_exception = jl_new_struct_uninit((jl_datatype_t*)core("DomainError"));
jl_overflow_exception = jl_new_struct_uninit((jl_datatype_t*)core("OverflowError"));
jl_inexact_exception = jl_new_struct_uninit((jl_datatype_t*)core("InexactError"));
jl_inexacterror_type = (jl_datatype_t*)core("InexactError");
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"));
Expand Down
20 changes: 10 additions & 10 deletions src/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,8 +625,8 @@ static jl_cgval_t generic_trunc(jl_value_t *targ, jl_value_t *x, jl_codectx_t *c
if (check) {
Value *back = signd ? builder.CreateSExt(ans, ix->getType()) :
builder.CreateZExt(ans, ix->getType());
raise_exception_unless(builder.CreateICmpEQ(back, ix),
literal_pointer_val(jl_inexact_exception), ctx);
emit_inexacterror_unless(builder.CreateICmpEQ(back, ix),
targ, emit_expr(x, ctx), ctx);
}
return mark_julia_type(ans, false, jlto, ctx);
}
Expand Down Expand Up @@ -840,7 +840,7 @@ struct math_builder {
};

static Value *emit_untyped_intrinsic(intrinsic f, Value *x, Value *y, Value *z, size_t nargs,
jl_codectx_t *ctx, jl_datatype_t **newtyp, jl_value_t* xtyp);
jl_codectx_t *ctx, jl_datatype_t **newtyp, jl_value_t* xtyp, const jl_cgval_t &xinfo);
static jl_cgval_t emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs,
jl_codectx_t *ctx)
{
Expand Down Expand Up @@ -1066,7 +1066,7 @@ static jl_cgval_t emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs,
if (f == not_int && xinfo.typ == (jl_value_t*)jl_bool_type)
r = builder.CreateXor(x, ConstantInt::get(T_int8, 1, true));
else
r = emit_untyped_intrinsic(f, x, y, z, nargs, ctx, (jl_datatype_t**)&newtyp, xinfo.typ);
r = emit_untyped_intrinsic(f, x, y, z, nargs, ctx, (jl_datatype_t**)&newtyp, xinfo.typ, xinfo);

if (!newtyp && r->getType() != x->getType())
// cast back to the exact original type (e.g. float vs. int) before remarking as a julia type
Expand All @@ -1081,7 +1081,7 @@ static jl_cgval_t emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs,
}

static Value *emit_untyped_intrinsic(intrinsic f, Value *x, Value *y, Value *z, size_t nargs,
jl_codectx_t *ctx, jl_datatype_t **newtyp, jl_value_t* xtyp)
jl_codectx_t *ctx, jl_datatype_t **newtyp, jl_value_t* xtyp, const jl_cgval_t &xinfo)
{
Type *t = x->getType();
Value *fy;
Expand Down Expand Up @@ -1242,11 +1242,11 @@ static Value *emit_untyped_intrinsic(intrinsic f, Value *x, Value *y, Value *z,
case check_top_bit:
// raise InexactError if argument's top bit is set
x = JL_INT(x);
raise_exception_if(builder.
CreateTrunc(builder.
CreateLShr(x, ConstantInt::get(t, t->getPrimitiveSizeInBits()-1)),
T_int1),
literal_pointer_val(jl_inexact_exception), ctx);
emit_inexacterror_if(builder.
CreateTrunc(builder.
CreateLShr(x, ConstantInt::get(t, t->getPrimitiveSizeInBits()-1)),
T_int1),
xtyp, xinfo, ctx);
return x;

case eq_int: *newtyp = jl_bool_type; return builder.CreateICmpEQ(JL_INT(x), JL_INT(y));
Expand Down
Loading

0 comments on commit 0c0782c

Please sign in to comment.