Skip to content

Commit

Permalink
eliminate unbound TypeVars on argtypes construction
Browse files Browse the repository at this point in the history
This commit eliminates unbound `TypeVar`s from `argtypes` in order to
make the analysis much easier down the road.

At runtime, only `Type{...}::DataType` can contain invalid type parameters,
but we may have arbitrary malformed user-constructed type arguments given
at inference entries.
So we will replace only the malformed `Type{...}::DataType` with `Type`
and simply replace other possibilities with `Any`.
The replacement might not be that accurate, but an unbound `TypeVar` is
really invalid in anyway.

Like the change added on `arrayref_tfunc`, now we may be able to remove
some `isa(x, TypeVar)`/`has_free_typevars` predicates used in various
places, but it is left as an exercise for the reader.

(cherry picked from commit 36cc1c1)
  • Loading branch information
aviatesk authored and KristofferC committed Nov 8, 2021
1 parent bbe91db commit 2a4d14f
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 12 deletions.
21 changes: 17 additions & 4 deletions base/compiler/inferenceresult.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ end
function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(specTypes),
isva::Bool, withfirst::Bool = true)
toplevel = method === nothing
linfo_argtypes = Any[unwrap_unionall(specTypes).parameters...]
linfo_argtypes = Any[(unwrap_unionall(specTypes)::DataType).parameters...]
nargs::Int = toplevel ? 0 : method.nargs
if !withfirst
# For opaque closure, the closure environment is processed elsewhere
Expand Down Expand Up @@ -80,8 +80,8 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe
else
vargtype_elements = Any[]
for p in linfo_argtypes[nargs:linfo_argtypes_length]
p = isvarargtype(p) ? unconstrain_vararg_length(p) : p
push!(vargtype_elements, rewrap(p, specTypes))
p = unwraptv(isvarargtype(p) ? unconstrain_vararg_length(p) : p)
push!(vargtype_elements, elim_free_typevars(rewrap(p, specTypes)))
end
for i in 1:length(vargtype_elements)
atyp = vargtype_elements[i]
Expand Down Expand Up @@ -120,7 +120,7 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe
elseif isconstType(atyp)
atyp = Const(atyp.parameters[1])
else
atyp = rewrap(atyp, specTypes)
atyp = elim_free_typevars(rewrap(atyp, specTypes))
end
i == n && (lastatype = atyp)
cache_argtypes[i] = atyp
Expand All @@ -134,6 +134,19 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe
cache_argtypes
end

# eliminate free `TypeVar`s in order to make the life much easier down the road:
# at runtime only `Type{...}::DataType` can contain invalid type parameters, and other
# malformed types here are user-constructed type arguments given at an inference entry
# so this function will replace only the malformed `Type{...}::DataType` with `Type`
# and simply replace other possibilities with `Any`
function elim_free_typevars(@nospecialize t)
if has_free_typevars(t)
return isType(t) ? Type : Any
else
return t
end
end

function matching_cache_argtypes(linfo::MethodInstance, ::Nothing, va_override::Bool)
mthd = isa(linfo.def, Method) ? linfo.def::Method : nothing
cache_argtypes = most_general_argtypes(mthd, linfo.specTypes,
Expand Down
8 changes: 2 additions & 6 deletions base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1470,12 +1470,8 @@ end
function arrayref_tfunc(@nospecialize(boundscheck), @nospecialize(a), @nospecialize i...)
a = widenconst(a)
if a <: Array
if isa(a, DataType) && begin
ap1 = a.parameters[1]
isa(ap1, Type) || isa(ap1, TypeVar)
end
# TODO: the TypeVar case should not be needed here
return unwraptv(ap1)
if isa(a, DataType) && isa(a.parameters[1], Type)
return a.parameters[1]
elseif isa(a, UnionAll) && !has_free_typevars(a)
unw = unwrap_unionall(a)
if isa(unw, DataType)
Expand Down
3 changes: 1 addition & 2 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,6 @@ function typeinf_ext_toplevel(interp::AbstractInterpreter, linfo::MethodInstance
return src
end


function return_type(@nospecialize(f), @nospecialize(t))
world = ccall(:jl_get_tls_world_age, UInt, ())
return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Ptr{Cvoid}}, Cint), Any[_return_type, f, t, world], 4)
Expand All @@ -973,7 +972,7 @@ function _return_type(interp::AbstractInterpreter, @nospecialize(f), @nospeciali
rt = widenconst(rt)
else
for match in _methods(f, t, -1, get_world_counter(interp))::Vector
match = match::Core.MethodMatch
match = match::MethodMatch
ty = typeinf_type(interp, match.method, match.spec_types, match.sparams)
ty === nothing && return Any
rt = tmerge(rt, ty)
Expand Down
38 changes: 38 additions & 0 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3507,3 +3507,41 @@ Foo42097(f::F, args) where {F} = Foo42097{F}()
Foo42097(A) = Foo42097(Base.inferencebarrier(+), Base.inferencebarrier(1)...)
foo42097() = Foo42097([1]...)
@test foo42097() isa Foo42097{typeof(+)}

# eliminate unbound `TypeVar`s on `argtypes` construction
let
a0(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, va...) = nothing
method = only(methods(a0))
unbound = TypeVar(:Unbound, Integer)
specTypes = Tuple{typeof(a0),
# TypeVar
#=01=# Bound, # => Integer
#=02=# unbound, # => Integer (invalid `TypeVar` widened beforehand)
# DataType
#=03=# Type{Bound}, # => Type{Bound} where Bound<:Integer
#=04=# Type{unbound}, # => Type
#=05=# Vector{Bound}, # => Vector{Bound} where Bound<:Integer
#=06=# Vector{unbound}, # => Any
# UnionAll
#=07=# Type{<:Bound}, # => Type{<:Bound} where Bound<:Integer
#=08=# Type{<:unbound}, # => Any
# Union
#=09=# Union{Nothing,Bound}, # => Union{Nothing,Bound} where Bound<:Integer
#=10=# Union{Nothing,unbound}, # => Any
# Vararg
#=va=# Bound, unbound, # => Tuple{Integer,Integer} (invalid `TypeVar` widened beforehand)
} where Bound<:Integer
argtypes = Core.Compiler.most_general_argtypes(method, specTypes, true)
popfirst!(argtypes)
@test argtypes[1] == Integer
@test argtypes[2] == Integer
@test argtypes[3] == Type{Bound} where Bound<:Integer
@test argtypes[4] == Type
@test argtypes[5] == Vector{Bound} where Bound<:Integer
@test argtypes[6] == Any
@test argtypes[7] == Type{<:Bound} where Bound<:Integer
@test argtypes[8] == Any
@test argtypes[9] == Union{Nothing,Bound} where Bound<:Integer
@test argtypes[10] == Any
@test argtypes[11] == Tuple{Integer,Integer}
end

0 comments on commit 2a4d14f

Please sign in to comment.