Skip to content

inference: fix fieldtype tfunc design #31670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 59 additions & 19 deletions base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ function fieldcount_noerror(@nospecialize t)
end


function try_compute_fieldidx(@nospecialize(typ), @nospecialize(field))
function try_compute_fieldidx(typ::DataType, @nospecialize(field))
if isa(field, Symbol)
field = fieldindex(typ, field, false)
field == 0 && return nothing
Expand Down Expand Up @@ -802,6 +802,7 @@ fieldtype_tfunc(@nospecialize(s0), @nospecialize(name), @nospecialize(inbounds))
fieldtype_tfunc(s0, name)

function fieldtype_nothrow(@nospecialize(s0), @nospecialize(name))
s0 === Bottom && return true # unreachable
if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0
# We have no idea
return false
Expand All @@ -815,17 +816,27 @@ function fieldtype_nothrow(@nospecialize(s0), @nospecialize(name))

su = unwrap_unionall(s0)
if isa(su, Union)
return fieldtype_nothrow(rewrap_unionall(su.a, s0), name) && fieldtype_nothrow(rewrap_unionall(su.b, s0), name)
return fieldtype_nothrow(rewrap_unionall(su.a, s0), name) &&
fieldtype_nothrow(rewrap_unionall(su.b, s0), name)
end

s = instanceof_tfunc(s0)[1]
u = unwrap_unionall(s)
return _fieldtype_nothrow(u, name)
s, exact = instanceof_tfunc(s0)
s === Bottom && return false # always
return _fieldtype_nothrow(s, exact, name)
end

function _fieldtype_nothrow(@nospecialize(u), name::Const)
function _fieldtype_nothrow(@nospecialize(s), exact::Bool, name::Const)
u = unwrap_unionall(s)
if isa(u, Union)
return _fieldtype_nothrow(u.a, name) || _fieldtype_nothrow(u.b, name)
a = _fieldtype_nothrow(u.a, exact, name)
b = _fieldtype_nothrow(u.b, exact, name)
return exact ? (a || b) : (a && b)
end
u isa DataType || return false
u.abstract && return false
if u.name === _NAMEDTUPLE_NAME && !isconcretetype(u)
# TODO: better approximate inference
return false
end
fld = name.val
if isa(fld, Symbol)
Expand All @@ -844,6 +855,9 @@ function _fieldtype_nothrow(@nospecialize(u), name::Const)
end

function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name))
if s0 === Bottom
return Bottom
end
if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0
return Type
end
Expand All @@ -855,18 +869,28 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name))
return Bottom
end

s = instanceof_tfunc(s0)[1]
u = unwrap_unionall(s)

if isa(u, Union)
return tmerge(rewrap(fieldtype_tfunc(Type{u.a}, name), s),
rewrap(fieldtype_tfunc(Type{u.b}, name), s))
su = unwrap_unionall(s0)
if isa(su, Union)
return tmerge(fieldtype_tfunc(rewrap(su.a, s0), name),
fieldtype_tfunc(rewrap(su.b, s0), name))
end

if !isa(u, DataType) || u.abstract
return Type
s, exact = instanceof_tfunc(s0)
s === Bottom && return Bottom
return _fieldtype_tfunc(s, exact, name)
end

function _fieldtype_tfunc(@nospecialize(s), exact::Bool, @nospecialize(name))
exact = exact && !has_free_typevars(s)
u = unwrap_unionall(s)
if isa(u, Union)
return tmerge(_fieldtype_tfunc(rewrap(u.a, s), exact, name),
_fieldtype_tfunc(rewrap(u.b, s), exact, name))
end
u isa DataType || return Type
u.abstract && return Type
if u.name === _NAMEDTUPLE_NAME && !isconcretetype(u)
# TODO: better approximate inference
return Type
end
ftypes = u.types
Expand All @@ -875,12 +899,25 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name))
end

if !isa(name, Const)
name = widenconst(name)
if !(Int <: name || Symbol <: name)
return Bottom
end
t = Bottom
for i in 1:length(ftypes)
t = tmerge(t, fieldtype_tfunc(s0, Const(i)))
ft1 = unwrapva(ftypes[i])
exactft1 = exact || !has_free_typevars(ft1)
ft1 = rewrap_unionall(ft1, s)
if exactft1
if issingletontype(ft1)
ft1 = Const(ft1) # ft unique via type cache
else
ft1 = Type{ft1}
end
else
ft1 = Type{ft} where ft<:ft1
end
t = tmerge(t, ft1)
t === Any && break
end
return t
Expand All @@ -902,10 +939,13 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name))
ft = ftypes[fld]
end

exact = (isa(s0, Const) || isType(s0)) && !has_free_typevars(s)
exactft = exact || !has_free_typevars(ft)
ft = rewrap_unionall(ft, s)
if exact
return Const(ft)
if exactft
if issingletontype(ft)
return Const(ft) # ft unique via type cache
end
return Type{ft}
end
return Type{<:ft}
end
Expand Down
21 changes: 21 additions & 0 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,27 @@ mutable struct HasAbstractlyTypedField
end
f_infer_abstract_fieldtype() = fieldtype(HasAbstractlyTypedField, :x)
@test Base.return_types(f_infer_abstract_fieldtype, ()) == Any[Type{Union{Int,String}}]
let fieldtype_tfunc = Core.Compiler.fieldtype_tfunc,
fieldtype_nothrow = Core.Compiler.fieldtype_nothrow
@test fieldtype_tfunc(Union{}, :x) == Union{}
@test fieldtype_tfunc(Union{Type{Int32}, Int32}, Const(:x)) == Union{}
@test fieldtype_tfunc(Union{Type{Base.RefValue{T}}, Type{Int32}} where {T<:Array}, Const(:x)) == Type{<:Array}
@test fieldtype_tfunc(Union{Type{Base.RefValue{T}}, Type{Int32}} where {T<:Real}, Const(:x)) == Type{<:Real}
@test fieldtype_tfunc(Union{Type{Base.RefValue{<:Array}}, Type{Int32}}, Const(:x)) == Type{Array}
@test fieldtype_tfunc(Union{Type{Base.RefValue{<:Real}}, Type{Int32}}, Const(:x)) == Const(Real)
@test fieldtype_tfunc(Const(Union{Base.RefValue{<:Real}, Type{Int32}}), Const(:x)) == Type
@test fieldtype_tfunc(Type{Union{Base.RefValue{T}, Type{Int32}}} where {T<:Real}, Const(:x)) == Type
@test fieldtype_nothrow(Type{Base.RefValue{<:Real}}, Const(:x))
@test !fieldtype_nothrow(Type{Union{}}, Const(:x))
@test !fieldtype_nothrow(Union{Type{Base.RefValue{T}}, Int32} where {T<:Real}, Const(:x))
@test !fieldtype_nothrow(Union{Type{Base.RefValue{<:Real}}, Int32}, Const(:x))
@test fieldtype_nothrow(Const(Union{Base.RefValue{<:Real}, Int32}), Const(:x))
@test !fieldtype_nothrow(Type{Union{Base.RefValue{T}, Int32}} where {T<:Real}, Const(:x)) # improvable?
@test fieldtype_nothrow(Union{Type{Base.RefValue{T}}, Type{Base.RefValue{Any}}} where {T<:Real}, Const(:x))
@test fieldtype_nothrow(Union{Type{Base.RefValue{<:Real}}, Type{Base.RefValue{Any}}}, Const(:x))
@test fieldtype_nothrow(Const(Union{Base.RefValue{<:Real}, Base.RefValue{Any}}), Const(:x))
@test fieldtype_nothrow(Type{Union{Base.RefValue{T}, Base.RefValue{Any}}} where {T<:Real}, Const(:x))
end

# issue #11480
@noinline f11480(x,y) = x
Expand Down