diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 294c659377587..7ddfe4ad881e4 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -775,7 +775,8 @@ function getfield_tfunc(s00, name, order, boundscheck) end return getfield_tfunc(s00, name) end -function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) +getfield_tfunc(@nospecialize(s00), @nospecialize(name)) = _getfield_tfunc(s00, name, false) +function _getfield_tfunc(@nospecialize(s00), @nospecialize(name), setfield::Bool) s = unwrap_unionall(s00) if isa(s, Union) return tmerge(getfield_tfunc(rewrap_unionall(s.a, s00), name), @@ -791,6 +792,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if isa(name, Const) nv = name.val if isa(sv, Module) + setfield && return Bottom if isa(nv, Symbol) return abstract_eval_global(sv, nv) end @@ -834,9 +836,8 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) return Bottom end if s <: Module - if !(Symbol <: widenconst(name)) - return Bottom - end + setfield && return Bottom + hasintersect(widenconst(name), Symbol) || return Bottom return Any end if s.name === _NAMEDTUPLE_NAME && !isconcretetype(s) @@ -857,9 +858,10 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) return getfield_tfunc(_ts, name) end ftypes = datatype_fieldtypes(s) + nf = length(ftypes) # If no value has this type, then this statement should be unreachable. # Bail quickly now. - if !has_concrete_subtype(s) || isempty(ftypes) + if !has_concrete_subtype(s) || nf == 0 return Bottom end if isa(name, Conditional) @@ -870,12 +872,14 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if !(Int <: name || Symbol <: name) return Bottom end - if length(ftypes) == 1 + if nf == 1 return rewrap_unionall(unwrapva(ftypes[1]), s00) end # union together types of all fields t = Bottom - for _ft in ftypes + for i in 1:nf + _ft = ftypes[i] + setfield && isconst(s, i) && continue t = tmerge(t, rewrap_unionall(unwrapva(_ft), s00)) t === Any && break end @@ -888,12 +892,13 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if !isa(fld, Int) return Bottom end - nf = length(ftypes) if s <: Tuple && fld >= nf && isvarargtype(ftypes[nf]) return rewrap_unionall(unwrapva(ftypes[nf]), s00) end if fld < 1 || fld > nf return Bottom + elseif setfield && isconst(s, fld) + return Bottom end R = ftypes[fld] if isempty(s.parameters) @@ -902,8 +907,66 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) return rewrap_unionall(R, s00) end -setfield!_tfunc(o, f, v, order) = (@nospecialize; v) -setfield!_tfunc(o, f, v) = (@nospecialize; v) +function setfield!_tfunc(o, f, v, order) + @nospecialize + if !isvarargtype(order) + hasintersect(widenconst(order), Symbol) || return Bottom + end + return setfield!_tfunc(o, f, v) +end +function setfield!_tfunc(o, f, v) + @nospecialize + mutability_errorcheck(o) || return Bottom + ft = _getfield_tfunc(o, f, true) + ft === Bottom && return Bottom + hasintersect(widenconst(v), widenconst(ft)) || return Bottom + return v +end +function mutability_errorcheck(@nospecialize obj) + objt0 = widenconst(obj) + objt = unwrap_unionall(objt0) + if isa(objt, Union) + return mutability_errorcheck(rewrap_unionall(objt.a, objt0)) || + mutability_errorcheck(rewrap_unionall(objt.b, objt0)) + elseif isa(objt, DataType) + # Can't say anything about abstract types + isabstracttype(objt) && return true + return ismutabletype(objt) + end + return true +end + +function setfield!_nothrow(argtypes::Vector{Any}) + if length(argtypes) == 4 + order = argtypes[4] + order === Const(:not_atomic) || return false # currently setfield!_nothrow is assuming not atomic + else + length(argtypes) == 3 || return false + end + return setfield!_nothrow(argtypes[1], argtypes[2], argtypes[3]) +end +function setfield!_nothrow(s00, name, v) + @nospecialize + s0 = widenconst(s00) + s = unwrap_unionall(s0) + if isa(s, Union) + return setfield!_nothrow(rewrap_unionall(s.a, s00), name, v) && + setfield!_nothrow(rewrap_unionall(s.b, s00), name, v) + elseif isa(s, DataType) + # Can't say anything about abstract types + isabstracttype(s) && return false + ismutabletype(s) || return false + s.name.atomicfields == C_NULL || return false # TODO: currently we're only testing for ordering == :not_atomic + isa(name, Const) || return false + field = try_compute_fieldidx(s, name.val) + field === nothing && return false + # `try_compute_fieldidx` already check for field index bound. + isconst(s, field) && return false + v_expected = fieldtype(s0, field) + return v ⊑ v_expected + end + return false +end swapfield!_tfunc(o, f, v, order) = (@nospecialize; getfield_tfunc(o, f)) swapfield!_tfunc(o, f, v) = (@nospecialize; getfield_tfunc(o, f)) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 2fc06e67b639b..6d26d4226552b 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1639,6 +1639,112 @@ end @test getfield_tfunc(ARef{Int},Const(:x),Symbol,Symbol) === Union{} @test getfield_tfunc(ARef{Int},Const(:x),Bool,Bool) === Union{} +import Core.Compiler: setfield!_tfunc, setfield!_nothrow, Const +mutable struct XY{X,Y} + x::X + y::Y +end +mutable struct ABCDconst + const a + const b::Int + c + const d::Union{Int,Nothing} +end +@test setfield!_tfunc(Base.RefValue{Int}, Const(:x), Int) === Int +@test setfield!_tfunc(Base.RefValue{Int}, Const(:x), Int, Symbol) === Int +@test setfield!_tfunc(Base.RefValue{Int}, Const(1), Int) === Int +@test setfield!_tfunc(Base.RefValue{Int}, Const(1), Int, Symbol) === Int +@test setfield!_tfunc(Base.RefValue{Int}, Int, Int) === Int +@test setfield!_tfunc(Base.RefValue{Any}, Const(:x), Int) === Int +@test setfield!_tfunc(Base.RefValue{Any}, Const(:x), Int, Symbol) === Int +@test setfield!_tfunc(Base.RefValue{Any}, Const(1), Int) === Int +@test setfield!_tfunc(Base.RefValue{Any}, Const(1), Int, Symbol) === Int +@test setfield!_tfunc(Base.RefValue{Any}, Int, Int) === Int +@test setfield!_tfunc(XY{Any,Any}, Const(1), Int) === Int +@test setfield!_tfunc(XY{Any,Any}, Const(2), Float64) === Float64 +@test setfield!_tfunc(XY{Int,Float64}, Const(1), Int) === Int +@test setfield!_tfunc(XY{Int,Float64}, Const(2), Float64) === Float64 +@test setfield!_tfunc(ABCDconst, Const(:c), Any) === Any +@test setfield!_tfunc(ABCDconst, Const(3), Any) === Any +@test setfield!_tfunc(ABCDconst, Symbol, Any) === Any +@test setfield!_tfunc(ABCDconst, Int, Any) === Any +@test setfield!_tfunc(Union{Base.RefValue{Any},Some{Any}}, Const(:x), Int) === Int +@test setfield!_tfunc(Union{Base.RefValue,Some{Any}}, Const(:x), Int) === Int +@test setfield!_tfunc(Union{Base.RefValue{Any},Some{Any}}, Const(1), Int) === Int +@test setfield!_tfunc(Union{Base.RefValue,Some{Any}}, Const(1), Int) === Int +@test setfield!_tfunc(Union{Base.RefValue{Any},Some{Any}}, Symbol, Int) === Int +@test setfield!_tfunc(Union{Base.RefValue,Some{Any}}, Symbol, Int) === Int +@test setfield!_tfunc(Union{Base.RefValue{Any},Some{Any}}, Int, Int) === Int +@test setfield!_tfunc(Union{Base.RefValue,Some{Any}}, Int, Int) === Int +@test setfield!_tfunc(Any, Symbol, Int) === Int +@test setfield!_tfunc(Any, Int, Int) === Int +@test setfield!_tfunc(Any, Any, Int) === Int +@test setfield!_tfunc(Base.RefValue{Int}, Const(:x), Float64) === Union{} +@test setfield!_tfunc(Base.RefValue{Int}, Const(:x), Float64, Symbol) === Union{} +@test setfield!_tfunc(Base.RefValue{Int}, Const(1), Float64) === Union{} +@test setfield!_tfunc(Base.RefValue{Int}, Const(1), Float64, Symbol) === Union{} +@test setfield!_tfunc(Base.RefValue{Int}, Int, Float64) === Union{} +@test setfield!_tfunc(Base.RefValue{Any}, Const(:y), Int) === Union{} +@test setfield!_tfunc(Base.RefValue{Any}, Const(:y), Int, Bool) === Union{} +@test setfield!_tfunc(Base.RefValue{Any}, Const(2), Int) === Union{} +@test setfield!_tfunc(Base.RefValue{Any}, Const(2), Int, Bool) === Union{} +@test setfield!_tfunc(Base.RefValue{Any}, String, Int) === Union{} +@test setfield!_tfunc(Some{Any}, Const(:value), Int) === Union{} +@test setfield!_tfunc(Some, Const(:value), Int) === Union{} +@test setfield!_tfunc(Some{Any}, Const(1), Int) === Union{} +@test setfield!_tfunc(Some, Const(1), Int) === Union{} +@test setfield!_tfunc(Some{Any}, Symbol, Int) === Union{} +@test setfield!_tfunc(Some, Symbol, Int) === Union{} +@test setfield!_tfunc(Some{Any}, Int, Int) === Union{} +@test setfield!_tfunc(Some, Int, Int) === Union{} +@test setfield!_tfunc(Const(@__MODULE__), Const(:v), Int) === Union{} +@test setfield!_tfunc(Const(@__MODULE__), Int, Int) === Union{} +@test setfield!_tfunc(Module, Const(:v), Int) === Union{} +@test setfield!_tfunc(ABCDconst, Const(:a), Any) === Union{} +@test setfield!_tfunc(ABCDconst, Const(:b), Any) === Union{} +@test setfield!_tfunc(ABCDconst, Const(:d), Any) === Union{} +@test setfield!_tfunc(ABCDconst, Const(1), Any) === Union{} +@test setfield!_tfunc(ABCDconst, Const(2), Any) === Union{} +@test setfield!_tfunc(ABCDconst, Const(4), Any) === Union{} +@test setfield!_nothrow(Base.RefValue{Int}, Const(:x), Int) +@test setfield!_nothrow(Base.RefValue{Int}, Const(1), Int) +@test setfield!_nothrow(Base.RefValue{Any}, Const(:x), Int) +@test setfield!_nothrow(Base.RefValue{Any}, Const(1), Int) +@test setfield!_nothrow(XY{Any,Any}, Const(:x), Int) +@test setfield!_nothrow(XY{Any,Any}, Const(:x), Any) +@test setfield!_nothrow(XY{Int,Float64}, Const(:x), Int) +@test setfield!_nothrow(ABCDconst, Const(:c), Any) +@test setfield!_nothrow(ABCDconst, Const(3), Any) +@test !setfield!_nothrow(XY{Int,Float64}, Symbol, Any) +@test !setfield!_nothrow(XY{Int,Float64}, Int, Any) +@test !setfield!_nothrow(Base.RefValue{Int}, Const(:x), Any) +@test !setfield!_nothrow(Base.RefValue{Int}, Const(1), Any) +@test !setfield!_nothrow(Any[Base.RefValue{Any}, Const(:x), Int, Symbol]) +@test !setfield!_nothrow(Base.RefValue{Any}, Symbol, Int) +@test !setfield!_nothrow(Base.RefValue{Any}, Int, Int) +@test !setfield!_nothrow(XY{Int,Float64}, Const(:y), Int) +@test !setfield!_nothrow(XY{Int,Float64}, Symbol, Int) +@test !setfield!_nothrow(XY{Int,Float64}, Int, Int) +@test !setfield!_nothrow(ABCDconst, Const(:a), Any) +@test !setfield!_nothrow(ABCDconst, Const(:b), Any) +@test !setfield!_nothrow(ABCDconst, Const(:d), Any) +@test !setfield!_nothrow(ABCDconst, Symbol, Any) +@test !setfield!_nothrow(ABCDconst, Const(1), Any) +@test !setfield!_nothrow(ABCDconst, Const(2), Any) +@test !setfield!_nothrow(ABCDconst, Const(4), Any) +@test !setfield!_nothrow(ABCDconst, Int, Any) +@test !setfield!_nothrow(Union{Base.RefValue{Any},Some{Any}}, Const(:x), Int) +@test !setfield!_nothrow(Union{Base.RefValue,Some{Any}}, Const(:x), Int) +@test !setfield!_nothrow(Union{Base.RefValue{Any},Some{Any}}, Const(1), Int) +@test !setfield!_nothrow(Union{Base.RefValue,Some{Any}}, Const(1), Int) +@test !setfield!_nothrow(Union{Base.RefValue{Any},Some{Any}}, Symbol, Int) +@test !setfield!_nothrow(Union{Base.RefValue,Some{Any}}, Symbol, Int) +@test !setfield!_nothrow(Union{Base.RefValue{Any},Some{Any}}, Int, Int) +@test !setfield!_nothrow(Union{Base.RefValue,Some{Any}}, Int, Int) +@test !setfield!_nothrow(Any, Symbol, Int) +@test !setfield!_nothrow(Any, Int, Int) +@test !setfield!_nothrow(Any, Any, Int) + struct Foo_22708 x::Ptr{Foo_22708} end @@ -3181,7 +3287,7 @@ end @test Core.Compiler.return_type(apply26826, Tuple{typeof(===), Any, Vararg}) == Bool @test Core.Compiler.return_type(apply26826, Tuple{typeof(===), Any, Any, Vararg}) == Bool @test Core.Compiler.return_type(apply26826, Tuple{typeof(===), Any, Any, Any, Vararg}) == Union{} -@test Core.Compiler.return_type(apply26826, Tuple{typeof(setfield!), Vararg{Symbol}}) == Symbol +@test Core.Compiler.return_type(apply26826, Tuple{typeof(setfield!), Vararg{Symbol}}) == Union{} @test Core.Compiler.return_type(apply26826, Tuple{typeof(setfield!), Any, Vararg{Symbol}}) == Symbol @test Core.Compiler.return_type(apply26826, Tuple{typeof(setfield!), Any, Symbol, Vararg{Integer}}) == Integer @test Core.Compiler.return_type(apply26826, Tuple{typeof(setfield!), Any, Symbol, Integer, Vararg}) == Integer