Skip to content

Commit 22f58fc

Browse files
apaz-cliSeelengrab
authored andcommitted
Fix MethodError from invoke with UnionAll
Fixes #47559 Co-authored-by: Sukera <Seelengrab@users.noreply.github.com>
1 parent 3a671a6 commit 22f58fc

File tree

2 files changed

+28
-35
lines changed

2 files changed

+28
-35
lines changed

base/errorshow.jl

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,15 @@ end
224224
function showerror(io::IO, ex::MethodError)
225225
# ex.args is a tuple type if it was thrown from `invoke` and is
226226
# a tuple of the arguments otherwise.
227-
is_arg_types = isa(ex.args, DataType)
228-
arg_types = (is_arg_types ? ex.args : typesof(ex.args...))::DataType
227+
is_arg_types = !isa(ex.args, Tuple)
228+
arg_types = is_arg_types ? ex.args : typesof(ex.args...)
229+
arg_types_param::SimpleVector = (unwrap_unionall(arg_types)::DataType).parameters
230+
san_arg_types_param = Any[rewrap_unionall(a, arg_types) for a in arg_types_param]
229231
f = ex.f
230232
meth = methods_including_ambiguous(f, arg_types)
231233
if isa(meth, MethodList) && length(meth) > 1
232234
return showerror_ambiguous(io, meth, f, arg_types)
233235
end
234-
arg_types_param::SimpleVector = arg_types.parameters
235236
show_candidates = true
236237
print(io, "MethodError: ")
237238
ft = typeof(f)
@@ -241,7 +242,9 @@ function showerror(io::IO, ex::MethodError)
241242
f = (ex.args::Tuple)[2]
242243
ft = typeof(f)
243244
arg_types_param = arg_types_param[3:end]
245+
san_arg_types_param = san_arg_types_param[3:end]
244246
kwargs = pairs(ex.args[1])
247+
arg_types = Tuple{arg_types_param...}
245248
ex = MethodError(f, ex.args[3:end::Int])
246249
end
247250
name = ft.name.mt.name
@@ -261,29 +264,17 @@ function showerror(io::IO, ex::MethodError)
261264
end
262265
print(io, "no method matching ")
263266
show_signature_function(io, isa(f, Type) ? Type{f} : typeof(f))
264-
print(io, "(")
265-
for (i, typ) in enumerate(arg_types_param)
266-
print(io, "::", typ)
267-
i == length(arg_types_param) || print(io, ", ")
268-
end
269-
if !isempty(kwargs)
270-
print(io, "; ")
271-
for (i, (k, v)) in enumerate(kwargs)
272-
print(io, k, "::", typeof(v))
273-
i == length(kwargs)::Int || print(io, ", ")
274-
end
275-
end
276-
print(io, ")")
267+
show_tuple_as_call(io, :function, arg_types; hasfirst=false, kwargs = !isempty(kwargs) ? Any[(k, typeof(v)) for (k, v) in kwargs] : nothing)
277268
end
278269
# catch the two common cases of element-wise addition and subtraction
279-
if (f === Base.:+ || f === Base.:-) && length(arg_types_param) == 2
270+
if (f === Base.:+ || f === Base.:-) && length(san_arg_types_param) == 2
280271
# we need one array of numbers and one number, in any order
281-
if any(x -> x <: AbstractArray{<:Number}, arg_types_param) &&
282-
any(x -> x <: Number, arg_types_param)
272+
if any(x -> x <: AbstractArray{<:Number}, san_arg_types_param) &&
273+
any(x -> x <: Number, san_arg_types_param)
283274

284275
nounf = f === Base.:+ ? "addition" : "subtraction"
285276
varnames = ("scalar", "array")
286-
first, second = arg_types_param[1] <: Number ? varnames : reverse(varnames)
277+
first, second = san_arg_types_param[1] <: Number ? varnames : reverse(varnames)
287278
fstring = f === Base.:+ ? "+" : "-" # avoid depending on show_default for functions (invalidation)
288279
print(io, "\nFor element-wise $nounf, use broadcasting with dot syntax: $first .$fstring $second")
289280
end
@@ -294,13 +285,13 @@ function showerror(io::IO, ex::MethodError)
294285
# Check for local functions that shadow methods in Base
295286
if f_is_function && isdefined(Base, name)
296287
basef = getfield(Base, name)
297-
if basef !== ex.f && hasmethod(basef, arg_types)
288+
if basef !== f && hasmethod(basef, arg_types)
298289
print(io, "\nYou may have intended to import ")
299290
show_unquoted(io, Expr(:., :Base, QuoteNode(name)))
300291
end
301292
end
302-
if (ex.world != typemax(UInt) && hasmethod(ex.f, arg_types) &&
303-
!hasmethod(ex.f, arg_types, world = ex.world))
293+
if (ex.world != typemax(UInt) && hasmethod(f, arg_types) &&
294+
!hasmethod(f, arg_types, world = ex.world))
304295
curworld = get_world_counter()
305296
print(io, "\nThe applicable method may be too new: running in world age $(ex.world), while current world is $(curworld).")
306297
end
@@ -319,7 +310,7 @@ function showerror(io::IO, ex::MethodError)
319310
"\nYou can convert to a column vector with the vec() function.")
320311
end
321312
end
322-
Experimental.show_error_hints(io, ex, arg_types_param, kwargs)
313+
Experimental.show_error_hints(io, ex, san_arg_types_param, kwargs)
323314
show_candidates && try
324315
show_method_candidates(io, ex, kwargs)
325316
catch ex
@@ -330,16 +321,12 @@ end
330321
striptype(::Type{T}) where {T} = T
331322
striptype(::Any) = nothing
332323

333-
function showerror_ambiguous(io::IO, meths, f, args)
324+
function showerror_ambiguous(io::IO, meths, f, args::Type)
325+
@nospecialize f args
334326
print(io, "MethodError: ")
335327
show_signature_function(io, isa(f, Type) ? Type{f} : typeof(f))
336-
print(io, "(")
337-
p = args.parameters
338-
for (i,a) in enumerate(p)
339-
print(io, "::", a)
340-
i < length(p) && print(io, ", ")
341-
end
342-
println(io, ") is ambiguous.\n\nCandidates:")
328+
show_tuple_as_call(io, :var"", args, hasfirst=false)
329+
println(io, " is ambiguous.\n\nCandidates:")
343330
sigfix = Any
344331
for m in meths
345332
print(io, " ")
@@ -351,7 +338,7 @@ function showerror_ambiguous(io::IO, meths, f, args)
351338
let sigfix=sigfix
352339
if all(m->morespecific(sigfix, m.sig), meths)
353340
print(io, "\nPossible fix, define\n ")
354-
Base.show_tuple_as_call(io, :function, sigfix)
341+
show_tuple_as_call(io, :function, sigfix)
355342
else
356343
print(io, "To resolve the ambiguity, try making one of the methods more specific, or ")
357344
print(io, "adding a new method more specific than any of the existing applicable methods.")
@@ -380,9 +367,10 @@ stacktrace_linebreaks()::Bool =
380367
tryparse(Bool, get(ENV, "JULIA_STACKTRACE_LINEBREAKS", "false")) === true
381368

382369
function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=())
383-
is_arg_types = isa(ex.args, DataType)
370+
is_arg_types = !isa(ex.args, Tuple)
384371
arg_types = is_arg_types ? ex.args : typesof(ex.args...)
385-
arg_types_param = Any[arg_types.parameters...]
372+
arg_types_param = Any[(unwrap_unionall(arg_types)::DataType).parameters...]
373+
arg_types_param = Any[rewrap_unionall(a, arg_types) for a in arg_types_param]
386374
# Displays the closest candidates of the given function by looping over the
387375
# functions methods and counting the number of matching arguments.
388376
f = ex.f

test/errorshow.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,3 +933,8 @@ let err_str
933933
err_str = @except_str "a" + "b" MethodError
934934
@test occursin("String concatenation is performed with *", err_str)
935935
end
936+
937+
@testset "issue #47559" begin
938+
err = try; invoke(Returns, Tuple{Any,Val{N}} where N, 1, Val(1)) catch ex; ex; end
939+
@test startswith(sprint(Base.showerror, err), "MethodError: no method matching Returns(::Any, ::Val{N})")
940+
end

0 commit comments

Comments
 (0)