Skip to content

Commit 2859b11

Browse files
authored
improve display of keyword argument methods in stack traces (#33118)
part of #33065
1 parent 6cf8dba commit 2859b11

17 files changed

+154
-100
lines changed

base/errorshow.jl

+11-7
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ function showerror(io::IO, ex::MethodError)
181181
name = ft.name.mt.name
182182
f_is_function = false
183183
kwargs = ()
184-
if startswith(string(ft.name.name), "#kw#")
184+
if endswith(string(ft.name.name), "##kw")
185185
f = ex.args[2]
186186
ft = typeof(f)
187187
name = ft.name.mt.name
@@ -416,11 +416,10 @@ function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=()
416416
end
417417
end
418418
end
419-
kwords = Symbol[]
420-
if isdefined(ft.name.mt, :kwsorter)
421-
kwsorter_t = typeof(ft.name.mt.kwsorter)
422-
kwords = kwarg_decl(method, kwsorter_t)
423-
length(kwords) > 0 && print(iob, "; ", join(kwords, ", "))
419+
kwords = kwarg_decl(method)
420+
if !isempty(kwords)
421+
print(iob, "; ")
422+
join(iob, kwords, ", ")
424423
end
425424
print(iob, ")")
426425
show_method_params(iob0, tv)
@@ -602,6 +601,11 @@ function show_backtrace(io::IO, t::Vector{Any})
602601
end
603602
end
604603

604+
function is_kw_sorter_name(name::Symbol)
605+
sn = string(name)
606+
return !startswith(sn, '#') && endswith(sn, "##kw")
607+
end
608+
605609
function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true)
606610
n = 0
607611
last_frame = StackTraces.UNKNOWN
@@ -621,7 +625,7 @@ function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true)
621625
continue
622626
end
623627

624-
if lkup.from_c && skipC
628+
if (lkup.from_c && skipC) || is_kw_sorter_name(lkup.func)
625629
continue
626630
end
627631
count += 1

base/methodshow.jl

+33-35
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,27 @@ end
7373

7474
const empty_sym = Symbol("")
7575

76-
function kwarg_decl(m::Method, kwtype::DataType)
77-
sig = rewrap_unionall(Tuple{kwtype, Any, unwrap_unionall(m.sig).parameters...}, m.sig)
78-
kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, get_world_counter())
79-
if kwli !== nothing
80-
kwli = kwli::Method
81-
slotnames = ccall(:jl_uncompress_argnames, Vector{Any}, (Any,), kwli.slot_syms)
82-
kws = filter(x -> !(x === empty_sym || '#' in string(x)), slotnames[(kwli.nargs + 1):end])
83-
# ensure the kwarg... is always printed last. The order of the arguments are not
84-
# necessarily the same as defined in the function
85-
i = findfirst(x -> endswith(string(x), "..."), kws)
86-
if i !== nothing
87-
push!(kws, kws[i])
88-
deleteat!(kws, i)
76+
function kwarg_decl(m::Method)
77+
mt = get_methodtable(m)
78+
if isdefined(mt, :kwsorter)
79+
kwtype = typeof(mt.kwsorter)
80+
sig = rewrap_unionall(Tuple{kwtype, Any, unwrap_unionall(m.sig).parameters...}, m.sig)
81+
kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, get_world_counter())
82+
if kwli !== nothing
83+
kwli = kwli::Method
84+
slotnames = ccall(:jl_uncompress_argnames, Vector{Any}, (Any,), kwli.slot_syms)
85+
kws = filter(x -> !(x === empty_sym || '#' in string(x)), slotnames[(kwli.nargs + 1):end])
86+
# ensure the kwarg... is always printed last. The order of the arguments are not
87+
# necessarily the same as defined in the function
88+
i = findfirst(x -> endswith(string(x), "..."), kws)
89+
if i !== nothing
90+
push!(kws, kws[i])
91+
deleteat!(kws, i)
92+
end
93+
return kws
8994
end
90-
return kws
9195
end
92-
return ()
96+
return Any[]
9397
end
9498

9599
function show_method_params(io::IO, tv)
@@ -155,7 +159,7 @@ function functionloc(@nospecialize(f))
155159
return functionloc(first(mt))
156160
end
157161

158-
function show(io::IO, m::Method; kwtype::Union{DataType, Nothing}=nothing)
162+
function show(io::IO, m::Method)
159163
tv, decls, file, line = arg_decl_parts(m)
160164
sig = unwrap_unionall(m.sig)
161165
ft0 = sig.parameters[1]
@@ -182,12 +186,10 @@ function show(io::IO, m::Method; kwtype::Union{DataType, Nothing}=nothing)
182186
print(io, "(")
183187
join(io, [isempty(d[2]) ? d[1] : d[1]*"::"*d[2] for d in decls[2:end]],
184188
", ", ", ")
185-
if kwtype !== nothing
186-
kwargs = kwarg_decl(m, kwtype)
187-
if !isempty(kwargs)
188-
print(io, "; ")
189-
join(io, kwargs, ", ", ", ")
190-
end
189+
kwargs = kwarg_decl(m)
190+
if !isempty(kwargs)
191+
print(io, "; ")
192+
join(io, kwargs, ", ", ", ")
191193
end
192194
print(io, ")")
193195
show_method_params(io, tv)
@@ -235,7 +237,6 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru
235237
if header
236238
show_method_list_header(io, ms, str -> "\""*str*"\"")
237239
end
238-
kwtype = isdefined(mt, :kwsorter) ? typeof(mt.kwsorter) : nothing
239240
n = rest = 0
240241
local last
241242

@@ -245,7 +246,7 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru
245246
n += 1
246247
println(io)
247248
print(io, "[$(n)] ")
248-
show(io, meth; kwtype=kwtype)
249+
show(io, meth)
249250
file, line = meth.file, meth.line
250251
try
251252
file, line = invokelatest(methodloc_callback[], meth)
@@ -260,7 +261,7 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru
260261
if rest > 0
261262
println(io)
262263
if rest == 1
263-
show(io, last; kwtype=kwtype)
264+
show(io, last)
264265
else
265266
print(io, "... $rest methods not shown")
266267
if hasname
@@ -323,7 +324,7 @@ function url(m::Method)
323324
end
324325
end
325326

326-
function show(io::IO, ::MIME"text/html", m::Method; kwtype::Union{DataType, Nothing}=nothing)
327+
function show(io::IO, ::MIME"text/html", m::Method)
327328
tv, decls, file, line = arg_decl_parts(m)
328329
sig = unwrap_unionall(m.sig)
329330
ft0 = sig.parameters[1]
@@ -346,13 +347,11 @@ function show(io::IO, ::MIME"text/html", m::Method; kwtype::Union{DataType, Noth
346347
print(io, "(")
347348
join(io, [isempty(d[2]) ? d[1] : d[1]*"::<b>"*d[2]*"</b>"
348349
for d in decls[2:end]], ", ", ", ")
349-
if kwtype !== nothing
350-
kwargs = kwarg_decl(m, kwtype)
351-
if !isempty(kwargs)
352-
print(io, "; <i>")
353-
join(io, kwargs, ", ", ", ")
354-
print(io, "</i>")
355-
end
350+
kwargs = kwarg_decl(m)
351+
if !isempty(kwargs)
352+
print(io, "; <i>")
353+
join(io, kwargs, ", ", ", ")
354+
print(io, "</i>")
356355
end
357356
print(io, ")")
358357
if !isempty(tv)
@@ -379,11 +378,10 @@ end
379378
function show(io::IO, mime::MIME"text/html", ms::MethodList)
380379
mt = ms.mt
381380
show_method_list_header(io, ms, str -> "<b>"*str*"</b>")
382-
kwtype = isdefined(mt, :kwsorter) ? typeof(mt.kwsorter) : nothing
383381
print(io, "<ul>")
384382
for meth in ms
385383
print(io, "<li> ")
386-
show(io, mime, meth; kwtype=kwtype)
384+
show(io, mime, meth)
387385
print(io, "</li> ")
388386
end
389387
print(io, "</ul>")

base/reflection.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ function hasmethod(@nospecialize(f), @nospecialize(t), kwnames::Tuple{Vararg{Sym
12201220
hasmethod(f, t, world=world) || return false
12211221
isempty(kwnames) && return true
12221222
m = which(f, t)
1223-
kws = kwarg_decl(m, Core.kwftype(typeof(f)))
1223+
kws = kwarg_decl(m)
12241224
for kw in kws
12251225
endswith(String(kw), "...") && return true
12261226
end

base/show.jl

+23-3
Original file line numberDiff line numberDiff line change
@@ -1501,11 +1501,21 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int)
15011501
nothing
15021502
end
15031503

1504-
function show_tuple_as_call(io::IO, name::Symbol, sig::Type)
1504+
demangle_function_name(name::Symbol) = Symbol(demangle_function_name(string(name)))
1505+
function demangle_function_name(name::AbstractString)
1506+
demangle = split(name, '#')
1507+
# kw sorters and impl methods use the name scheme `f#...`
1508+
if length(demangle) >= 2 && demangle[1] != ""
1509+
return demangle[1]
1510+
end
1511+
return name
1512+
end
1513+
1514+
function show_tuple_as_call(io::IO, name::Symbol, sig::Type, demangle=false, kwargs=nothing)
15051515
# print a method signature tuple for a lambda definition
15061516
color = get(io, :color, false) && get(io, :backtrace, false) ? stackframe_function_color() : :nothing
15071517
if sig === Tuple
1508-
printstyled(io, name, "(...)", color=color)
1518+
printstyled(io, demangle ? demangle_function_name(name) : name, "(...)", color=color)
15091519
return
15101520
end
15111521
tv = Any[]
@@ -1522,7 +1532,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type)
15221532
if ft <: Function && isa(uw,DataType) && isempty(uw.parameters) &&
15231533
isdefined(uw.name.module, uw.name.mt.name) &&
15241534
ft == typeof(getfield(uw.name.module, uw.name.mt.name))
1525-
print(io, uw.name.mt.name)
1535+
print(io, (demangle ? demangle_function_name : identity)(uw.name.mt.name))
15261536
elseif isa(ft, DataType) && ft.name === Type.body.name && !Core.Compiler.has_free_typevars(ft)
15271537
f = ft.parameters[1]
15281538
print(io, f)
@@ -1538,6 +1548,16 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type)
15381548
first = false
15391549
print(env_io, "::", sig[i])
15401550
end
1551+
if kwargs !== nothing
1552+
print(io, "; ")
1553+
first = true
1554+
for (k, t) in kwargs
1555+
first || print(io, ", ")
1556+
first = false
1557+
print(io, k, "::")
1558+
show(io, t)
1559+
end
1560+
end
15411561
printstyled(io, ")", color=print_style)
15421562
show_method_params(io, tv)
15431563
nothing

base/stacktraces.jl

+20-3
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,28 @@ function show_spec_linfo(io::IO, frame::StackFrame)
248248
color = get(io, :color, false) && get(io, :backtrace, false) ?
249249
Base.stackframe_function_color() :
250250
:nothing
251-
printstyled(io, string(frame.func), color=color)
251+
printstyled(io, Base.demangle_function_name(string(frame.func)), color=color)
252252
end
253253
elseif frame.linfo isa Core.MethodInstance
254-
if isa(frame.linfo.def, Method)
255-
Base.show_tuple_as_call(io, frame.linfo.def.name, frame.linfo.specTypes)
254+
def = frame.linfo.def
255+
if isa(def, Method)
256+
sig = frame.linfo.specTypes
257+
if def.nkw > 0
258+
# rearrange call kw_impl(kw_args..., func, pos_args...) to func(pos_args...)
259+
kwarg_types = Any[ fieldtype(sig, i) for i = 2:(1+def.nkw) ]
260+
uw = Base.unwrap_unionall(sig)
261+
pos_sig = Base.rewrap_unionall(Tuple{uw.parameters[(def.nkw+2):end]...}, sig)
262+
kwnames = Base.method_argnames(def)[2:(def.nkw+1)]
263+
for i = 1:length(kwnames)
264+
str = string(kwnames[i])
265+
if endswith(str, "...")
266+
kwnames[i] = Symbol(str[1:end-3])
267+
end
268+
end
269+
Base.show_tuple_as_call(io, def.name, pos_sig, true, zip(kwnames, kwarg_types))
270+
else
271+
Base.show_tuple_as_call(io, def.name, sig, true)
272+
end
256273
else
257274
Base.show(io, frame.linfo)
258275
end

src/builtins.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
12931293
jl_builtin_applicable = add_builtin_func("applicable", jl_f_applicable);
12941294
jl_builtin_invoke = add_builtin_func("invoke", jl_f_invoke);
12951295
jl_typename_t *itn = ((jl_datatype_t*)jl_typeof(jl_builtin_invoke))->name;
1296-
jl_value_t *ikws = jl_new_generic_function_with_supertype(itn->name, jl_core_module, jl_builtin_type, 1);
1296+
jl_value_t *ikws = jl_new_generic_function_with_supertype(itn->name, jl_core_module, jl_builtin_type);
12971297
itn->mt->kwsorter = ikws;
12981298
jl_gc_wb(itn->mt, ikws);
12991299
jl_mk_builtin_func((jl_datatype_t*)jl_typeof(ikws), jl_symbol_name(jl_gf_name(ikws)), jl_f_invoke_kwsorter);

src/datatype.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jl_sym_t *jl_demangle_typename(jl_sym_t *s) JL_NOTSAFEPOINT
2929
if (end == n || end == n+1)
3030
len = strlen(n) - 1;
3131
else
32-
len = (end-n) - 1;
32+
len = (end-n) - 1; // extract `f` from `#f#...`
3333
return jl_symbol_n(&n[1], len);
3434
}
3535

src/dump.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,9 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) JL_
317317
tag = 10;
318318
}
319319

320-
if (strncmp(jl_symbol_name(dt->name->name), "#kw#", 4) == 0 && !internal && tag != 0) {
320+
char *dtname = jl_symbol_name(dt->name->name);
321+
size_t dtnl = strlen(dtname);
322+
if (dtnl > 4 && strcmp(&dtname[dtnl - 4], "##kw") == 0 && !internal && tag != 0) {
321323
/* XXX: yuck, this is horrible, but the auto-generated kw types from the serializer isn't a real type, so we *must* be very careful */
322324
assert(tag == 6); // other struct types should never exist
323325
tag = 9;
@@ -335,6 +337,8 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) JL_
335337
prefixed = (char*)malloc(l + 2);
336338
prefixed[0] = '#';
337339
strcpy(&prefixed[1], jl_symbol_name(mt->name));
340+
// remove ##kw suffix
341+
prefixed[l-3] = 0;
338342
jl_sym_t *tname = jl_symbol(prefixed);
339343
free(prefixed);
340344
jl_value_t *primarydt = jl_get_global(mt->module, tname);
@@ -821,6 +825,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li
821825
write_int32(s->s, m->called);
822826
write_int32(s->s, m->nargs);
823827
write_int32(s->s, m->nospecialize);
828+
write_int32(s->s, m->nkw);
824829
write_int8(s->s, m->isva);
825830
write_int8(s->s, m->pure);
826831
jl_serialize_value(s, (jl_value_t*)m->module);
@@ -1692,6 +1697,7 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_
16921697
m->called = read_int32(s->s);
16931698
m->nargs = read_int32(s->s);
16941699
m->nospecialize = read_int32(s->s);
1700+
m->nkw = read_int32(s->s);
16951701
m->isva = read_int8(s->s);
16961702
m->pure = read_int8(s->s);
16971703
m->module = (jl_module_t*)jl_deserialize_value(s, (jl_value_t**)&m->module);

src/gf.c

+17-17
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a
137137
{
138138
jl_sym_t *sname = jl_symbol(name);
139139
if (dt == NULL) {
140-
jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type, 0);
140+
jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type);
141141
jl_set_const(jl_core_module, sname, f);
142142
dt = (jl_datatype_t*)jl_typeof(f);
143143
}
@@ -2428,21 +2428,14 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_typemap_entry_t *entry, jl_valu
24282428
}
24292429

24302430
// Return value is rooted globally
2431-
jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st, int iskw)
2431+
jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st)
24322432
{
24332433
// type name is function name prefixed with #
24342434
size_t l = strlen(jl_symbol_name(name));
24352435
char *prefixed;
2436-
if (iskw) {
2437-
prefixed = (char*)malloc(l+5);
2438-
strcpy(&prefixed[0], "#kw#");
2439-
strcpy(&prefixed[4], jl_symbol_name(name));
2440-
}
2441-
else {
2442-
prefixed = (char*)malloc(l+2);
2443-
prefixed[0] = '#';
2444-
strcpy(&prefixed[1], jl_symbol_name(name));
2445-
}
2436+
prefixed = (char*)malloc(l+2);
2437+
prefixed[0] = '#';
2438+
strcpy(&prefixed[1], jl_symbol_name(name));
24462439
jl_sym_t *tname = jl_symbol(prefixed);
24472440
free(prefixed);
24482441
jl_datatype_t *ftype = (jl_datatype_t*)jl_new_datatype(
@@ -2466,16 +2459,23 @@ JL_DLLEXPORT jl_function_t *jl_get_kwsorter(jl_value_t *ty)
24662459
if (!mt->kwsorter) {
24672460
JL_LOCK(&mt->writelock);
24682461
if (!mt->kwsorter) {
2469-
jl_sym_t *name;
2462+
char *name;
24702463
if (mt == jl_nonfunction_mt) {
2471-
name = mt->name;
2464+
name = jl_symbol_name(mt->name);
24722465
}
24732466
else {
24742467
jl_datatype_t *dt = (jl_datatype_t*)jl_argument_datatype(ty);
24752468
assert(jl_is_datatype(dt));
2476-
name = dt->name->name;
2469+
name = jl_symbol_name(dt->name->name);
2470+
if (name[0] == '#')
2471+
name++;
24772472
}
2478-
mt->kwsorter = jl_new_generic_function_with_supertype(name, mt->module, jl_function_type, 1);
2473+
size_t l = strlen(name);
2474+
char *suffixed = (char*)malloc(l+5);
2475+
strcpy(&suffixed[0], name);
2476+
strcpy(&suffixed[l], "##kw");
2477+
jl_sym_t *fname = jl_symbol(suffixed);
2478+
mt->kwsorter = jl_new_generic_function_with_supertype(fname, mt->module, jl_function_type);
24792479
jl_gc_wb(mt, mt->kwsorter);
24802480
}
24812481
JL_UNLOCK(&mt->writelock);
@@ -2485,7 +2485,7 @@ JL_DLLEXPORT jl_function_t *jl_get_kwsorter(jl_value_t *ty)
24852485

24862486
jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module)
24872487
{
2488-
return jl_new_generic_function_with_supertype(name, module, jl_function_type, 0);
2488+
return jl_new_generic_function_with_supertype(name, module, jl_function_type);
24892489
}
24902490

24912491
struct ml_matches_env {

0 commit comments

Comments
 (0)