diff --git a/base/show.jl b/base/show.jl index 650a697316b9d..93026fa7e2fe7 100644 --- a/base/show.jl +++ b/base/show.jl @@ -532,8 +532,9 @@ function makeproper(io::IO, x::Type) push!(y, typ) end end - normal || (x = Union{y...}) - properx = rewrap_unionall(x, properx) + if !normal + properx = rewrap_unionall(Union{y...}, properx) + end end has_free_typevars(properx) && return Any return properx @@ -554,7 +555,7 @@ function make_typealias(@nospecialize(x::Type)) for name in names(mod) if isdefined(mod, name) && !isdeprecated(mod, name) && isconst(mod, name) alias = getfield(mod, name) - if alias isa Type && !has_free_typevars(alias) && !isvarargtype(alias) && !print_without_params(alias) && x <: alias + if alias isa Type && !has_free_typevars(alias) && !print_without_params(alias) && x <: alias if alias isa UnionAll (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), x, alias)::SimpleVector # ti === Union{} && continue # impossible, since we already checked that x <: alias @@ -580,8 +581,8 @@ function make_typealias(@nospecialize(x::Type)) applied = rewrap_unionall(applied, p) end has_free_typevars(applied) && continue - applied == x || continue # it couldn't figure out the parameter matching - elseif alias <: x + applied === x || continue # it couldn't figure out the parameter matching + elseif alias === x env = Core.svec() else continue # not a complete match @@ -596,7 +597,7 @@ function make_typealias(@nospecialize(x::Type)) end end -function show_typealias(io::IO, name::GlobalRef, x::Type, env::SimpleVector) +function show_typealias(io::IO, name::GlobalRef, x::Type, env::SimpleVector, wheres::Vector) if !(get(io, :compact, false)::Bool) # Print module prefix unless alias is visible from module passed to # IOContext. If :module is not set, default to Main. nothing can be used @@ -612,34 +613,70 @@ function show_typealias(io::IO, name::GlobalRef, x::Type, env::SimpleVector) n == 0 && return print(io, "{") - let io = IOContext(io) - for i = n:-1:1 - p = env[i] - if p isa TypeVar - io = IOContext(io, :unionall_env => p) + param_io = IOContext(io) + for i = 1:length(wheres) + p = wheres[i]::TypeVar + param_io = IOContext(param_io, :unionall_env => p) + end + for i = 1:n + p = env[i] + show(param_io, p) + i < n && print(io, ", ") + end + print(io, "}") +end + +function make_wheres(io::IO, env::SimpleVector, @nospecialize(x::Type)) + seen = IdSet() + wheres = TypeVar[] + # record things printed by the context + if io isa IOContext + for (key, val) in io.dict + if key === :unionall_env && val isa TypeVar && has_typevar(x, val) + push!(seen, val) end end - for i = 1:n - p = env[i] - show(io, p) - i < n && print(io, ", ") + end + # record things in x to print outermost + while x isa UnionAll + if !(x.var in seen) + push!(seen, x.var) + push!(wheres, x.var) end + x = x.body end - print(io, "}") - for i = n:-1:1 + # record remaining things in env to print innermost + for i = length(env):-1:1 p = env[i] - if p isa TypeVar && !io_has_tvar_name(io, p.name, x) - print(io, " where ") - show(io, p) + if p isa TypeVar && !(p in seen) + push!(seen, p) + pushfirst!(wheres, p) end end + return wheres +end + +function show_wheres(io::IO, env::Vector) + isempty(env) && return + io = IOContext(io) + n = length(env) + for i = 1:n + p = env[i]::TypeVar + print(io, n == 1 ? " where " : i == 1 ? " where {" : ", ") + show(io, p) + io = IOContext(io, :unionall_env => p) + end + n > 1 && print(io, "}") + nothing end function show_typealias(io::IO, x::Type) properx = makeproper(io, x) alias = make_typealias(properx) alias === nothing && return false - show_typealias(io, alias[1], x, alias[2]) + wheres = make_wheres(io, alias[2], x) + show_typealias(io, alias[1], x, alias[2], wheres) + show_wheres(io, wheres) return true end @@ -661,7 +698,7 @@ function make_typealiases(@nospecialize(x::Type)) for name in names(mod) if isdefined(mod, name) && !isdeprecated(mod, name) && isconst(mod, name) alias = getfield(mod, name) - if alias isa Type && !has_free_typevars(alias) && !isvarargtype(alias) && !print_without_params(alias) && !(alias <: Tuple) + if alias isa Type && !has_free_typevars(alias) && !print_without_params(alias) && !(alias <: Tuple) (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), x, alias)::SimpleVector ti === Union{} && continue mod in modulesof!(Set{Module}(), alias) || continue # make sure this alias wasn't from an unrelated part of the Union @@ -735,13 +772,17 @@ function show_unionaliases(io::IO, x::Union) end if first && length(aliases) == 1 alias = aliases[1] - show_typealias(io, alias[1], x, alias[2]) + wheres = make_wheres(io, alias[2], x) + show_typealias(io, alias[1], x, alias[2], wheres) + show_wheres(io, wheres) else for alias in aliases print(io, first ? "Union{" : ", ") first = false env = alias[2] - show_typealias(io, alias[1], x, alias[2]) + wheres = make_wheres(io, alias[2], x) + show_typealias(io, alias[1], x, alias[2], wheres) + show_wheres(io, wheres) end print(io, "}") end @@ -751,7 +792,7 @@ function show(io::IO, ::MIME"text/plain", @nospecialize(x::Type)) show(io, x) if !print_without_params(x) && get(io, :compact, true) properx = makeproper(io, x) - if make_typealias(properx) !== nothing || x <: make_typealiases(properx)[2] + if make_typealias(properx) !== nothing || (unwrap_unionall(x) isa Union && x <: make_typealiases(properx)[2]) print(io, " (alias for ") show(IOContext(io, :compact => false), x) print(io, ")") @@ -786,22 +827,30 @@ function show(io::IO, @nospecialize(x::Type)) end x = x::UnionAll - if x.var.name === :_ || io_has_tvar_name(io, x.var.name, x) - counter = 1 - while true - newname = Symbol(x.var.name, counter) - if !io_has_tvar_name(io, newname, x) - newtv = TypeVar(newname, x.var.lb, x.var.ub) - x = UnionAll(newtv, x{newtv}) - break + wheres = TypeVar[] + let io = IOContext(io) + while x isa UnionAll + var = x.var + if var.name === :_ || io_has_tvar_name(io, var.name, x) + counter = 1 + while true + newname = Symbol(var.name, counter) + if !io_has_tvar_name(io, newname, x) + var = TypeVar(newname, var.lb, var.ub) + x = x{var} + break + end + counter += 1 + end + else + x = x.body end - counter += 1 + push!(wheres, var) + io = IOContext(io, :unionall_env => var) end + show(io, x) end - - show(IOContext(io, :unionall_env => x.var), x.body) - print(io, " where ") - show(io, x.var) + show_wheres(io, wheres) end # Check whether 'sym' (defined in module 'parent') is visible from module 'from' diff --git a/test/show.jl b/test/show.jl index 3b918c064fcb0..65fd558cf9280 100644 --- a/test/show.jl +++ b/test/show.jl @@ -634,7 +634,7 @@ end # `where` syntax @test_repr "A where T<:B" @test_repr "A where T<:(Array{T} where T<:Real)" -@test_repr "Array{T} where T<:Array{S} where S<:Real" +@test_repr "Array{T} where {S<:Real, T<:Array{S}}" @test_repr "x::Array{T} where T" @test_repr "(a::b) where T" @test_repr "a::b where T" @@ -1568,12 +1568,12 @@ end end let x = TypeVar(:_), y = TypeVar(:_) - @test repr(UnionAll(x, UnionAll(y, Pair{x,y}))) == "Pair{_1, _2} where _2 where _1" + @test repr(UnionAll(x, UnionAll(y, Pair{x,y}))) == "Pair{_1, _2} where {_1, _2}" @test repr(UnionAll(x, UnionAll(y, Pair{UnionAll(x,Ref{x}),y}))) == "Pair{Ref{_1} where _1, _1} where _1" x = TypeVar(:a) y = TypeVar(:a) z = TypeVar(:a) - @test repr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z})))) == "Tuple{a1, a2, a} where a2 where a1 where a" + @test repr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z})))) == "Tuple{a1, a2, a} where {a, a1, a2}" end @testset "showarg" begin @@ -2070,15 +2070,21 @@ end end module M37012 +export AValue, B2 struct AnInteger{S<:Integer} end struct AStruct{N} end const AValue{S} = Union{AStruct{S}, AnInteger{S}} +struct BStruct{T,S} end +const B2{S,T} = BStruct{T,S} end @test Base.make_typealias(M37012.AStruct{1}) === nothing @test isempty(Base.make_typealiases(M37012.AStruct{1})[1]) @test string(M37012.AStruct{1}) == "$(curmod_prefix)M37012.AStruct{1}" @test string(Union{Nothing, Number, Vector}) == "Union{Nothing, Number, Vector{T} where T}" @test string(Union{Nothing, AbstractVecOrMat}) == "Union{Nothing, AbstractVecOrMat{T} where T}" +@test string(M37012.BStruct{T, T} where T) == "$(curmod_prefix)M37012.B2{T, T} where T" +@test string(M37012.BStruct{T, S} where {T<:Unsigned, S<:Signed}) == "$(curmod_prefix)M37012.B2{S, T} where {T<:Unsigned, S<:Signed}" +@test string(M37012.BStruct{T, S} where {T<:Signed, S<:T}) == "$(curmod_prefix)M37012.B2{S, T} where {T<:Signed, S<:T}" @test sprint(show, :(./)) == ":((./))" @test sprint(show, :((.|).(.&, b))) == ":((.|).((.&), b))"