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 ea51a06

Browse files
committedMay 5, 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`). typevars with named symbols are printed verbosely as before unless `:compact => true` is set in the IOContext. 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 a1f6448 commit ea51a06

File tree

5 files changed

+148
-34
lines changed

5 files changed

+148
-34
lines changed
 

Diff for: ‎base/errorshow.jl

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

Diff for: ‎base/methodshow.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function arg_decl_parts(m::Method)
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]
@@ -110,7 +110,7 @@ function show_method_params(io::IO, tv)
110110
end
111111
x = tv[i]
112112
show(io, x)
113-
io = IOContext(io, :unionall_env => x)
113+
io = IOContext(io, :tv_standalone => x)
114114
end
115115
print(io, "}")
116116
end

Diff for: ‎base/show.jl

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

532+
io = IOContext(io, :unionall_tvs => tvars(io))
533+
534+
_unionall_name_tvs(io, x)
532535
show(IOContext(io, :unionall_env => x.var), x.body)
533-
print(io, " where ")
534-
show(io, x.var)
536+
537+
if isnamed(io, x.var)
538+
print(io, " where ")
539+
show(io, x.var)
540+
end
535541
end
536542

543+
544+
tvars(io::IO) = get(io, :unionall_tvs, IdDict{TypeVar,Int}())
545+
546+
isgensym(x::Symbol) = first(String(x)) == '#'
547+
isnamed(io::IO, tv) = get(tvars(io), tv, 0) > 1
548+
setnamed(io::IO, tv) = setindex!(tvars(io), 2, tv)
549+
550+
function _unionall_name_tvs(io::IO, @nospecialize x::UnionAll)
551+
# collect typevar naming candidates
552+
tvs = TypeVar[]
553+
y = x
554+
while y isa UnionAll
555+
# if it is named then this method was called already on a parent
556+
isnamed(io, y.var) && return io
557+
push!(tvs, y.var)
558+
y = y.body
559+
end
560+
561+
if y isa DataType
562+
# count typevar occurrences within the body recursively
563+
isempty(tvars(io)) && _count_tvs!(tvars(io), y)
564+
565+
for tv in reverse(collect(y.parameters))
566+
tv isa TypeVar || continue
567+
# might have been named above or earlier in the printing hierarchy
568+
isnamed(io, tv) && continue
569+
570+
if last(tvs) == tv && (isgensym(tv.name) || (:compact => true) in io) &&
571+
(tv.lb == Bottom || tv.ub == Any)
572+
# safe for elision
573+
pop!(tvs)
574+
isempty(tvs) && return io
575+
else
576+
setnamed(io, tv)
577+
end
578+
end
579+
end
580+
581+
# remaining ones need to be named, since anonymizing further nested
582+
# occurences is disallowed, includes e.g. y::Union
583+
foreach(tv -> isnamed(io, tv) || setnamed(io, tv), tvs)
584+
end
585+
586+
@nospecialize
587+
_count_tvs!(counts) = counts
588+
_count_tvs!(counts, x) = counts
589+
_count_tvs!(counts, x::TypeVar) = setindex!(counts, get(counts, x, 0) + 1, x)
590+
_count_tvs!(counts, x::UnionAll) = _count_tvs!(counts, x.var.lb, x.var.ub, x.body)
591+
_count_tvs!(counts, x::Union) = _count_tvs!(counts, x.a, x.b)
592+
_count_tvs!(counts, x::DataType) = _count_tvs!(counts, x.parameters...)
593+
_count_tvs!(counts, x1, x2, x...) = (_count_tvs!(counts, x1); _count_tvs!(counts, x2, x...))
594+
@specialize
595+
537596
# Check whether 'sym' (defined in module 'parent') is visible from module 'from'
538597
# If an object with this name exists in 'from', we need to check that it's the same binding
539598
# and that it's not deprecated.
@@ -596,14 +655,19 @@ function show_datatype(io::IO, x::DataType)
596655
print(io, "NTuple{", n, ',', x.parameters[1], "}")
597656
else
598657
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.
658+
659+
# find the first type parameter that can not be elided when
660+
# printing
661+
j = istuple ? n : findlast(x.parameters) do tv
662+
return !isa(tv, TypeVar) || !isgensym(tv.name) || isnamed(io, tv) ||
663+
in(:tv_standalone => tv, io) || tv.lb != Bottom || tv.ub != Any
664+
end
665+
isnothing(j) && return
666+
603667
print(io, '{')
604-
for (i, p) in enumerate(x.parameters)
668+
for (i, p) in enumerate(x.parameters[1:j])
605669
show(io, p)
606-
i < n && print(io, ',')
670+
i < j && print(io, ',')
607671
end
608672
print(io, '}')
609673
end
@@ -1753,7 +1817,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type, demangle=false, kwa
17531817
env_io = io
17541818
while isa(sig, UnionAll)
17551819
push!(tv, sig.var)
1756-
env_io = IOContext(env_io, :unionall_env => sig.var)
1820+
env_io = IOContext(env_io, :tv_standalone => sig.var)
17571821
sig = sig.body
17581822
end
17591823
sig = sig.parameters
@@ -1821,37 +1885,45 @@ function ismodulecall(ex::Expr)
18211885
end
18221886

18231887
function show(io::IO, tv::TypeVar)
1824-
# If we are in the `unionall_env`, the type-variable is bound
1825-
# and the type constraints are already printed.
1826-
# We don't need to print it again.
1827-
# Otherwise, the lower bound should be printed if it is not `Bottom`
1828-
# and the upper bound should be printed if it is not `Any`.
1829-
in_env = (:unionall_env => tv) in io
18301888
function show_bound(io::IO, @nospecialize(b))
18311889
parens = isa(b,UnionAll) && !print_without_params(b)
18321890
parens && print(io, "(")
18331891
show(io, b)
18341892
parens && print(io, ")")
18351893
end
18361894
lb, ub = tv.lb, tv.ub
1837-
if !in_env && lb !== Bottom
1838-
if ub === Any
1839-
show_unquoted(io, tv.name)
1840-
print(io, ">:")
1841-
show_bound(io, lb)
1842-
else
1895+
1896+
(:tv_standalone => tv) in io && return show_unquoted(io, tv.name)
1897+
1898+
if (:unionall_env => tv) in io
1899+
# within unionall type argument for `tv`
1900+
if isnamed(io, tv)
1901+
# named typevars get their constraints printed after `where`
1902+
return show_unquoted(io, tv.name)
1903+
elseif lb == Bottom
1904+
# anonymous upper bound, need to show `<:Any` even if trivial
1905+
return print(io, "<:", ub)
1906+
elseif ub == Any
1907+
# anonymous non-trivial lower bound
1908+
return print(io, ">:", lb)
1909+
end
1910+
@assert false
1911+
elseif lb != Bottom && ub == Any
1912+
show_unquoted(io, tv.name)
1913+
print(io, ">:")
1914+
return show_bound(io, lb)
1915+
else
1916+
if lb != Bottom
18431917
show_bound(io, lb)
18441918
print(io, "<:")
1845-
show_unquoted(io, tv.name)
18461919
end
1847-
else
18481920
show_unquoted(io, tv.name)
1921+
if ub != Any
1922+
print(io, "<:")
1923+
show_bound(io, ub)
1924+
end
18491925
end
1850-
if !in_env && ub !== Any
1851-
print(io, "<:")
1852-
show_bound(io, ub)
1853-
end
1854-
nothing
1926+
return nothing
18551927
end
18561928

18571929
module IRShow
@@ -2007,7 +2079,7 @@ function dump(io::IOContext, x::DataType, n::Int, indent)
20072079
# approximately recapture the list of tvar parameterization
20082080
# that may be used by the internal fields
20092081
if isa(tparam, TypeVar)
2010-
tvar_io = IOContext(tvar_io, :unionall_env => tparam)
2082+
tvar_io = IOContext(tvar_io, :tv_standalone => tparam)
20112083
end
20122084
end
20132085
if x.name === NamedTuple_typename && !(x.parameters[1] isa Tuple)

Diff for: ‎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, ", ")

Diff for: ‎test/show.jl

+43-1
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)"
@@ -1498,6 +1500,46 @@ let x = TypeVar(:_), y = TypeVar(:_)
14981500
@test repr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z})))) == "Tuple{a1,a2,a} where a2 where a1 where a"
14991501
end
15001502

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+
1508+
# named typevars
1509+
@test is_juliarepr(Ref{A} where A)
1510+
@test is_juliarepr(Ref{A} where A>:Ref)
1511+
@test is_juliarepr(Ref{A} where A<:Ref)
1512+
@test is_juliarepr(Ref{A} where Ref<:A<:Ref)
1513+
1514+
# typearg order
1515+
@test is_juliarepr(UnionAll(X, Pair{X,<:Any}))
1516+
@test is_juliarepr(UnionAll(X, Pair{<:Any,X}))
1517+
1518+
# duplicates
1519+
@test is_juliarepr(UnionAll(X, Pair{X,X}))
1520+
1521+
# nesting
1522+
@test is_juliarepr(UnionAll(X, Ref{Ref{X}}))
1523+
@test is_juliarepr(Union{T, Int} where T)
1524+
1525+
# renumbered typevars with same names
1526+
@test is_juliarepr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z}))))
1527+
1528+
# shortened typevar printing
1529+
@test repr(Ref{<:Any}) == "Ref"
1530+
@test repr(Pair{1,<:Any}) == "Pair{1}"
1531+
@test repr(Ref{<:Ref}) == "Ref{<:Ref}"
1532+
@test repr(Ref{>:Ref}) == "Ref{>:Ref}"
1533+
@test repr(Pair{<:Any,1}) == "Pair{<:Any,1}"
1534+
yname = sprint(Base.show_unquoted, Y.name)
1535+
@test repr(UnionAll(Y, Ref{Y})) == "Ref{$yname} where Ref<:$yname<:Ref"
1536+
1537+
# exception for tuples
1538+
@test is_juliarepr(Tuple)
1539+
@test is_juliarepr(Tuple{})
1540+
@test is_juliarepr(Tuple{<:Any})
1541+
end
1542+
15011543
@testset "showarg" begin
15021544
A = reshape(Vector(Int16(1):Int16(2*3*5)), 2, 3, 5)
15031545
@test summary(A) == "2×3×5 Array{Int16,3}"
@@ -1834,7 +1876,7 @@ end
18341876

18351877
@testset """printing "Any" is not skipped with nested arrays""" begin
18361878
@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)])"
1879+
"1-element Array{Union{X28004, Array{<:Any,1}},1}:\n X(Any[X(1)])"
18381880
end
18391881

18401882
# Issue 25589 - Underlines in cmd printing

0 commit comments

Comments
 (0)
Please sign in to comment.