diff --git a/src/rtutils.c b/src/rtutils.c index c4ed21a78347b..01a47228f3c0a 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -604,6 +604,43 @@ JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt JL_PROPAGATES_ROO return (jl_value_t*)dt; } +static 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; + + char *sn = jl_symbol_name(name); + int hidden = 0; + if (!(jl_is_identifier(sn) || jl_is_operator(sn))) { + hidden = 1; + } + + if (hidden) { + n += jl_printf(out, "var\""); + } + n += jl_printf(out, "%s", sn); + if (hidden) { + n += jl_printf(out, "\""); + } + return n; +} + // `v` might be pointing to a field inlined in a structure therefore // `jl_typeof(v)` may not be the same with `vt` and only `vt` should be // used to determine the type of the value. @@ -670,48 +707,32 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "UnionAll"); } else if (vt == jl_datatype_type) { + // typeof(v) == DataType, so v is a Type object. + // Types are printed as a fully qualified name, with parameters, e.g. + // `Base.Set{Int}`, and function types are printed as e.g. `typeof(Main.f)` 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); - int hidden = !globfunc && strchr(sn, '#'); - size_t i = 0; - int quote = 0; - if (hidden) { - n += jl_printf(out, "getfield("); - } - else if (globfunc) { + size_t quote = 0; + if (globfunc) { n += jl_printf(out, "typeof("); } 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); - if (!hidden) { - n += jl_printf(out, "."); - if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) { - n += jl_printf(out, ":("); - quote = 1; - } + n += jl_printf(out, "."); + size_t i = 0; + if (globfunc && !jl_id_start_char(u8_nextchar(sn, &i))) { + n += jl_printf(out, ":("); + quote = 1; } } - if (hidden) { - n += jl_printf(out, ", Symbol(\""); - n += jl_printf(out, "%s", sn); - n += jl_printf(out, "\"))"); - } - else { - n += jl_printf(out, "%s", sn); - if (globfunc) { + n += jl_static_show_x_sym_escaped(out, sym); + if (globfunc) { + n += jl_printf(out, ")"); + if (quote) { n += jl_printf(out, ")"); - if (quote) - n += jl_printf(out, ")"); } } if (dv->parameters && (jl_value_t*)dv != dv->name->wrapper && @@ -832,7 +853,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, ")"); n += jl_printf(out, "<:"); } - n += jl_printf(out, "%s", jl_symbol_name(var->name)); + n += jl_static_show_x_sym_escaped(out, var->name); if (showbounds && (ub != (jl_value_t*)jl_any_type || lb != jl_bottom_type)) { // show type-var upper bound if it is defined, or if we showed the lower bound int ua = jl_is_unionall(ub); @@ -978,7 +999,38 @@ 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)) { + // v is function instance (an instance of a 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)) { + // typeof(v) isa DataType, so v is an *instance of* a type that is a Datatype, + // meaning v is e.g. an instance of a struct. These are printed as a call to a + // type constructor, such as e.g. `Base.UnitRange{Int64}(start=1, stop=2)` int istuple = jl_is_tuple_type(vt), isnamedtuple = jl_is_namedtuple_type(vt); size_t tlen = jl_datatype_nfields(vt); if (isnamedtuple) { diff --git a/test/show.jl b/test/show.jl index 046baf89a5d17..9c887a34be178 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1336,6 +1336,39 @@ end @test static_shown(QuoteNode(:x)) == ":(:x)" +# PR #38049 +@test static_shown(sum) == "Base.sum" +@test static_shown(+) == "Base.:(+)" +@test static_shown(typeof(+)) == "typeof(Base.:(+))" + +struct var"#X#" end +var"#f#"() = 2 +struct var"%X%" end # Invalid name without '#' + +# (Just to make this test more sustainable,) we don't necesssarily need to test the exact +# output format, just ensure that it prints at least the parts we expect: +@test occursin(".var\"#X#\"", static_shown(var"#X#")) # Leading `.` tests it printed a module name. +@test occursin(r"Set{var\"[^\"]+\"} where var\"[^\"]+\"", static_shown(Set{<:Any})) + +# Test that static_shown is returning valid, correct julia expressions +@testset "static_show() prints valid julia" begin + @testset for v in ( + var"#X#", + var"#X#"(), + 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 +end + # Test @show let fname = tempname() try