Skip to content

Commit ddd6afb

Browse files
committed
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 ddd6afb

File tree

11 files changed

+204
-56
lines changed

11 files changed

+204
-56
lines changed

base/docs/basedocs.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -2320,7 +2320,7 @@ arguments accepted by varargs methods (see the section on [Varargs Functions](@r
23202320
# Examples
23212321
```jldoctest
23222322
julia> mytupletype = Tuple{AbstractString,Vararg{Int}}
2323-
Tuple{AbstractString,Vararg{Int64,N} where N}
2323+
Tuple{AbstractString,Vararg{Int64}}
23242324
23252325
julia> isa(("1",), mytupletype)
23262326
true

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

+135-37
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,29 @@ 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) ||
678+
in(:tv_standalone => tv, io) || !in(:unionall_env => tv, io) ||
679+
# can only elide when typevar constraints are
680+
# the same as defined in the parametric type itself
681+
isa(tv.lb, TypeVar) || isa(tv.ub, TypeVar) ||
682+
!(tv.lb == y.var.lb) || !(tv.ub == y.var.ub)
683+
n = j
684+
end
685+
y = y.body
686+
end
687+
n == 0 && return
688+
end
689+
603690
print(io, '{')
604-
for (i, p) in enumerate(x.parameters)
691+
for (i, p) in enumerate(x.parameters[1:n])
605692
show(io, p)
606693
i < n && print(io, ',')
607694
end
@@ -1780,7 +1867,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type, demangle=false, kwa
17801867
env_io = io
17811868
while isa(sig, UnionAll)
17821869
push!(tv, sig.var)
1783-
env_io = IOContext(env_io, :unionall_env => sig.var)
1870+
env_io = IOContext(env_io, :tv_standalone => sig.var)
17841871
sig = sig.body
17851872
end
17861873
sig = sig.parameters
@@ -1837,37 +1924,48 @@ function ismodulecall(ex::Expr)
18371924
end
18381925

18391926
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
18461927
function show_bound(io::IO, @nospecialize(b))
18471928
parens = isa(b,UnionAll) && !print_without_params(b)
18481929
parens && print(io, "(")
18491930
show(io, b)
18501931
parens && print(io, ")")
18511932
end
18521933
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
1934+
1935+
# the `:tv_standalone => tv::TypeVar` io context tells to print the typevar
1936+
# `tv` without any typebounds, this is not necessarily related to being
1937+
# within a unionall environment
1938+
(:tv_standalone => tv) in io && return show_unquoted(io, tv.name)
1939+
1940+
if (:unionall_env => tv) in io
1941+
# within unionall type argument for `tv`
1942+
if isnamed(io, tv)
1943+
# named typevars get their constraints printed after `where`
1944+
return show_unquoted(io, tv.name)
1945+
elseif lb == Bottom
1946+
# anonymous upper bound, need to show `<:Any` even if trivial
1947+
return print(io, "<:", ub)
1948+
elseif ub == Any
1949+
# anonymous non-trivial lower bound
1950+
return print(io, ">:", lb)
1951+
end
1952+
@assert false
1953+
elseif lb != Bottom && ub == Any
1954+
show_unquoted(io, tv.name)
1955+
print(io, ">:")
1956+
return show_bound(io, lb)
1957+
else
1958+
if lb != Bottom
18591959
show_bound(io, lb)
18601960
print(io, "<:")
1861-
show_unquoted(io, tv.name)
18621961
end
1863-
else
18641962
show_unquoted(io, tv.name)
1963+
if ub != Any
1964+
print(io, "<:")
1965+
show_bound(io, ub)
1966+
end
18651967
end
1866-
if !in_env && ub !== Any
1867-
print(io, "<:")
1868-
show_bound(io, ub)
1869-
end
1870-
nothing
1968+
return nothing
18711969
end
18721970

18731971
module IRShow
@@ -2027,19 +2125,19 @@ end
20272125

20282126
# Types
20292127
function dump(io::IOContext, x::DataType, n::Int, indent)
2030-
print(io, x)
2128+
tvar_io = io
2129+
for tparam in x.parameters
2130+
# approximately recapture the list of tvar parameterization
2131+
# that may be used by the internal fields
2132+
if isa(tparam, TypeVar)
2133+
tvar_io = IOContext(tvar_io, :tv_standalone => tparam)
2134+
end
2135+
end
2136+
print(tvar_io, x)
20312137
if x !== Any
2032-
print(io, " <: ", supertype(x))
2138+
print(tvar_io, " <: ", supertype(x))
20332139
end
20342140
if n > 0 && !(x <: Tuple) && !x.abstract
2035-
tvar_io::IOContext = io
2036-
for tparam in x.parameters
2037-
# approximately recapture the list of tvar parameterization
2038-
# that may be used by the internal fields
2039-
if isa(tparam, TypeVar)
2040-
tvar_io = IOContext(tvar_io, :unionall_env => tparam)
2041-
end
2042-
end
20432141
if x.name === NamedTuple_typename && !(x.parameters[1] isa Tuple)
20442142
# named tuple type with unknown field names
20452143
return

doc/src/devdocs/types.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ julia> Tuple
252252
Tuple
253253
254254
julia> Tuple.parameters
255-
svec(Vararg{Any,N} where N)
255+
svec(Vararg{Any})
256256
```
257257

258258
Unlike other types, tuple types are covariant in their parameters, so this definition permits

doc/src/manual/types.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ of trailing elements:
901901

902902
```jldoctest
903903
julia> mytupletype = Tuple{AbstractString,Vararg{Int}}
904-
Tuple{AbstractString,Vararg{Int64,N} where N}
904+
Tuple{AbstractString,Vararg{Int64}}
905905
906906
julia> isa(("1",), mytupletype)
907907
true
@@ -1089,7 +1089,7 @@ consider the two types created by the following declarations:
10891089

10901090
```jldoctest
10911091
julia> const T1 = Array{Array{T,1} where T, 1}
1092-
Array{Array{T,1} where T,1}
1092+
Array{Array{<:Any,1},1}
10931093
10941094
julia> const T2 = Array{Array{T,1}, 1} where T
10951095
Array{Array{T,1},1} where T

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

0 commit comments

Comments
 (0)