Skip to content

Commit 04bfa32

Browse files
committed
Make functions print as valid julia expressions.
Previously, function instances would be printed in `static_show()` as elements of a `DataType`, with no special printing for `<:Function`s. This led to printing them via _invalid_ syntax, since there is no empty constructor for `T<:Function`. Before: ```julia julia> g() = 2 g (generic function with 1 method) julia> println(static_shown(g)) typeof(Main.g)() julia> eval(Meta.parse(static_shown(g))) ERROR: MethodError: no method matching typeof(g)() ``` After: ```julia julia> g() = 2 g (generic function with 1 method) julia> println(static_shown(g)) Main.g julia> eval(Meta.parse(static_shown(g))) g (generic function with 1 method) ``` I chose to keep the Module prefix on the functions, so that they will be valid julia expressions that you can enter at the REPL to reproduce the function instance, even though this is a break from the julia `show()` behavior. One caveat is that this is still not correct for anonymous functions, but I'm also not really sure what would be a better way to print anonymous functions... I think this is _probably_ better than it was before, but very open to suggestions for how to improve it. Before: ```julia julia> l = (x,y)->x+y julia> println(static_shown(l)) getfield(Main, Symbol("#3#4"))() julia> eval(Meta.parse(static_shown(l))) ERROR: MethodError: no method matching var"#3#4"() ``` After: ```julia julia> l = (x,y)->x+y julia> println(static_shown(l)) Main.var"#3" julia> eval(Meta.parse(static_shown(l))) ERROR: UndefVarError: #3 not defined ```
1 parent 3599557 commit 04bfa32

File tree

3 files changed

+55
-11
lines changed

3 files changed

+55
-11
lines changed

src/julia.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,7 @@ static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEP
11051105
#define jl_is_svec(v) jl_typeis(v,jl_simplevector_type)
11061106
#define jl_is_simplevector(v) jl_is_svec(v)
11071107
#define jl_is_datatype(v) jl_typeis(v,jl_datatype_type)
1108+
#define jl_is_function(v) jl_typeis(v,jl_function_type)
11081109
#define jl_is_mutable(t) (((jl_datatype_t*)t)->mutabl)
11091110
#define jl_is_mutable_datatype(t) (jl_is_datatype(t) && (((jl_datatype_t*)t)->mutabl))
11101111
#define jl_is_immutable(t) (!((jl_datatype_t*)t)->mutabl)

src/rtutils.c

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,22 @@ JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt JL_PROPAGATES_ROO
604604
return (jl_value_t*)dt;
605605
}
606606

607+
int is_globfunction(jl_value_t *v, jl_datatype_t *dv, jl_sym_t **globname_out) {
608+
jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL;
609+
*globname_out = globname;
610+
int globfunc = 0;
611+
if (globname && !strchr(jl_symbol_name(globname), '#') &&
612+
!strchr(jl_symbol_name(globname), '@') && dv->name->module &&
613+
jl_binding_resolved_p(dv->name->module, globname)) {
614+
jl_binding_t *b = jl_get_module_binding(dv->name->module, globname);
615+
// The `||` makes this function work for both function instances and function types.
616+
if (b && b->value && (b->value == v || jl_typeof(b->value) == v)) {
617+
globfunc = 1;
618+
}
619+
}
620+
return globfunc;
621+
}
622+
607623
static size_t jl_static_show_x_sym_escaped(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT
608624
{
609625
size_t n = 0;
@@ -688,25 +704,18 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt
688704
}
689705
else if (vt == jl_datatype_type) {
690706
jl_datatype_t *dv = (jl_datatype_t*)v;
691-
jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL;
692-
int globfunc = 0;
693-
if (globname && !strchr(jl_symbol_name(globname), '#') &&
694-
!strchr(jl_symbol_name(globname), '@') && dv->name->module &&
695-
jl_binding_resolved_p(dv->name->module, globname)) {
696-
jl_binding_t *b = jl_get_module_binding(dv->name->module, globname);
697-
if (b && b->value && jl_typeof(b->value) == v)
698-
globfunc = 1;
699-
}
707+
jl_sym_t *globname;
708+
int globfunc = is_globfunction(v, dv, &globname);
700709
jl_sym_t *sym = globfunc ? globname : dv->name->name;
701710
char *sn = jl_symbol_name(sym);
702-
size_t i = 0;
703711
size_t quote = 0;
704712
if (globfunc) {
705713
n += jl_printf(out, "typeof(");
706714
}
707715
if (jl_core_module && (dv->name->module != jl_core_module || !jl_module_exports_p(jl_core_module, sym))) {
708716
n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth);
709717
n += jl_printf(out, ".");
718+
size_t i = 0;
710719
if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) {
711720
n += jl_printf(out, ":(");
712721
quote = 1;
@@ -715,8 +724,9 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt
715724
n += jl_static_show_x_sym_escaped(out, sym);
716725
if (globfunc) {
717726
n += jl_printf(out, ")");
718-
if (quote)
727+
if (quote) {
719728
n += jl_printf(out, ")");
729+
}
720730
}
721731
if (dv->parameters && (jl_value_t*)dv != dv->name->wrapper &&
722732
(jl_has_free_typevars(v) ||
@@ -982,6 +992,33 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt
982992
n += jl_static_show_x(out, *(jl_value_t**)v, depth);
983993
n += jl_printf(out, ")");
984994
}
995+
else if (jl_function_type && jl_isa(v, (jl_value_t*)jl_function_type)) {
996+
jl_datatype_t *dv = (jl_datatype_t*)vt;
997+
jl_sym_t *sym = dv->name->mt->name;
998+
char *sn = jl_symbol_name(sym);
999+
1000+
jl_sym_t *globname;
1001+
int globfunc = is_globfunction(v, dv, &globname);
1002+
int quote = 0;
1003+
if (jl_core_module && (dv->name->module != jl_core_module || !jl_module_exports_p(jl_core_module, sym))) {
1004+
n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth);
1005+
n += jl_printf(out, ".");
1006+
1007+
size_t i = 0;
1008+
if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) {
1009+
n += jl_printf(out, ":(");
1010+
quote = 1;
1011+
}
1012+
}
1013+
1014+
n += jl_static_show_x_sym_escaped(out, sym);
1015+
1016+
if (globfunc) {
1017+
if (quote) {
1018+
n += jl_printf(out, ")");
1019+
}
1020+
}
1021+
}
9851022
else if (jl_datatype_type && jl_is_datatype(vt)) {
9861023
int istuple = jl_is_tuple_type(vt), isnamedtuple = jl_is_namedtuple_type(vt);
9871024
size_t tlen = jl_datatype_nfields(vt);

test/show.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,7 @@ end
13381338

13391339
# PR #38049
13401340
struct var"#X#" end
1341+
var"#f#"() = 2
13411342

13421343
@test static_shown(var"#X#") == "Main.var\"#X#\""
13431344
# (Just to make this test more sustainable,) for types, we don't necesssarily need to test
@@ -1349,8 +1350,13 @@ struct var"#X#" end
13491350
@testset for v in (
13501351
var"#X#",
13511352
var"#X#"(),
1353+
Vector,
13521354
Vector{<:Any},
13531355
Vector{var"#X#"},
1356+
+,
1357+
typeof(+),
1358+
var"#f#",
1359+
typeof(var"#f#"),
13541360
)
13551361
@test v == eval(Meta.parse(static_shown(v)))
13561362
end

0 commit comments

Comments
 (0)