Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 861438c

Browse files
committedMay 30, 2020
make printing of UnionAll typevars shorter
fixes JuliaLang#34887 typevars in unionall type arguments can now be printed inlined if their type bounds allow to do so (e.g. `<:UB` or `>:LB`) and they are not referenced from elsewhere in the type hierarchy. Before: julia> Ref{<:Any} Ref{var"#s2"} where var"#s2" julia> Ref{<:Ref{<:Ref}} Ref{var"#s1"} where var"#s1"<:(Ref{var"#s2"} where var"#s2"<:Ref) After: julia> Ref{<:Any} Ref julia> Ref{<:Ref{<:Ref}} Ref{<:Ref{<:Ref}}
1 parent 84dd4dd commit 861438c

File tree

8 files changed

+190
-43
lines changed

8 files changed

+190
-43
lines changed
 

‎base/errorshow.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=()
404404
sig0 = method.sig
405405
while isa(sig0, UnionAll)
406406
push!(tv, sig0.var)
407-
iob = IOContext(iob, :unionall_env => sig0.var)
407+
iob = IOContext(iob, :tv_standalone => sig0.var)
408408
sig0 = sig0.body
409409
end
410410
s1 = sig0.parameters[1]

‎base/methodshow.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function arg_decl_parts(m::Method, html=false)
6161
if length(argnames) >= m.nargs
6262
show_env = ImmutableDict{Symbol, Any}()
6363
for t in tv
64-
show_env = ImmutableDict(show_env, :unionall_env => t)
64+
show_env = ImmutableDict(show_env, :tv_standalone => t)
6565
end
6666
decls = Tuple{String,String}[argtype_decl(show_env, argnames[i], sig, i, m.nargs, m.isva)
6767
for i = 1:m.nargs]
@@ -112,7 +112,7 @@ function show_method_params(io::IO, tv)
112112
end
113113
x = tv[i]
114114
show(io, x)
115-
io = IOContext(io, :unionall_env => x)
115+
io = IOContext(io, :tv_standalone => x)
116116
end
117117
print(io, "}")
118118
end

‎base/show.jl

+125-28
Original file line numberDiff line numberDiff line change
@@ -529,11 +529,81 @@ function show(io::IO, @nospecialize(x::Type))
529529
end
530530
end
531531

532+
# The `:unionall_tvs` io context records typevar naming data, i.e. whether
533+
# the typevar needs a corresponding `where` being printed or not.
534+
# It is being filled recursively here first before printing the typevar
535+
# itself.
536+
io = IOContext(io, :unionall_tvs => _unionall_tvs(io))
537+
_unionall_name_tvs(io, x)
538+
539+
# The `:unionall_env => tv::TypeVar` io context is used to signify that
540+
# io is happening within the unionall environment of `tv`.
532541
show(IOContext(io, :unionall_env => x.var), x.body)
533-
print(io, " where ")
534-
show(io, x.var)
542+
543+
if isnamed(io, x.var)
544+
print(io, " where ")
545+
show(io, x.var)
546+
end
535547
end
536548

549+
"""
550+
isgensym(x::Symbol)
551+
552+
Return whether `x` is an generated symbol, as generated by e.g.
553+
[`gensym`](@ref).
554+
"""
555+
isgensym(x::Symbol) = first(String(x)) == '#'
556+
557+
_unionall_tvs(io::IO) = get(io, :unionall_tvs, IdDict{TypeVar,Int}())
558+
isnamed(io::IO, tv::TypeVar) = get(_unionall_tvs(io), tv, 0) > 1
559+
setnamed(io::IO, tv::TypeVar) = setindex!(_unionall_tvs(io), 2, tv)
560+
561+
function _unionall_name_tvs(io::IO, @nospecialize x::UnionAll)
562+
# collect typevar naming candidates
563+
tvs = TypeVar[]
564+
y = x
565+
while y isa UnionAll
566+
# if it is named then this method was called already on a parent
567+
isnamed(io, y.var) && return io
568+
push!(tvs, y.var)
569+
y = y.body
570+
end
571+
572+
if y isa DataType
573+
# count typevar occurrences within the body recursively
574+
isempty(_unionall_tvs(io)) && _count_tvs!(_unionall_tvs(io), y)
575+
576+
for tv in reverse(collect(y.parameters))
577+
tv isa TypeVar || continue
578+
# might have been named above or earlier in the printing hierarchy
579+
isnamed(io, tv) && continue
580+
581+
if last(tvs) == tv && (tv.lb == Bottom || tv.ub == Any)
582+
# safe for elision
583+
pop!(tvs)
584+
isempty(tvs) && return io
585+
else
586+
setnamed(io, tv)
587+
end
588+
end
589+
end
590+
591+
# remaining ones need to be named, since anonymizing further nested
592+
# occurences is disallowed, includes e.g. y::Union
593+
foreach(tv -> isnamed(io, tv) || setnamed(io, tv), tvs)
594+
end
595+
596+
@nospecialize
597+
_count_tvs!(counts) = counts
598+
_count_tvs!(counts, x) = counts
599+
_count_tvs!(counts, x::TypeVar) =
600+
(setindex!(counts, get(counts, x, 0) + 1, x); _count_tvs!(counts, x.lb, x.ub))
601+
_count_tvs!(counts, x::UnionAll) = _count_tvs!(counts, x.var.lb, x.var.ub, x.body)
602+
_count_tvs!(counts, x::Union) = _count_tvs!(counts, x.a, x.b)
603+
_count_tvs!(counts, x::DataType) = _count_tvs!(counts, x.parameters...)
604+
_count_tvs!(counts, x1, x2, x...) = (_count_tvs!(counts, x1); _count_tvs!(counts, x2, x...))
605+
@specialize
606+
537607
# Check whether 'sym' (defined in module 'parent') is visible from module 'from'
538608
# If an object with this name exists in 'from', we need to check that it's the same binding
539609
# and that it's not deprecated.
@@ -596,12 +666,28 @@ function show_datatype(io::IO, x::DataType)
596666
print(io, "NTuple{", n, ',', x.parameters[1], "}")
597667
else
598668
show_type_name(io, x.name)
599-
# Do not print the type parameters for the primary type if we are
600-
# printing a method signature or type parameter.
601-
# Always print the type parameter if we are printing the type directly
602-
# since this information is still useful.
669+
670+
if !istuple
671+
# find out up until which parameter we need to print typevars
672+
y = x.name.wrapper
673+
n, j = 0, 0
674+
while y isa UnionAll
675+
j += 1
676+
tv = x.parameters[j]
677+
if !isa(tv, TypeVar) || isnamed(io, tv) || in(:tv_standalone => tv, io) ||
678+
# can only elide when typevar constraints are
679+
# the same as defined in the parametric type itself
680+
isa(tv.lb, TypeVar) || isa(tv.ub, TypeVar) ||
681+
!(tv.lb == y.var.lb) || !(tv.ub == y.var.ub)
682+
n = j
683+
end
684+
y = y.body
685+
end
686+
n == 0 && return
687+
end
688+
603689
print(io, '{')
604-
for (i, p) in enumerate(x.parameters)
690+
for (i, p) in enumerate(x.parameters[1:n])
605691
show(io, p)
606692
i < n && print(io, ',')
607693
end
@@ -1780,7 +1866,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type, demangle=false, kwa
17801866
env_io = io
17811867
while isa(sig, UnionAll)
17821868
push!(tv, sig.var)
1783-
env_io = IOContext(env_io, :unionall_env => sig.var)
1869+
env_io = IOContext(env_io, :tv_standalone => sig.var)
17841870
sig = sig.body
17851871
end
17861872
sig = sig.parameters
@@ -1837,37 +1923,48 @@ function ismodulecall(ex::Expr)
18371923
end
18381924

18391925
function show(io::IO, tv::TypeVar)
1840-
# If we are in the `unionall_env`, the type-variable is bound
1841-
# and the type constraints are already printed.
1842-
# We don't need to print it again.
1843-
# Otherwise, the lower bound should be printed if it is not `Bottom`
1844-
# and the upper bound should be printed if it is not `Any`.
1845-
in_env = (:unionall_env => tv) in io
18461926
function show_bound(io::IO, @nospecialize(b))
18471927
parens = isa(b,UnionAll) && !print_without_params(b)
18481928
parens && print(io, "(")
18491929
show(io, b)
18501930
parens && print(io, ")")
18511931
end
18521932
lb, ub = tv.lb, tv.ub
1853-
if !in_env && lb !== Bottom
1854-
if ub === Any
1855-
show_unquoted(io, tv.name)
1856-
print(io, ">:")
1857-
show_bound(io, lb)
1858-
else
1933+
1934+
# the `:tv_standalone => tv::TypeVar` io context tells to print the typevar
1935+
# `tv` without any typebounds, this is not necessarily related to being
1936+
# within a unionall environment
1937+
(:tv_standalone => tv) in io && return show_unquoted(io, tv.name)
1938+
1939+
if (:unionall_env => tv) in io
1940+
# within unionall type argument for `tv`
1941+
if isnamed(io, tv)
1942+
# named typevars get their constraints printed after `where`
1943+
return show_unquoted(io, tv.name)
1944+
elseif lb == Bottom
1945+
# anonymous upper bound, need to show `<:Any` even if trivial
1946+
return print(io, "<:", ub)
1947+
elseif ub == Any
1948+
# anonymous non-trivial lower bound
1949+
return print(io, ">:", lb)
1950+
end
1951+
@assert false
1952+
elseif lb != Bottom && ub == Any
1953+
show_unquoted(io, tv.name)
1954+
print(io, ">:")
1955+
return show_bound(io, lb)
1956+
else
1957+
if lb != Bottom
18591958
show_bound(io, lb)
18601959
print(io, "<:")
1861-
show_unquoted(io, tv.name)
18621960
end
1863-
else
18641961
show_unquoted(io, tv.name)
1962+
if ub != Any
1963+
print(io, "<:")
1964+
show_bound(io, ub)
1965+
end
18651966
end
1866-
if !in_env && ub !== Any
1867-
print(io, "<:")
1868-
show_bound(io, ub)
1869-
end
1870-
nothing
1967+
return nothing
18711968
end
18721969

18731970
module IRShow
@@ -2037,7 +2134,7 @@ function dump(io::IOContext, x::DataType, n::Int, indent)
20372134
# approximately recapture the list of tvar parameterization
20382135
# that may be used by the internal fields
20392136
if isa(tparam, TypeVar)
2040-
tvar_io = IOContext(tvar_io, :unionall_env => tparam)
2137+
tvar_io = IOContext(tvar_io, :tv_standalone => tparam)
20412138
end
20422139
end
20432140
if x.name === NamedTuple_typename && !(x.parameters[1] isa Tuple)

‎stdlib/InteractiveUtils/src/InteractiveUtils.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent)
298298
tp = t
299299
while true
300300
show(tvar_io, tp.var)
301-
tvar_io = IOContext(tvar_io, :unionall_env => tp.var)
301+
tvar_io = IOContext(tvar_io, :tv_standalone => tp.var)
302302
tp = tp.body
303303
if isa(tp, UnionAll)
304304
print(io, ", ")

‎stdlib/InteractiveUtils/test/runtests.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ end
5656
Base.getindex(A::Stable, i) = A.A[i]
5757
Base.getindex(A::Unstable, i) = A.A[i]
5858

59-
tag = "ARRAY{FLOAT64,N}"
59+
tag = "ARRAY{FLOAT64}"
6060
@test warntype_hastag(getindex, Tuple{Unstable{Float64},Int}, tag)
6161
@test !warntype_hastag(getindex, Tuple{Stable{Float64,2},Int}, tag)
6262
@test warntype_hastag(getindex, Tuple{Stable{Float64},Int}, tag)

‎test/errorshow.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ let err_str,
430430
@test startswith(sprint(show, which(FunctionLike(), Tuple{})),
431431
"(::$(curmod_prefix)FunctionLike)() in $curmod_str at $sp:$(method_defs_lineno + 7)")
432432
@test startswith(sprint(show, which(StructWithUnionAllMethodDefs{<:Integer}, (Any,))),
433-
"($(curmod_prefix)StructWithUnionAllMethodDefs{T} where T<:Integer)(x)")
433+
"($(curmod_prefix)StructWithUnionAllMethodDefs{<:Integer})(x)")
434434
@test repr("text/plain", FunctionLike()) == "(::$(curmod_prefix)FunctionLike) (generic function with 1 method)"
435435
@test repr("text/plain", Core.arraysize) == "arraysize (built-in function)"
436436

‎test/reflection.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ let ex = :(a + b)
214214
end
215215
foo13825(::Array{T, N}, ::Array, ::Vector) where {T, N} = nothing
216216
@test startswith(string(first(methods(foo13825))),
217-
"foo13825(::Array{T,N}, ::Array, ::Array{T,1} where T)")
217+
"foo13825(::Array{T,N}, ::Array, ::Array{<:Any,1})")
218218

219219
mutable struct TLayout
220220
x::Int8

‎test/show.jl

+58-8
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,8 @@ function + end
14721472
function == end
14731473
end
14741474

1475+
is_juliarepr(x) = eval(Meta.parse(repr(x))) == x
1476+
14751477
@testset "Parseable printing of types" begin
14761478
@test repr(typeof(print)) == "typeof(print)"
14771479
@test repr(typeof(Base.show_default)) == "typeof(Base.show_default)"
@@ -1490,12 +1492,60 @@ end
14901492
end
14911493

14921494
let x = TypeVar(:_), y = TypeVar(:_)
1493-
@test repr(UnionAll(x, UnionAll(y, Pair{x,y}))) == "Pair{_1,_2} where _2 where _1"
1494-
@test repr(UnionAll(x, UnionAll(y, Pair{UnionAll(x,Ref{x}),y}))) == "Pair{Ref{_1} where _1,_1} where _1"
1495+
@test repr(UnionAll(x, UnionAll(y, Pair{x,y}))) == "Pair"
1496+
@test repr(UnionAll(x, UnionAll(y, Pair{UnionAll(x,Ref{x}),y}))) == "Pair{Ref}"
14951497
x = TypeVar(:a)
14961498
y = TypeVar(:a)
14971499
z = TypeVar(:a)
1498-
@test repr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z})))) == "Tuple{a1,a2,a} where a2 where a1 where a"
1500+
@test repr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z})))) == "Tuple{<:Any,<:Any,a} where a"
1501+
end
1502+
1503+
@testset "unionall types" begin
1504+
X = TypeVar(gensym())
1505+
Y = TypeVar(gensym(), Ref, Ref)
1506+
x, y, z = TypeVar(:a), TypeVar(:a), TypeVar(:a)
1507+
struct TestTVUpper{A<:Integer} end
1508+
1509+
# named typevars
1510+
@test is_juliarepr(Ref{A} where A)
1511+
@test is_juliarepr(Ref{A} where A>:Ref)
1512+
@test is_juliarepr(Ref{A} where A<:Ref)
1513+
@test is_juliarepr(Ref{A} where Ref<:A<:Ref)
1514+
@test is_juliarepr(TestTVUpper{<:Real})
1515+
@test is_juliarepr(TestTVUpper{<:Integer})
1516+
@test is_juliarepr(TestTVUpper{<:Signed})
1517+
1518+
# typearg order
1519+
@test is_juliarepr(UnionAll(X, Pair{X,<:Any}))
1520+
@test is_juliarepr(UnionAll(X, Pair{<:Any,X}))
1521+
1522+
# duplicates
1523+
@test is_juliarepr(UnionAll(X, Pair{X,X}))
1524+
1525+
# nesting
1526+
@test is_juliarepr(UnionAll(X, Ref{Ref{X}}))
1527+
@test is_juliarepr(Union{T, Int} where T)
1528+
@test is_juliarepr(Pair{A,<:A} where A)
1529+
1530+
# renumbered typevars with same names
1531+
@test is_juliarepr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z}))))
1532+
1533+
# shortened typevar printing
1534+
@test repr(Ref{<:Any}) == "Ref"
1535+
@test repr(Pair{1,<:Any}) == "Pair{1}"
1536+
@test repr(Ref{<:Ref}) == "Ref{<:Ref}"
1537+
@test repr(Ref{>:Ref}) == "Ref{>:Ref}"
1538+
@test repr(Pair{<:Any,1}) == "Pair{<:Any,1}"
1539+
yname = sprint(Base.show_unquoted, Y.name)
1540+
@test repr(UnionAll(Y, Ref{Y})) == "Ref{$yname} where Ref<:$yname<:Ref"
1541+
@test endswith(repr(TestTVUpper{<:Real}), "TestTVUpper{<:Real}")
1542+
@test endswith(repr(TestTVUpper{<:Integer}), "TestTVUpper")
1543+
@test endswith(repr(TestTVUpper{<:Signed}), "TestTVUpper{<:Signed}")
1544+
1545+
# exception for tuples
1546+
@test is_juliarepr(Tuple)
1547+
@test is_juliarepr(Tuple{})
1548+
@test is_juliarepr(Tuple{<:Any})
14991549
end
15001550

15011551
@testset "showarg" begin
@@ -1564,7 +1614,7 @@ end
15641614
@test showstr([Float16(1)]) == "Float16[1.0]"
15651615
@test showstr([[Float16(1)]]) == "Array{Float16,1}[[1.0]]"
15661616
@test replstr(Real[Float16(1)]) == "1-element Array{Real,1}:\n Float16(1.0)"
1567-
@test replstr(Array{Real}[Real[1]]) == "1-element Array{Array{Real,N} where N,1}:\n [1]"
1617+
@test replstr(Array{Real}[Real[1]]) == "1-element Array{Array{Real},1}:\n [1]"
15681618
# printing tuples (Issue #25042)
15691619
@test replstr(fill((Int64(1), zeros(Float16, 3)), 1)) ==
15701620
"1-element Array{Tuple{Int64,Array{Float16,1}},1}:\n (1, [0.0, 0.0, 0.0])"
@@ -1591,7 +1641,7 @@ end
15911641
end
15921642

15931643
# issue #25857
1594-
@test repr([(1,),(1,2),(1,2,3)]) == "Tuple{$Int,Vararg{$Int,N} where N}[(1,), (1, 2), (1, 2, 3)]"
1644+
@test repr([(1,),(1,2),(1,2,3)]) == "Tuple{$Int,Vararg{$Int}}[(1,), (1, 2), (1, 2, 3)]"
15951645

15961646
# issues #25466 & #26256
15971647
@test replstr([:A => [1]]) == "1-element Array{Pair{Symbol,Array{$Int,1}},1}:\n :A => [1]"
@@ -1611,9 +1661,9 @@ end
16111661
@test replstr([[1.0]=>1.0]) == "1-element Array{Pair{Array{Float64,1},Float64},1}:\n [1.0] => 1.0"
16121662

16131663
# issue #28159
1614-
@test replstr([(a=1, b=2), (a=3,c=4)]) == "2-element Array{NamedTuple{names,Tuple{$Int,$Int}} where names,1}:\n (a = 1, b = 2)\n (a = 3, c = 4)"
1664+
@test replstr([(a=1, b=2), (a=3,c=4)]) == "2-element Array{NamedTuple{<:Any,Tuple{$Int,$Int}},1}:\n (a = 1, b = 2)\n (a = 3, c = 4)"
16151665

1616-
@test replstr(Vector[Any[1]]) == "1-element Array{Array{T,1} where T,1}:\n Any[1]"
1666+
@test replstr(Vector[Any[1]]) == "1-element Array{Array{<:Any,1},1}:\n Any[1]"
16171667
@test replstr(AbstractDict{Integer,Integer}[Dict{Integer,Integer}(1=>2)]) ==
16181668
"1-element Array{AbstractDict{Integer,Integer},1}:\n Dict(1 => 2)"
16191669

@@ -1834,7 +1884,7 @@ end
18341884

18351885
@testset """printing "Any" is not skipped with nested arrays""" begin
18361886
@test replstr(Union{X28004,Vector}[X28004(Any[X28004(1)])], :compact => true) ==
1837-
"1-element Array{Union{X28004, Array{T,1} where T},1}:\n X(Any[X(1)])"
1887+
"1-element Array{Union{X28004, Array{<:Any,1}},1}:\n X(Any[X(1)])"
18381888
end
18391889

18401890
# Issue 25589 - Underlines in cmd printing

0 commit comments

Comments
 (0)
Please sign in to comment.