Skip to content

Commit

Permalink
inference: improve isa-constraint propagation for iskindtype obje…
Browse files Browse the repository at this point in the history
…cts (JuliaLang#46712)

Currently our inference isn't able to propagate `isa`-based type
constraint for cases like `isa(Type{<:...}, DataType)` since
`typeintersect` may return `Type` object itself when taking `Type`
object and `iskindtype`-object.

This case happens in the following kind of situation (motivated by the
discussion at <JuliaLang#46553 (comment)>):
```julia
julia> function isa_kindtype(T::Type{<:AbstractVector})
           if isa(T, DataType)
               # `T` here should be inferred as `DataType` rather than `Type{<:AbstractVector}`
               return T.name.name # should be inferred as ::Symbol
           end
           return nothing
       end
isa_kindtype (generic function with 1 method)

julia> only(code_typed(isa_kindtype; optimize=false))
CodeInfo(
1 ─ %1 = (T isa Main.DataType)::Bool
└──      goto #3 if not %1
2 ─ %3 = Base.getproperty(T, :name)::Any
│   %4 = Base.getproperty(%3, :name)::Any
└──      return %4
3 ─      return Main.nothing
) => Any
```

This commit improves the situation by adding a special casing for
abstract interpretation, rather than changing the behavior of
`typeintersect`.
  • Loading branch information
aviatesk authored Sep 13, 2022
1 parent 969193b commit 0b38483
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 7 deletions.
26 changes: 19 additions & 7 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,10 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs
tty_lb = tty_ub # TODO: this would be wrong if !isexact_tty, but instanceof_tfunc doesn't preserve this info
if !has_free_typevars(tty_lb) && !has_free_typevars(tty_ub)
ifty = typeintersect(aty, tty_ub)
if iskindtype(tty_ub) && ifty !== Bottom
# `typeintersect` may be unable narrow down `Type`-type
ifty = tty_ub
end
valid_as_lattice(ifty) || (ifty = Union{})
elty = typesubtract(aty, tty_lb, InferenceParams(interp).MAX_UNION_SPLITTING)
return Conditional(a, ifty, elty)
Expand Down Expand Up @@ -2678,14 +2682,22 @@ end
function conditional_change(state::VarTable, @nospecialize(typ), slot::Int)
vtype = state[slot]
oldtyp = vtype.typ
# approximate test for `typ ∩ oldtyp` being better than `oldtyp`
# since we probably formed these types with `typesubstract`, the comparison is likely simple
if ignorelimited(typ) ignorelimited(oldtyp)
# typ is better unlimited, but we may still need to compute the tmeet with the limit "causes" since we ignored those in the comparison
oldtyp isa LimitedAccuracy && (typ = tmerge(typ, LimitedAccuracy(Bottom, oldtyp.causes)))
return StateUpdate(SlotNumber(slot), VarState(typ, vtype.undef), state, true)
if iskindtype(typ)
# this code path corresponds to the special handling for `isa(x, iskindtype)` check
# implemented within `abstract_call_builtin`
elseif ignorelimited(typ) ignorelimited(oldtyp)
# approximate test for `typ ∩ oldtyp` being better than `oldtyp`
# since we probably formed these types with `typesubstract`,
# the comparison is likely simple
else
return nothing
end
return nothing
if oldtyp isa LimitedAccuracy
# typ is better unlimited, but we may still need to compute the tmeet with the limit
# "causes" since we ignored those in the comparison
typ = tmerge(typ, LimitedAccuracy(Bottom, oldtyp.causes))
end
return StateUpdate(SlotNumber(slot), VarState(typ, vtype.undef), state, true)
end

function bool_rt_to_conditional(@nospecialize(rt), slottypes::Vector{Any}, state::VarTable, slot_id::Int)
Expand Down
9 changes: 9 additions & 0 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4202,3 +4202,12 @@ end
end
call_pure_annotated_loop(x) = Val{pure_annotated_loop(x, 1)}()
@test only(Base.return_types(call_pure_annotated_loop, Tuple{Int})) === Val{1}

function isa_kindtype(T::Type{<:AbstractVector})
if isa(T, DataType)
# `T` here should be inferred as `DataType` rather than `Type{<:AbstractVector}`
return T.name.name # should be inferred as ::Symbol
end
return nothing
end
@test only(Base.return_types(isa_kindtype)) === Union{Nothing,Symbol}

0 comments on commit 0b38483

Please sign in to comment.