From 4e0bf3f556a02b976a2711ceb867352f59bbd51b Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Thu, 15 Oct 2020 18:31:48 -0400 Subject: [PATCH] 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 ``` --- src/julia.h | 1 + src/rtutils.c | 59 +++++++++++++++++++++++++++++++++++++++++---------- test/show.jl | 6 ++++++ 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/julia.h b/src/julia.h index 73cac24dee6dd..636a719732069 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1105,6 +1105,7 @@ static inline int jl_is_layout_opaque(const jl_datatype_layout_t *l) JL_NOTSAFEP #define jl_is_svec(v) jl_typeis(v,jl_simplevector_type) #define jl_is_simplevector(v) jl_is_svec(v) #define jl_is_datatype(v) jl_typeis(v,jl_datatype_type) +#define jl_is_function(v) jl_typeis(v,jl_function_type) #define jl_is_mutable(t) (((jl_datatype_t*)t)->mutabl) #define jl_is_mutable_datatype(t) (jl_is_datatype(t) && (((jl_datatype_t*)t)->mutabl)) #define jl_is_immutable(t) (!((jl_datatype_t*)t)->mutabl) diff --git a/src/rtutils.c b/src/rtutils.c index 3f7768db4c1db..9e19c6f431f69 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -604,6 +604,22 @@ JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt JL_PROPAGATES_ROO return (jl_value_t*)dt; } +int is_globfunction(jl_value_t *v, jl_datatype_t *dv, jl_sym_t **globname_out) { + jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL; + *globname_out = globname; + int globfunc = 0; + if (globname && !strchr(jl_symbol_name(globname), '#') && + !strchr(jl_symbol_name(globname), '@') && dv->name->module && + jl_binding_resolved_p(dv->name->module, globname)) { + jl_binding_t *b = jl_get_module_binding(dv->name->module, globname); + // The `||` makes this function work for both function instances and function types. + if (b && b->value && (b->value == v || jl_typeof(b->value) == v)) { + globfunc = 1; + } + } + return globfunc; +} + static size_t jl_static_show_x_sym_escaped(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT { size_t n = 0; @@ -688,18 +704,10 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } else if (vt == jl_datatype_type) { jl_datatype_t *dv = (jl_datatype_t*)v; - jl_sym_t *globname = dv->name->mt != NULL ? dv->name->mt->name : NULL; - int globfunc = 0; - if (globname && !strchr(jl_symbol_name(globname), '#') && - !strchr(jl_symbol_name(globname), '@') && dv->name->module && - jl_binding_resolved_p(dv->name->module, globname)) { - jl_binding_t *b = jl_get_module_binding(dv->name->module, globname); - if (b && b->value && jl_typeof(b->value) == v) - globfunc = 1; - } + jl_sym_t *globname; + int globfunc = is_globfunction(v, dv, &globname); jl_sym_t *sym = globfunc ? globname : dv->name->name; char *sn = jl_symbol_name(sym); - size_t i = 0; size_t quote = 0; if (globfunc) { n += jl_printf(out, "typeof("); @@ -707,6 +715,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt if (jl_core_module && (dv->name->module != jl_core_module || !jl_module_exports_p(jl_core_module, sym))) { n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth); n += jl_printf(out, "."); + size_t i = 0; if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) { n += jl_printf(out, ":("); quote = 1; @@ -715,8 +724,9 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_static_show_x_sym_escaped(out, sym); if (globfunc) { n += jl_printf(out, ")"); - if (quote) + if (quote) { n += jl_printf(out, ")"); + } } if (dv->parameters && (jl_value_t*)dv != dv->name->wrapper && (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 n += jl_static_show_x(out, *(jl_value_t**)v, depth); n += jl_printf(out, ")"); } + else if (jl_function_type && jl_isa(v, (jl_value_t*)jl_function_type)) { + jl_datatype_t *dv = (jl_datatype_t*)vt; + jl_sym_t *sym = dv->name->mt->name; + char *sn = jl_symbol_name(sym); + + jl_sym_t *globname; + int globfunc = is_globfunction(v, dv, &globname); + int quote = 0; + if (jl_core_module && (dv->name->module != jl_core_module || !jl_module_exports_p(jl_core_module, sym))) { + n += jl_static_show_x(out, (jl_value_t*)dv->name->module, depth); + n += jl_printf(out, "."); + + size_t i = 0; + if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) { + n += jl_printf(out, ":("); + quote = 1; + } + } + + n += jl_static_show_x_sym_escaped(out, sym); + + if (globfunc) { + if (quote) { + n += jl_printf(out, ")"); + } + } + } else if (jl_datatype_type && jl_is_datatype(vt)) { int istuple = jl_is_tuple_type(vt), isnamedtuple = jl_is_namedtuple_type(vt); size_t tlen = jl_datatype_nfields(vt); diff --git a/test/show.jl b/test/show.jl index 40d62e94802a1..8ce3aee3de18c 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1338,6 +1338,7 @@ end # PR #38049 struct var"#X#" end +var"#f#"() = 2 @test static_shown(var"#X#") == "$(@__MODULE__).var\"#X#\"" # (Just to make this test more sustainable,) for types, we don't necesssarily need to test @@ -1349,8 +1350,13 @@ struct var"#X#" end @testset for v in ( var"#X#", var"#X#"(), + Vector, Vector{<:Any}, Vector{var"#X#"}, + +, + typeof(+), + var"#f#", + typeof(var"#f#"), ) @test v == eval(Meta.parse(static_shown(v))) end