Skip to content

Ambiguity warnings on definition -> error on usage #16125

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

Merged
merged 5 commits into from
May 5, 2016
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
18 changes: 12 additions & 6 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,10 @@ Compiler/Runtime improvements
Breaking changes
----------------

* Local variables and arguments are represented in lowered code as numbered `Slot`
objects instead of as symbols ([#15609]).

* The information that used to be in the `ast` field of the `LambdaStaticData` type
is now divided among the fields `code`, `slotnames`, `slottypes`, `slotflags`,
`gensymtypes`, `rettype`, `nargs`, and `isva` in the `LambdaInfo` type ([#15609]).
* Method ambiguities no longer generate warnings when files are
loaded, nor do they dispatch to an arbitrarily-chosen method;
instead, a call that cannot be resolved to a single method results
in a `MethodError`. ([#6190])

* `pmap` keyword arguments `err_retry=true` and `err_stop=false` are deprecated.
`pmap` no longer retries or returns `Exception` objects in the result collection.
Expand All @@ -67,6 +65,13 @@ Breaking changes
If a reshaped copy is needed, use `copy(reshape(a))` or `copy!` to a new array of
the desired shape ([#4211]).

* Local variables and arguments are represented in lowered code as numbered `Slot`
objects instead of as symbols ([#15609]).

* The information that used to be in the `ast` field of the `LambdaStaticData` type
is now divided among the fields `code`, `slotnames`, `slottypes`, `slotflags`,
`gensymtypes`, `rettype`, `nargs`, and `isva` in the `LambdaInfo` type ([#15609]).

Library improvements
--------------------

Expand Down Expand Up @@ -206,3 +211,4 @@ Deprecated or removed
[#15550]: https://github.com/JuliaLang/julia/issues/15550
[#15609]: https://github.com/JuliaLang/julia/issues/15609
[#15763]: https://github.com/JuliaLang/julia/issues/15763
[#6190]: https://github.com/JuliaLang/julia/issues/6190
2 changes: 1 addition & 1 deletion base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7305,7 +7305,7 @@ digits!
"""
MethodError(f, args)

A method with the required type signature does not exist in the given generic function.
A method with the required type signature does not exist in the given generic function. Alternatively, there is no unique most-specific method.
Copy link
Contributor

Choose a reason for hiding this comment

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

run make docs and commit the rst

"""
MethodError

Expand Down
4 changes: 2 additions & 2 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ end
map(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i=1:length(a) ]

function precompile(f::ANY, args::Tuple)
ccall(:jl_compile_hint, Void, (Any,), Tuple{Core.Typeof(f), args...})
ccall(:jl_compile_hint, Cint, (Any,), Tuple{Core.Typeof(f), args...}) != 0
end

function precompile(argt::Type)
ccall(:jl_compile_hint, Void, (Any,), argt)
ccall(:jl_compile_hint, Cint, (Any,), argt) != 0
end

esc(e::ANY) = Expr(:escape, e)
Expand Down
10 changes: 9 additions & 1 deletion base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ function _dump_function(f, t::ANY, native, wrapper, strip_ir_metadata, dump_modu
llvmf = ccall(:jl_get_llvmf, Ptr{Void}, (Any, Bool, Bool), t, wrapper, native)

if llvmf == C_NULL
error("no method found for the specified argument types")
error("did not find a unique method for the specified argument types")
end

if native
Expand Down Expand Up @@ -434,3 +434,11 @@ function method_exists(f::ANY, t::ANY)
t = Tuple{isa(f,Type) ? Type{f} : typeof(f), t.parameters...}
return ccall(:jl_method_exists, Cint, (Any, Any), typeof(f).name.mt, t) != 0
end

function isambiguous(m1::Method, m2::Method)
ti = typeintersect(m1.sig, m2.sig)
ml = _methods_by_ftype(ti, 1)
ml == false && return true
m = ml[1][3]
!(m.sig <: ti)
end
20 changes: 19 additions & 1 deletion base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,13 @@ function showerror(io::IO, ex::MethodError)
# a tuple of the arguments otherwise.
is_arg_types = isa(ex.args, DataType)
arg_types = is_arg_types ? ex.args : typesof(ex.args...)
f = ex.f
meth = methods(f, arg_types)
if length(meth) > 1
return showerror_ambiguous(io, meth, f, arg_types)
end
arg_types_param::SimpleVector = arg_types.parameters
print(io, "MethodError: ")
f = ex.f
ft = typeof(f)
name = ft.name.mt.name
f_is_function = false
Expand Down Expand Up @@ -247,6 +251,20 @@ end
striptype{T}(::Type{T}) = T
striptype(::Any) = nothing

function showerror_ambiguous(io::IO, meth, f, args)
print(io, "MethodError: ", f, "(")
p = args.parameters
for (i,a) in enumerate(p)
print(io, "::", a)
i == length(p) ? print(io, ")") : print(io, ", ")
end
print(io, " is ambiguous. Candidates:")
for m in meth
print(io, "\n ", m)
end
nothing
end

#Show an error by directly calling jl_printf.
#Useful in Base submodule __init__ functions where STDERR isn't defined yet.
function showerror_nostdio(err, msg::AbstractString)
Expand Down
3 changes: 3 additions & 0 deletions base/serialize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ function serialize(s::SerializationState, meth::Method)
serialize(s, meth.line)
serialize(s, meth.sig)
serialize(s, meth.tvars)
serialize(s, meth.ambig)
serialize(s, meth.isstaged)
serialize(s, meth.lambda_template)
if isdefined(meth, :roots)
Expand Down Expand Up @@ -589,6 +590,7 @@ function deserialize(s::SerializationState, ::Type{Method})
line = deserialize(s)
sig = deserialize(s)
tvars = deserialize(s)
ambig = deserialize(s)
isstaged = deserialize(s)::Bool
template = deserialize(s)::LambdaInfo
tag = Int32(read(s.io, UInt8)::UInt8)
Expand All @@ -604,6 +606,7 @@ function deserialize(s::SerializationState, ::Type{Method})
meth.line = line
meth.sig = sig
meth.tvars = tvars
meth.ambig = ambig
meth.isstaged = isstaged
meth.lambda_template = template
roots === nothing || (meth.roots = roots)
Expand Down
42 changes: 42 additions & 0 deletions base/test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export @test, @test_throws
export @testset
# Legacy approximate testing functions, yet to be included
export @test_approx_eq, @test_approx_eq_eps, @inferred
export detect_ambiguities

#-----------------------------------------------------------------------

Expand Down Expand Up @@ -823,4 +824,45 @@ function test_approx_eq_modphase{S<:Real,T<:Real}(
end
end

"""
detect_ambiguities(mod1, mod2...; imported=false)

Returns a vector of `(Method,Method)` pairs of ambiguous methods
defined in the specified modules. Use `imported=true` if you wish to
also test functions that were imported into these modules from
elsewhere.
"""
function detect_ambiguities(mods...; imported::Bool=false)
function sortdefs(m1, m2)
ord12 = m1.file < m2.file
if !ord12 && (m1.file == m2.file)
ord12 = m1.line < m2.line
end
ord12 ? (m1, m2) : (m2, m1)
end
ambs = Set{Tuple{Method,Method}}()
for mod in mods
for n in names(mod, true, imported)
try
f = getfield(mod, n)
if isa(f, Function)
mt = methods(f)
for m in mt
if m.ambig != nothing
for m2 in m.ambig
if Base.isambiguous(m, m2)
push!(ambs, sortdefs(m, m2))
end
end
end
end
end
catch
println("Skipping ", mod, '.', n) # typically stale exports
end
end
end
collect(ambs)
end

end # module
2 changes: 1 addition & 1 deletion doc/stdlib/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ Errors

.. Docstring generated from Julia source

A method with the required type signature does not exist in the given generic function.
A method with the required type signature does not exist in the given generic function. Alternatively, there is no unique most-specific method.

.. function:: NullException()

Expand Down
3 changes: 3 additions & 0 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ static jl_lambda_info_t *jl_copy_lambda(jl_lambda_info_t *linfo)
new_linfo->nargs = linfo->nargs;
new_linfo->isva = linfo->isva;
new_linfo->rettype = linfo->rettype;
new_linfo->def = linfo->def;
return new_linfo;
}

Expand Down Expand Up @@ -542,6 +543,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(void)
m->tfunc.unknown = jl_nothing;
m->sig = NULL;
m->tvars = NULL;
m->ambig = NULL;
m->roots = NULL;
m->module = jl_current_module;
m->lambda_template = NULL;
Expand All @@ -567,6 +569,7 @@ jl_method_t *jl_new_method(jl_lambda_info_t *definition, jl_sym_t *name, jl_tupl
if (jl_svec_len(tvars) == 1)
tvars = (jl_svec_t*)jl_svecref(tvars, 0);
m->tvars = tvars;
m->ambig = jl_nothing;
JL_GC_PUSH1(&m);
// the front end may add this lambda to multiple methods; make a copy if so
jl_method_t *oldm = definition->def;
Expand Down
3 changes: 2 additions & 1 deletion src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,8 @@ static void jl_check_type_tuple(jl_value_t *t, jl_sym_t *name, const char *ctx)
JL_CALLABLE(jl_f_applicable)
{
JL_NARGSV(applicable, 1);
return jl_method_lookup(jl_gf_mtable(args[0]), args, nargs, 1) != NULL ?
jl_typemap_entry_t *entry;
return jl_method_lookup(jl_gf_mtable(args[0]), args, nargs, 1, &entry) != NULL ?
jl_true : jl_false;
}

Expand Down
2 changes: 1 addition & 1 deletion src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1417,7 +1417,7 @@ static Value *boxed(const jl_cgval_t &vinfo, jl_codectx_t *ctx, bool gcrooted)
if (t == T_int1)
return julia_bool(v);

if (ctx->linfo->def) { // don't bother codegen pre-boxing for toplevel
if (ctx->linfo && ctx->linfo->def) { // don't bother codegen pre-boxing for toplevel
if (Constant *c = dyn_cast<Constant>(v)) {
jl_value_t *s = static_constant_instance(c, jt);
if (s) {
Expand Down
5 changes: 3 additions & 2 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1096,9 +1096,10 @@ void *jl_get_llvmf(jl_tupletype_t *tt, bool getwrapper, bool getdeclarations)
if (tt != NULL) {
linfo = jl_get_specialization1(tt);
if (linfo == NULL) {
jl_typemap_entry_t *entry;
linfo = jl_method_lookup_by_type(
((jl_datatype_t*)jl_tparam0(tt))->name->mt, tt, 0, 0);
if (linfo == NULL) {
((jl_datatype_t*)jl_tparam0(tt))->name->mt, tt, 0, 0, &entry);
if (linfo == NULL || jl_has_call_ambiguities(tt, linfo->def)) {
JL_GC_POP();
return NULL;
}
Expand Down
3 changes: 3 additions & 0 deletions src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ static void jl_serialize_value_(ios_t *s, jl_value_t *v)
write_int32(s, m->line);
jl_serialize_value(s, (jl_value_t*)m->sig);
jl_serialize_value(s, (jl_value_t*)m->tvars);
jl_serialize_value(s, (jl_value_t*)m->ambig);
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm also a little uncertain about whether one should add this, or whether there's a scan for ambiguities upon deserialization.

Copy link
Member

Choose a reason for hiding this comment

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

We probably want to keep this, to avoid re-computing ambiguities.

write_int8(s, m->called);
jl_serialize_value(s, (jl_value_t*)m->module);
jl_serialize_value(s, (jl_value_t*)m->roots);
Expand Down Expand Up @@ -1444,6 +1445,8 @@ static jl_value_t *jl_deserialize_value_(ios_t *s, jl_value_t *vtag, jl_value_t
jl_gc_wb(m, m->sig);
m->tvars = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&m->tvars);
jl_gc_wb(m, m->tvars);
m->ambig = jl_deserialize_value(s, (jl_value_t**)&m->ambig);
jl_gc_wb(m, m->ambig);
m->called = read_int8(s);
m->module = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&m->module);
jl_gc_wb(m, m->module);
Expand Down
Loading