From f4ac759e20bfdb4e994f13690401297fbeba681b Mon Sep 17 00:00:00 2001 From: pchintalapudi <34727397+pchintalapudi@users.noreply.github.com> Date: Tue, 15 Aug 2023 15:07:47 +0000 Subject: [PATCH 1/3] inference: apply tmerge limit elementwise to the Union This allows forming larger unions, as long as each element in the Union is both relatively distinct and relatively simple. For example: tmerge(Base.BitSigned, Nothing) == Union{Nothing, Int128, Int16, Int32, Int64, Int8} tmerge(Tuple{Base.BitSigned, Int}, Nothing) == Union{Nothing, Tuple{Any, Int64}} tmerge(AbstractVector{Int}, Vector) == AbstractVector Disables a test from dc8d88559c8293dd6450ef6b1be0b68677750054. Co-authored-by: Oscar Smith --- base/compiler/typelimits.jl | 69 +++++++++++++++++++++++++++---------- test/compiler/inference.jl | 47 ++++++++++++++----------- 2 files changed, 78 insertions(+), 38 deletions(-) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index d1e08dd1e1676..2bcdbbadd44a4 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -692,6 +692,33 @@ end return tmerge_types_slow(typea, typeb) end +@nospecializeinfer @noinline function tname_intersect(aname::Core.TypeName, bname::Core.TypeName) + aname === bname && return aname + if !isabstracttype(aname.wrapper) && !isabstracttype(bname.wrapper) + return nothing # fast path + end + Any.name === aname && return aname + a = unwrap_unionall(aname.wrapper) + heighta = 0 + while a !== Any + heighta += 1 + a = a.super + end + b = unwrap_unionall(bname.wrapper) + heightb = 0 + while b !== Any + b.name === aname && return aname + heightb += 1 + b = b.super + end + a = unwrap_unionall(aname.wrapper) + while heighta > heightb + a = a.super + heighta -= 1 + end + return a.name === bname ? bname : nothing +end + @nospecializeinfer @noinline function tmerge_types_slow(@nospecialize(typea::Type), @nospecialize(typeb::Type)) # collect the list of types from past tmerge calls returning Union # and then reduce over that list @@ -716,9 +743,12 @@ end # in which case, simplify this tmerge by replacing it with # the widest possible version of itself (the wrapper) for i in 1:length(types) + typenames[i] === Any.name && continue ti = types[i] for j in (i + 1):length(types) - if typenames[i] === typenames[j] + typenames[j] === Any.name && continue + ijname = tname_intersect(typenames[i], typenames[j]) + if !(ijname === nothing) tj = types[j] if ti <: tj types[i] = Union{} @@ -728,27 +758,33 @@ end types[j] = Union{} typenames[j] = Any.name else - if typenames[i] === Tuple.name + if ijname === Tuple.name # try to widen Tuple slower: make a single non-concrete Tuple containing both # converge the Tuple element-wise if they are the same length # see 4ee2b41552a6bc95465c12ca66146d69b354317b, be59686f7613a2ccfd63491c7b354d0b16a95c05, widen = tuplemerge(unwrap_unionall(ti)::DataType, unwrap_unionall(tj)::DataType) widen = rewrap_unionall(rewrap_unionall(widen, ti), tj) else - wr = typenames[i].wrapper + wr = ijname.wrapper uw = unwrap_unionall(wr)::DataType ui = unwrap_unionall(ti)::DataType + while ui.name !== ijname + ui = ui.super + end uj = unwrap_unionall(tj)::DataType - merged = wr + while uj.name !== ijname + uj = uj.super + end + merged = Vector{Any}(undef, length(uw.parameters)) for k = 1:length(uw.parameters) ui_k = ui.parameters[k] if ui_k === uj.parameters[k] && !has_free_typevars(ui_k) - merged = merged{ui_k} + merged[k] = ui_k else - merged = merged{uw.parameters[k]} + merged[k] = uw.parameters[k] end end - widen = rewrap_unionall(merged, wr) + widen = rewrap_unionall(wr{merged...}, wr) end types[i] = Union{} typenames[i] = Any.name @@ -758,14 +794,12 @@ end end end end - u = Union{types...} - # don't let type unions get too big, if the above didn't reduce it enough - if issimpleenoughtype(u) - return u - end - # don't let the slow widening of Tuple cause the whole type to grow too fast + # don't let elements of the union get too big, if the above didn't reduce something # Specifically widen Tuple{..., Union{lots of stuff}...} to Tuple{..., Any, ...} for i in 1:length(types) + # this element is too complicated, so + # just return the widest possible type now + issimpleenoughtype(types[i]) && continue if typenames[i] === Tuple.name ti = types[i] tip = (unwrap_unionall(types[i])::DataType).parameters @@ -773,16 +807,15 @@ end p = Vector{Any}(undef, lt) for j = 1:lt ui = tip[j] - p[j] = (unioncomplexity(ui)==0) ? ui : isvarargtype(ui) ? Vararg : Any + p[j] = issimpleenoughtype(unwrapva(ui)) ? ui : isvarargtype(ui) ? Vararg : Any end types[i] = rewrap_unionall(Tuple{p...}, ti) + else + issimpleenoughtype(types[i]) || return Any end end u = Union{types...} - if issimpleenoughtype(u) - return u - end - return Any + return u end # the inverse of switchtupleunion, with limits on max element union size diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 8c0c996f97738..0ff885409dae0 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -164,7 +164,7 @@ Base.ndims(g::e43296) = ndims(typeof(g)) # PR 22120 -function tmerge_test(a, b, r, commutative=true) +function tuplemerge_test(a, b, r, commutative=true) @test r == Core.Compiler.tuplemerge(a, b) if commutative @test r == Core.Compiler.tuplemerge(b, a) @@ -172,32 +172,32 @@ function tmerge_test(a, b, r, commutative=true) @test_broken r == Core.Compiler.tuplemerge(b, a) end end -tmerge_test(Tuple{Int}, Tuple{String}, Tuple{Union{Int, String}}) -tmerge_test(Tuple{Int}, Tuple{String, String}, Tuple) -tmerge_test(Tuple{Vararg{Int}}, Tuple{String}, Tuple) -tmerge_test(Tuple{Int}, Tuple{Int, Int}, +tuplemerge_test(Tuple{Int}, Tuple{String}, Tuple{Union{Int, String}}) +tuplemerge_test(Tuple{Int}, Tuple{String, String}, Tuple) +tuplemerge_test(Tuple{Vararg{Int}}, Tuple{String}, Tuple) +tuplemerge_test(Tuple{Int}, Tuple{Int, Int}, Tuple{Vararg{Int}}) -tmerge_test(Tuple{Integer}, Tuple{Int, Int}, +tuplemerge_test(Tuple{Integer}, Tuple{Int, Int}, Tuple{Vararg{Integer}}) -tmerge_test(Tuple{}, Tuple{Int, Int}, +tuplemerge_test(Tuple{}, Tuple{Int, Int}, Tuple{Vararg{Int}}) -tmerge_test(Tuple{}, Tuple{Complex}, +tuplemerge_test(Tuple{}, Tuple{Complex}, Tuple{Vararg{Complex}}) -tmerge_test(Tuple{ComplexF32}, Tuple{ComplexF32, ComplexF64}, +tuplemerge_test(Tuple{ComplexF32}, Tuple{ComplexF32, ComplexF64}, Tuple{Vararg{Complex}}) -tmerge_test(Tuple{Vararg{ComplexF32}}, Tuple{Vararg{ComplexF64}}, +tuplemerge_test(Tuple{Vararg{ComplexF32}}, Tuple{Vararg{ComplexF64}}, Tuple{Vararg{Complex}}) -tmerge_test(Tuple{}, Tuple{ComplexF32, Vararg{Union{ComplexF32, ComplexF64}}}, +tuplemerge_test(Tuple{}, Tuple{ComplexF32, Vararg{Union{ComplexF32, ComplexF64}}}, Tuple{Vararg{Union{ComplexF32, ComplexF64}}}) -tmerge_test(Tuple{ComplexF32}, Tuple{ComplexF32, Vararg{Union{ComplexF32, ComplexF64}}}, +tuplemerge_test(Tuple{ComplexF32}, Tuple{ComplexF32, Vararg{Union{ComplexF32, ComplexF64}}}, Tuple{Vararg{Union{ComplexF32, ComplexF64}}}) -tmerge_test(Tuple{ComplexF32, ComplexF32, ComplexF32}, Tuple{ComplexF32, Vararg{Union{ComplexF32, ComplexF64}}}, +tuplemerge_test(Tuple{ComplexF32, ComplexF32, ComplexF32}, Tuple{ComplexF32, Vararg{Union{ComplexF32, ComplexF64}}}, Tuple{Vararg{Union{ComplexF32, ComplexF64}}}) -tmerge_test(Tuple{}, Tuple{Union{ComplexF64, ComplexF32}, Vararg{Union{ComplexF32, ComplexF64}}}, +tuplemerge_test(Tuple{}, Tuple{Union{ComplexF64, ComplexF32}, Vararg{Union{ComplexF32, ComplexF64}}}, Tuple{Vararg{Union{ComplexF32, ComplexF64}}}) -tmerge_test(Tuple{ComplexF64, ComplexF64, ComplexF32}, Tuple{Vararg{Union{ComplexF32, ComplexF64}}}, +tuplemerge_test(Tuple{ComplexF64, ComplexF64, ComplexF32}, Tuple{Vararg{Union{ComplexF32, ComplexF64}}}, Tuple{Vararg{Complex}}, false) -tmerge_test(Tuple{}, Tuple{Complex, Vararg{Union{ComplexF32, ComplexF64}}}, +tuplemerge_test(Tuple{}, Tuple{Complex, Vararg{Union{ComplexF32, ComplexF64}}}, Tuple{Vararg{Complex}}) @test Core.Compiler.tmerge(Tuple{}, Union{Nothing, Tuple{ComplexF32, ComplexF32}}) == Union{Nothing, Tuple{}, Tuple{ComplexF32, ComplexF32}} @@ -215,8 +215,15 @@ tmerge_test(Tuple{}, Tuple{Complex, Vararg{Union{ComplexF32, ComplexF64}}}, @test Core.Compiler.tmerge(Core.Compiler.fallback_ipo_lattice, Core.Compiler.InterConditional(1, Int, Union{}), Core.Compiler.InterConditional(2, String, Union{})) === Core.Compiler.Const(true) # test issue behind https://github.com/JuliaLang/julia/issues/50458 @test Core.Compiler.tmerge(Nothing, Tuple{Base.BitInteger, Int}) == Union{Nothing, Tuple{Any, Int}} -@test Core.Compiler.tmerge(Nothing, Tuple{Union{Char, String, SubString{String}, Symbol}, Int}) == Union{Nothing, Tuple{Any, Int}} +@test Core.Compiler.tmerge(Nothing, Tuple{Union{Char, String, SubString{String}, Symbol}, Int}) == Union{Nothing, Tuple{Union{Char, String, SubString{String}, Symbol}, Int}} @test Core.Compiler.tmerge(Nothing, Tuple{Integer, Int}) == Union{Nothing, Tuple{Integer, Int}} +@test Core.Compiler.tmerge(Union{Nothing, AbstractVector{Int}}, Vector) == Union{Nothing, AbstractVector} +@test Core.Compiler.tmerge(Union{Nothing, AbstractVector{Int}}, Matrix) == Union{Nothing, AbstractArray} +@test Core.Compiler.tmerge(Union{Nothing, AbstractVector{Int}}, Matrix{Int}) == Union{Nothing, AbstractArray{Int}} +@test Core.Compiler.tmerge(Union{Nothing, AbstractVector{Int}}, Array) == Union{Nothing, AbstractArray} +@test Core.Compiler.tmerge(Union{Nothing, AbstractArray{Int}}, Vector) == Union{Nothing, AbstractArray} +@test Core.Compiler.tmerge(Union{Nothing, AbstractVector}, Matrix{Int}) == Union{Nothing, AbstractArray} +@test Core.Compiler.tmerge(Union{Nothing, AbstractFloat}, Integer) == Union{Nothing, AbstractFloat, Integer} # test that recursively more complicated types don't widen all the way to Any when there is a useful valid type upper bound # Specificially test with base types of a trivial type, a simple union, a complicated union, and a tuple. @@ -2886,7 +2893,7 @@ end # issue #27316 - inference shouldn't hang on these f27316(::Vector) = nothing f27316(::Any) = f27316(Any[][1]), f27316(Any[][1]) -let expected = NTuple{2, Union{Nothing, NTuple{2, Union{Nothing, Tuple{Any, Any}}}}} +let expected = NTuple{2, Union{Nothing, Tuple{Any, Any}}} @test Tuple{Nothing, Nothing} <: only(Base.return_types(f27316, Tuple{Int})) == expected # we may be able to improve this bound in the future end function g27316() @@ -3501,8 +3508,8 @@ function pickvarnames(x::Vector{Any}) end @test pickvarnames(:a) === :a @test pickvarnames(Any[:a, :b]) === (:a, :b) -@test only(Base.return_types(pickvarnames, (Vector{Any},))) == Tuple{Vararg{Union{Symbol, Tuple}}} -@test only(Base.code_typed(pickvarnames, (Vector{Any},), optimize=false))[2] == Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple}}}}}} +@test only(Base.return_types(pickvarnames, (Vector{Any},))) == Tuple +@test only(Base.code_typed(pickvarnames, (Vector{Any},), optimize=false))[2] == Tuple{Vararg{Union{Symbol, Tuple}}} @test map(>:, [Int], [Int]) == [true] From bae558c8113d55560a672e631a3fd5afd12e3565 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 12 Sep 2023 20:05:17 +0000 Subject: [PATCH 2/3] fix problems exposed by previous commit and do a few small optimizations --- base/compiler/abstractinterpretation.jl | 6 +-- base/compiler/inferencestate.jl | 7 +--- base/compiler/typelimits.jl | 50 +++++++++++++++++++------ base/compiler/typeutils.jl | 4 +- test/compiler/inference.jl | 32 +++++++++++++--- 5 files changed, 70 insertions(+), 29 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 29dbd0e8c05f8..6aae1d6b384e8 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -3123,11 +3123,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) ssavaluetypes[currpc] = Any continue end - if !isempty(frame.ssavalue_uses[currpc]) - record_ssa_assign!(๐•ƒแตข, currpc, type, frame) - else - ssavaluetypes[currpc] = type - end + record_ssa_assign!(๐•ƒแตข, currpc, type, frame) end # for currpc in bbstart:bbend # Case 1: Fallthrough termination diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 9571eff9134d6..c3535eb5882ec 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -584,11 +584,8 @@ _topmod(sv::InferenceState) = _topmod(frame_module(sv)) function record_ssa_assign!(๐•ƒแตข::AbstractLattice, ssa_id::Int, @nospecialize(new), frame::InferenceState) ssavaluetypes = frame.ssavaluetypes old = ssavaluetypes[ssa_id] - if old === NOT_FOUND || !โŠ‘(๐•ƒแตข, new, old) - # typically, we expect that old โŠ‘ new (that output information only - # gets less precise with worse input information), but to actually - # guarantee convergence we need to use tmerge here to ensure that is true - ssavaluetypes[ssa_id] = old === NOT_FOUND ? new : tmerge(๐•ƒแตข, old, new) + if old === NOT_FOUND || !is_lattice_equal(๐•ƒแตข, new, old) + ssavaluetypes[ssa_id] = new W = frame.ip for r in frame.ssavalue_uses[ssa_id] if was_reached(frame, r) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 2bcdbbadd44a4..5d7f6890926e4 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -305,10 +305,17 @@ union_count_abstract(x::Union) = union_count_abstract(x.a) + union_count_abstrac union_count_abstract(@nospecialize(x)) = !isdispatchelem(x) function issimpleenoughtype(@nospecialize t) + ut = unwrap_unionall(t) + ut isa DataType && ut.name.wrapper == t && return true return unionlen(t) + union_count_abstract(t) <= MAX_TYPEUNION_LENGTH && unioncomplexity(t) <= MAX_TYPEUNION_COMPLEXITY end +# We may want to apply a stricter limit than issimpleenoughtype to +# tupleelements individually, to try to keep the whole tuple under the limit, +# even after complicated recursion and other operations on it elsewhere +const issimpleenoughtupleelem = issimpleenoughtype + # A simplified type_more_complex query over the extended lattice # (assumes typeb โŠ‘ typea) @nospecializeinfer function issimplertype(๐•ƒ::AbstractLattice, @nospecialize(typea), @nospecialize(typeb)) @@ -742,6 +749,7 @@ end # see if any of the union elements have the same TypeName # in which case, simplify this tmerge by replacing it with # the widest possible version of itself (the wrapper) + havetuples = false for i in 1:length(types) typenames[i] === Any.name && continue ti = types[i] @@ -764,6 +772,7 @@ end # see 4ee2b41552a6bc95465c12ca66146d69b354317b, be59686f7613a2ccfd63491c7b354d0b16a95c05, widen = tuplemerge(unwrap_unionall(ti)::DataType, unwrap_unionall(tj)::DataType) widen = rewrap_unionall(rewrap_unionall(widen, ti), tj) + havetuples = true else wr = ijname.wrapper uw = unwrap_unionall(wr)::DataType @@ -776,15 +785,20 @@ end uj = uj.super end merged = Vector{Any}(undef, length(uw.parameters)) + use_merged = true + widen = wr for k = 1:length(uw.parameters) ui_k = ui.parameters[k] if ui_k === uj.parameters[k] && !has_free_typevars(ui_k) merged[k] = ui_k + use_merged = true else merged[k] = uw.parameters[k] end end - widen = rewrap_unionall(wr{merged...}, wr) + if use_merged + widen = rewrap_unionall(wr{merged...}, wr) + end end types[i] = Union{} typenames[i] = Any.name @@ -796,22 +810,28 @@ end end # don't let elements of the union get too big, if the above didn't reduce something # Specifically widen Tuple{..., Union{lots of stuff}...} to Tuple{..., Any, ...} + # Even if there was only one element of the base type, don't let Val{<:Val{<:Val}} keep nesting abstract levels for i in 1:length(types) # this element is too complicated, so # just return the widest possible type now - issimpleenoughtype(types[i]) && continue - if typenames[i] === Tuple.name - ti = types[i] - tip = (unwrap_unionall(types[i])::DataType).parameters + tn = typenames[i] + ti = types[i] + if tn === Any.name || issimpleenoughtype(ti) + continue + elseif tn === Tuple.name + havetuples && continue # multiple-tuples case was already handled by tuplemerge above + # otherwise we need to do a simple version of tuplemerge for one element now + tip = (unwrap_unionall(ti)::DataType).parameters lt = length(tip) p = Vector{Any}(undef, lt) for j = 1:lt ui = tip[j] - p[j] = issimpleenoughtype(unwrapva(ui)) ? ui : isvarargtype(ui) ? Vararg : Any + p[j] = issimpleenoughtupleelem(unwrapva(ui)) ? ui : isvarargtype(ui) ? Vararg : Any end types[i] = rewrap_unionall(Tuple{p...}, ti) else - issimpleenoughtype(types[i]) || return Any + # this element is not simple enough yet, make it so now + types[i] = tn.wrapper end end u = Union{types...} @@ -837,7 +857,7 @@ function tuplemerge(a::DataType, b::DataType) p = Vector{Any}(undef, lt + vt) for i = 1:lt ui = Union{ap[i], bp[i]} - p[i] = issimpleenoughtype(ui) ? ui : Any + p[i] = issimpleenoughtupleelem(ui) ? ui : Any end # merge the remaining tail into a single, simple Tuple{Vararg{T}} (#22120) if vt @@ -855,8 +875,9 @@ function tuplemerge(a::DataType, b::DataType) # or (equivalently?) iteratively took super-types until reaching a common wrapper # e.g. consider the results of `tuplemerge(Tuple{Complex}, Tuple{Number, Int})` and of # `tuplemerge(Tuple{Int}, Tuple{String}, Tuple{Int, String})` - if !(ti <: tail) - if tail <: ti + hasfree = has_free_typevars(ti) + if hasfree || !(ti <: tail) + if !hasfree && tail <: ti tail = ti # widen to ti else uw = unwrap_unionall(tail) @@ -884,11 +905,16 @@ function tuplemerge(a::DataType, b::DataType) end end end - tail === Any && return Tuple # short-circuit loop + tail === Any && return Tuple # short-circuit loops end end @assert !(tail === Union{}) - p[lt + 1] = Vararg{tail} + if !issimpleenoughtupleelem(tail) || tail === Any + p[lt + 1] = Vararg + lt == 0 && return Tuple + else + p[lt + 1] = Vararg{tail} + end end return Tuple{p...} end diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 038e5f8abdf88..22952961b2484 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -291,7 +291,7 @@ end unioncomplexity(@nospecialize x) = _unioncomplexity(x)::Int function _unioncomplexity(@nospecialize x) if isa(x, DataType) - x.name === Tuple.name || isvarargtype(x) || return 0 + x.name === Tuple.name || return 0 c = 0 for ti in x.parameters c = max(c, unioncomplexity(ti)) @@ -302,7 +302,7 @@ function _unioncomplexity(@nospecialize x) elseif isa(x, UnionAll) return max(unioncomplexity(x.body), unioncomplexity(x.var.ub)) elseif isa(x, TypeofVararg) - return isdefined(x, :T) ? unioncomplexity(x.T) : 0 + return isdefined(x, :T) ? unioncomplexity(x.T) + 1 : 1 else return 0 end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 0ff885409dae0..e6eeeb81a2647 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -156,11 +156,11 @@ Base.ndims(g::e43296) = ndims(typeof(g)) @test Core.Compiler.unioncomplexity(Tuple{Union{Int8, Int16, Int32, Int64}}) == 3 @test Core.Compiler.unioncomplexity(Union{Int8, Int16, Int32, T} where T) == 3 @test Core.Compiler.unioncomplexity(Tuple{Val{T}, Union{Int8, Int16}, Int8} where T<:Union{Int8, Int16, Int32, Int64}) == 3 -@test Core.Compiler.unioncomplexity(Tuple{Vararg{Tuple{Union{Int8, Int16}}}}) == 1 -@test Core.Compiler.unioncomplexity(Tuple{Vararg{Symbol}}) == 0 -@test Core.Compiler.unioncomplexity(Tuple{Vararg{Union{Symbol, Tuple{Vararg{Symbol}}}}}) == 1 -@test Core.Compiler.unioncomplexity(Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Symbol}}}}}}}}) == 2 -@test Core.Compiler.unioncomplexity(Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Symbol}}}}}}}}}}}) == 3 +@test Core.Compiler.unioncomplexity(Tuple{Vararg{Tuple{Union{Int8, Int16}}}}) == 2 +@test Core.Compiler.unioncomplexity(Tuple{Vararg{Symbol}}) == 1 +@test Core.Compiler.unioncomplexity(Tuple{Vararg{Union{Symbol, Tuple{Vararg{Symbol}}}}}) == 3 +@test Core.Compiler.unioncomplexity(Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Symbol}}}}}}}}) == 5 +@test Core.Compiler.unioncomplexity(Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Symbol}}}}}}}}}}}) == 7 # PR 22120 @@ -3511,6 +3511,18 @@ end @test only(Base.return_types(pickvarnames, (Vector{Any},))) == Tuple @test only(Base.code_typed(pickvarnames, (Vector{Any},), optimize=false))[2] == Tuple{Vararg{Union{Symbol, Tuple}}} +# make sure this converges in a reasonable amount of time +function pickvarnames2(x::Vector{Any}) + varnames = () + for a in x + varnames = (varnames..., pickvarnames(a) ) + end + return varnames +end +@test only(Base.return_types(pickvarnames2, (Vector{Any},))) == Tuple{Vararg{Union{Symbol, Tuple}}} +@test only(Base.code_typed(pickvarnames2, (Vector{Any},), optimize=false))[2] == Tuple{Vararg{Union{Symbol, Tuple}}} + + @test map(>:, [Int], [Int]) == [true] # issue 35566 @@ -4604,6 +4616,16 @@ end a = Core.Compiler.tmerge(Core.Compiler.JLTypeLattice(), Val{<:a}, a) @test_broken a != Val{<:Val{Union{}}} @test_broken a == Val{<:Val} || a == Val + + a = Tuple{Vararg{Tuple{}}} + a = Core.Compiler.tmerge(Core.Compiler.JLTypeLattice(), Tuple{a}, a) + @test a == Tuple{Vararg{Tuple{Vararg{Tuple{}}}}} + a = Core.Compiler.tmerge(Core.Compiler.JLTypeLattice(), Tuple{a}, a) + @test a == Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{}}}}}}} + a = Core.Compiler.tmerge(Core.Compiler.JLTypeLattice(), Tuple{a}, a) + @test a == Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{}}}}}}}}} + a = Core.Compiler.tmerge(Core.Compiler.JLTypeLattice(), Tuple{a}, a) + @test a == Tuple end # Test that a function-wise `@max_methods` works as expected From 550281a1646583c2c5856ceb22a0c1632d60a8c2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 13 Sep 2023 14:05:02 +0000 Subject: [PATCH 3/3] make drastic changes to make this algorithm more commutative and simpler --- base/compiler/typelimits.jl | 41 ++++++++++++++++++++----------------- test/compiler/inference.jl | 5 ++++- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 5d7f6890926e4..44ddbf8dcba38 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -749,7 +749,7 @@ end # see if any of the union elements have the same TypeName # in which case, simplify this tmerge by replacing it with # the widest possible version of itself (the wrapper) - havetuples = false + simplify = falses(length(types)) for i in 1:length(types) typenames[i] === Any.name && continue ti = types[i] @@ -761,10 +761,14 @@ end if ti <: tj types[i] = Union{} typenames[i] = Any.name + simplify[i] = false + simplify[j] = true break elseif tj <: ti types[j] = Union{} typenames[j] = Any.name + simplify[j] = false + simplify[i] = true else if ijname === Tuple.name # try to widen Tuple slower: make a single non-concrete Tuple containing both @@ -772,7 +776,7 @@ end # see 4ee2b41552a6bc95465c12ca66146d69b354317b, be59686f7613a2ccfd63491c7b354d0b16a95c05, widen = tuplemerge(unwrap_unionall(ti)::DataType, unwrap_unionall(tj)::DataType) widen = rewrap_unionall(rewrap_unionall(widen, ti), tj) - havetuples = true + simplify[j] = false else wr = ijname.wrapper uw = unwrap_unionall(wr)::DataType @@ -784,42 +788,40 @@ end while uj.name !== ijname uj = uj.super end - merged = Vector{Any}(undef, length(uw.parameters)) - use_merged = true + p = Vector{Any}(undef, length(uw.parameters)) + usep = true widen = wr for k = 1:length(uw.parameters) ui_k = ui.parameters[k] if ui_k === uj.parameters[k] && !has_free_typevars(ui_k) - merged[k] = ui_k - use_merged = true + p[k] = ui_k + usep = true else - merged[k] = uw.parameters[k] + p[k] = uw.parameters[k] end end - if use_merged - widen = rewrap_unionall(wr{merged...}, wr) + if usep + widen = rewrap_unionall(wr{p...}, wr) end + simplify[j] = !usep end types[i] = Union{} typenames[i] = Any.name + simplify[i] = false types[j] = widen break end end end end - # don't let elements of the union get too big, if the above didn't reduce something + # don't let elements of the union get too big, if the above didn't reduce something enough # Specifically widen Tuple{..., Union{lots of stuff}...} to Tuple{..., Any, ...} - # Even if there was only one element of the base type, don't let Val{<:Val{<:Val}} keep nesting abstract levels + # Don't let Val{<:Val{<:Val}} keep nesting abstract levels either for i in 1:length(types) - # this element is too complicated, so - # just return the widest possible type now - tn = typenames[i] + simplify[i] || continue ti = types[i] - if tn === Any.name || issimpleenoughtype(ti) - continue - elseif tn === Tuple.name - havetuples && continue # multiple-tuples case was already handled by tuplemerge above + issimpleenoughtype(ti) && continue + if typenames[i] === Tuple.name # otherwise we need to do a simple version of tuplemerge for one element now tip = (unwrap_unionall(ti)::DataType).parameters lt = length(tip) @@ -831,7 +833,7 @@ end types[i] = rewrap_unionall(Tuple{p...}, ti) else # this element is not simple enough yet, make it so now - types[i] = tn.wrapper + types[i] = typenames[i].wrapper end end u = Union{types...} @@ -875,6 +877,7 @@ function tuplemerge(a::DataType, b::DataType) # or (equivalently?) iteratively took super-types until reaching a common wrapper # e.g. consider the results of `tuplemerge(Tuple{Complex}, Tuple{Number, Int})` and of # `tuplemerge(Tuple{Int}, Tuple{String}, Tuple{Int, String})` + # c.f. tname_intersect in the algorithm above hasfree = has_free_typevars(ti) if hasfree || !(ti <: tail) if !hasfree && tail <: ti diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e6eeeb81a2647..c7e0cdfb6f6b2 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -214,9 +214,12 @@ tuplemerge_test(Tuple{}, Tuple{Complex, Vararg{Union{ComplexF32, ComplexF64}}}, @test Core.Compiler.tmerge(Union{}, Base.BitIntegerType) === Base.BitIntegerType @test Core.Compiler.tmerge(Core.Compiler.fallback_ipo_lattice, Core.Compiler.InterConditional(1, Int, Union{}), Core.Compiler.InterConditional(2, String, Union{})) === Core.Compiler.Const(true) # test issue behind https://github.com/JuliaLang/julia/issues/50458 -@test Core.Compiler.tmerge(Nothing, Tuple{Base.BitInteger, Int}) == Union{Nothing, Tuple{Any, Int}} +@test Core.Compiler.tmerge(Nothing, Tuple{Base.BitInteger, Int}) == Union{Nothing, Tuple{Base.BitInteger, Int}} +@test Core.Compiler.tmerge(Union{Nothing, Tuple{Int, Int}}, Tuple{Base.BitInteger, Int}) == Union{Nothing, Tuple{Any, Int}} @test Core.Compiler.tmerge(Nothing, Tuple{Union{Char, String, SubString{String}, Symbol}, Int}) == Union{Nothing, Tuple{Union{Char, String, SubString{String}, Symbol}, Int}} +@test Core.Compiler.tmerge(Union{Nothing, Tuple{Char, Int}}, Tuple{Union{Char, String, SubString{String}, Symbol}, Int}) == Union{Nothing, Tuple{Union{Char, String, SubString{String}, Symbol}, Int}} @test Core.Compiler.tmerge(Nothing, Tuple{Integer, Int}) == Union{Nothing, Tuple{Integer, Int}} +@test Core.Compiler.tmerge(Union{Nothing, Tuple{Int, Int}}, Tuple{Integer, Int}) == Union{Nothing, Tuple{Integer, Int}} @test Core.Compiler.tmerge(Union{Nothing, AbstractVector{Int}}, Vector) == Union{Nothing, AbstractVector} @test Core.Compiler.tmerge(Union{Nothing, AbstractVector{Int}}, Matrix) == Union{Nothing, AbstractArray} @test Core.Compiler.tmerge(Union{Nothing, AbstractVector{Int}}, Matrix{Int}) == Union{Nothing, AbstractArray{Int}}