Skip to content

Commit

Permalink
improve apply_type_tfunc accuracy in rare case (#49069)
Browse files Browse the repository at this point in the history
In the unlikely event this call fails, we can either confidently
conclude the result will always fail and stop inference immediately
there. Or we can at least conclude that the base type is confidently
known, which can potentially improve ml-matches performance later by
excluding Union{} or other subtypes.
  • Loading branch information
vtjnash committed Mar 21, 2023
1 parent 9c19f40 commit 2f687a2
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 7 deletions.
37 changes: 31 additions & 6 deletions base/compiler/tfuncs.jl
Expand Up @@ -1628,6 +1628,7 @@ function apply_type_nothrow(𝕃::AbstractLattice, argtypes::Vector{Any}, @nospe
(headtype === Union) && return true
isa(rt, Const) && return true
u = headtype
# TODO: implement optimization for isvarargtype(u) and istuple occurences (which are valid but are not UnionAll)
for i = 2:length(argtypes)
isa(u, UnionAll) || return false
ai = widenconditional(argtypes[i])
Expand Down Expand Up @@ -1747,6 +1748,9 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K,
end
ua = ua.body
end
if largs > outer_start && isa(headtype, UnionAll) # e.g. !isvarargtype(ua) && !istuple
return Bottom # too many arguments
end
outer_start = outer_start - largs + 1

varnamectr = 1
Expand Down Expand Up @@ -1815,19 +1819,40 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K,
push!(outervars, v)
end
end
if isa(ua, UnionAll)
if ua isa UnionAll
ua = ua.body
else
ua = nothing
#otherwise, sometimes ua isa Vararg (Core.TypeofVararg) or Tuple (DataType)
end
end
local appl
try
appl = apply_type(headtype, tparams...)
catch ex
# type instantiation might fail if one of the type parameters
# doesn't match, which could happen if a type estimate is too coarse
return isvarargtype(headtype) ? TypeofVararg : Type{<:headtype}
# type instantiation might fail if one of the type parameters doesn't
# match, which could happen only if a type estimate is too coarse
# and might guess a concrete value while the actual type for it is Bottom
if !uncertain
return Union{}
end
canconst = false
uncertain = true
empty!(outervars)
outer_start = 1
# FIXME: if these vars are substituted with TypeVar here, the result
# might be wider than the input, so should we use the `.name.wrapper`
# object here instead, to replace all of these outervars with
# unconstrained ones? Note that this code is nearly unreachable though,
# and possibly should simply return Union{} here also, since
# `apply_type` is already quite conservative about detecting and
# throwing errors.
appl = headtype
if isa(appl, UnionAll)
for _ = 1:largs
appl = appl::UnionAll
push!(outervars, appl.var)
appl = appl.body
end
end
end
!uncertain && canconst && return Const(appl)
if isvarargtype(appl)
Expand Down
6 changes: 5 additions & 1 deletion test/compiler/inference.jl
Expand Up @@ -2648,10 +2648,14 @@ end |> only === Int
# https://github.com/JuliaLang/julia/issues/47089
import Core: Const
import Core.Compiler: apply_type_tfunc
struct Issue47089{A,B} end
struct Issue47089{A<:Number,B<:Number} end
let 𝕃 = Core.Compiler.fallback_lattice
A = Type{<:Integer}
@test apply_type_tfunc(𝕃, Const(Issue47089), A, A) <: (Type{Issue47089{A,B}} where {A<:Integer, B<:Integer})
@test apply_type_tfunc(𝕃, Const(Issue47089), Const(Int), Const(Int), Const(Int)) === Union{}
@test apply_type_tfunc(𝕃, Const(Issue47089), Const(String)) === Union{}
@test apply_type_tfunc(𝕃, Const(Issue47089), Const(AbstractString)) === Union{}
@test apply_type_tfunc(𝕃, Const(Issue47089), Type{Ptr}, Type{Ptr{T}} where T) === Base.rewrap_unionall(Type{Issue47089.body.body}, Issue47089)
end
@test only(Base.return_types(keys, (Dict{String},))) == Base.KeySet{String, T} where T<:(Dict{String})
@test only(Base.return_types((r)->similar(Array{typeof(r[])}, 1), (Base.RefValue{Array{Int}},))) == Vector{<:Array{Int}}
Expand Down

0 comments on commit 2f687a2

Please sign in to comment.