Skip to content

Commit 2a4d14f

Browse files
aviateskKristofferC
authored andcommitted
eliminate unbound TypeVars on argtypes construction
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)
1 parent bbe91db commit 2a4d14f

File tree

4 files changed

+58
-12
lines changed

4 files changed

+58
-12
lines changed

base/compiler/inferenceresult.jl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ end
5050
function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(specTypes),
5151
isva::Bool, withfirst::Bool = true)
5252
toplevel = method === nothing
53-
linfo_argtypes = Any[unwrap_unionall(specTypes).parameters...]
53+
linfo_argtypes = Any[(unwrap_unionall(specTypes)::DataType).parameters...]
5454
nargs::Int = toplevel ? 0 : method.nargs
5555
if !withfirst
5656
# For opaque closure, the closure environment is processed elsewhere
@@ -80,8 +80,8 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe
8080
else
8181
vargtype_elements = Any[]
8282
for p in linfo_argtypes[nargs:linfo_argtypes_length]
83-
p = isvarargtype(p) ? unconstrain_vararg_length(p) : p
84-
push!(vargtype_elements, rewrap(p, specTypes))
83+
p = unwraptv(isvarargtype(p) ? unconstrain_vararg_length(p) : p)
84+
push!(vargtype_elements, elim_free_typevars(rewrap(p, specTypes)))
8585
end
8686
for i in 1:length(vargtype_elements)
8787
atyp = vargtype_elements[i]
@@ -120,7 +120,7 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe
120120
elseif isconstType(atyp)
121121
atyp = Const(atyp.parameters[1])
122122
else
123-
atyp = rewrap(atyp, specTypes)
123+
atyp = elim_free_typevars(rewrap(atyp, specTypes))
124124
end
125125
i == n && (lastatype = atyp)
126126
cache_argtypes[i] = atyp
@@ -134,6 +134,19 @@ function most_general_argtypes(method::Union{Method, Nothing}, @nospecialize(spe
134134
cache_argtypes
135135
end
136136

137+
# eliminate free `TypeVar`s in order to make the life much easier down the road:
138+
# at runtime only `Type{...}::DataType` can contain invalid type parameters, and other
139+
# malformed types here are user-constructed type arguments given at an inference entry
140+
# so this function will replace only the malformed `Type{...}::DataType` with `Type`
141+
# and simply replace other possibilities with `Any`
142+
function elim_free_typevars(@nospecialize t)
143+
if has_free_typevars(t)
144+
return isType(t) ? Type : Any
145+
else
146+
return t
147+
end
148+
end
149+
137150
function matching_cache_argtypes(linfo::MethodInstance, ::Nothing, va_override::Bool)
138151
mthd = isa(linfo.def, Method) ? linfo.def::Method : nothing
139152
cache_argtypes = most_general_argtypes(mthd, linfo.specTypes,

base/compiler/tfuncs.jl

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,12 +1470,8 @@ end
14701470
function arrayref_tfunc(@nospecialize(boundscheck), @nospecialize(a), @nospecialize i...)
14711471
a = widenconst(a)
14721472
if a <: Array
1473-
if isa(a, DataType) && begin
1474-
ap1 = a.parameters[1]
1475-
isa(ap1, Type) || isa(ap1, TypeVar)
1476-
end
1477-
# TODO: the TypeVar case should not be needed here
1478-
return unwraptv(ap1)
1473+
if isa(a, DataType) && isa(a.parameters[1], Type)
1474+
return a.parameters[1]
14791475
elseif isa(a, UnionAll) && !has_free_typevars(a)
14801476
unw = unwrap_unionall(a)
14811477
if isa(unw, DataType)

base/compiler/typeinfer.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,6 @@ function typeinf_ext_toplevel(interp::AbstractInterpreter, linfo::MethodInstance
958958
return src
959959
end
960960

961-
962961
function return_type(@nospecialize(f), @nospecialize(t))
963962
world = ccall(:jl_get_tls_world_age, UInt, ())
964963
return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Ptr{Cvoid}}, Cint), Any[_return_type, f, t, world], 4)
@@ -973,7 +972,7 @@ function _return_type(interp::AbstractInterpreter, @nospecialize(f), @nospeciali
973972
rt = widenconst(rt)
974973
else
975974
for match in _methods(f, t, -1, get_world_counter(interp))::Vector
976-
match = match::Core.MethodMatch
975+
match = match::MethodMatch
977976
ty = typeinf_type(interp, match.method, match.spec_types, match.sparams)
978977
ty === nothing && return Any
979978
rt = tmerge(rt, ty)

test/compiler/inference.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3507,3 +3507,41 @@ Foo42097(f::F, args) where {F} = Foo42097{F}()
35073507
Foo42097(A) = Foo42097(Base.inferencebarrier(+), Base.inferencebarrier(1)...)
35083508
foo42097() = Foo42097([1]...)
35093509
@test foo42097() isa Foo42097{typeof(+)}
3510+
3511+
# eliminate unbound `TypeVar`s on `argtypes` construction
3512+
let
3513+
a0(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, va...) = nothing
3514+
method = only(methods(a0))
3515+
unbound = TypeVar(:Unbound, Integer)
3516+
specTypes = Tuple{typeof(a0),
3517+
# TypeVar
3518+
#=01=# Bound, # => Integer
3519+
#=02=# unbound, # => Integer (invalid `TypeVar` widened beforehand)
3520+
# DataType
3521+
#=03=# Type{Bound}, # => Type{Bound} where Bound<:Integer
3522+
#=04=# Type{unbound}, # => Type
3523+
#=05=# Vector{Bound}, # => Vector{Bound} where Bound<:Integer
3524+
#=06=# Vector{unbound}, # => Any
3525+
# UnionAll
3526+
#=07=# Type{<:Bound}, # => Type{<:Bound} where Bound<:Integer
3527+
#=08=# Type{<:unbound}, # => Any
3528+
# Union
3529+
#=09=# Union{Nothing,Bound}, # => Union{Nothing,Bound} where Bound<:Integer
3530+
#=10=# Union{Nothing,unbound}, # => Any
3531+
# Vararg
3532+
#=va=# Bound, unbound, # => Tuple{Integer,Integer} (invalid `TypeVar` widened beforehand)
3533+
} where Bound<:Integer
3534+
argtypes = Core.Compiler.most_general_argtypes(method, specTypes, true)
3535+
popfirst!(argtypes)
3536+
@test argtypes[1] == Integer
3537+
@test argtypes[2] == Integer
3538+
@test argtypes[3] == Type{Bound} where Bound<:Integer
3539+
@test argtypes[4] == Type
3540+
@test argtypes[5] == Vector{Bound} where Bound<:Integer
3541+
@test argtypes[6] == Any
3542+
@test argtypes[7] == Type{<:Bound} where Bound<:Integer
3543+
@test argtypes[8] == Any
3544+
@test argtypes[9] == Union{Nothing,Bound} where Bound<:Integer
3545+
@test argtypes[10] == Any
3546+
@test argtypes[11] == Tuple{Integer,Integer}
3547+
end

0 commit comments

Comments
 (0)