From 33cfb1823316ab5f31efe137caa45f891d329546 Mon Sep 17 00:00:00 2001 From: Oscar Blumberg Date: Thu, 8 Jun 2017 21:57:46 -0400 Subject: [PATCH] add constant inference of `isdefined` --- base/inference.jl | 44 ++++++++++++++++++++++++++++++++++++++++++-- test/inference.jl | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 5ef16364df4b5a..eb146356567969 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -542,7 +542,47 @@ add_tfunc(===, 2, 2, end return Bool end) -add_tfunc(isdefined, 1, IInf, (args...)->Bool) +function isdefined_tfunc(args...) + arg1 = args[1] + if isa(arg1, Const) + a1 = typeof(arg1.val) + else + a1 = widenconst(arg1) + end + if isType(a1) + return Bool + end + a1 = unwrap_unionall(a1) + if isa(a1, DataType) && !a1.abstract + if a1 <: Array # TODO update when deprecation is removed + elseif a1 === Module + length(args) == 2 || return Bottom + sym = args[2] + Symbol <: widenconst(sym) || return Bottom + if isa(sym, Const) && isa(sym.val, Symbol) && isa(arg1, Const) && isdefined(arg1.val, sym.val) + return Const(true) + end + elseif length(args) == 2 && isa(args[2], Const) + val = args[2].val + idx::Int = 0 + if isa(val, Symbol) + idx = fieldindex(a1, val, false) + elseif isa(val, Int) + idx = val + else + return Bottom + end + if 1 <= idx <= a1.ninitialized + return Const(true) + elseif idx <= 0 || idx > nfields(a1) + return Const(false) + end + end + end + Bool +end +# TODO change IInf to 2 when deprecation is removed +add_tfunc(isdefined, 1, IInf, isdefined_tfunc) add_tfunc(Core.sizeof, 1, 1, x->Int) add_tfunc(nfields, 1, 1, function (x::ANY) @@ -3389,7 +3429,7 @@ end const _pure_builtins = Any[tuple, svec, fieldtype, apply_type, ===, isa, typeof, UnionAll, nfields] # known effect-free calls (might not be affect-free) -const _pure_builtins_volatile = Any[getfield, arrayref] +const _pure_builtins_volatile = Any[getfield, arrayref, isdefined] function is_pure_intrinsic(f::IntrinsicFunction) return !(f === Intrinsics.pointerref || # this one is volatile diff --git a/test/inference.jl b/test/inference.jl index 61d283c73368c3..e89a28a1e223b7 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -843,3 +843,46 @@ f21771(::Val{U}) where {U} = Tuple{g21771(U)} # ensure that we don't try to resolve cycles using uncached edges f21653() = f21653() @test code_typed(f21653, Tuple{}, optimize=false)[1] isa Pair{CodeInfo, typeof(Union{})} + +# constant inference of isdefined +let f(x) = isdefined(x, 2) ? 1 : "" + @test Base.return_types(f, (Tuple{Int,Int},)) == Any[Int] + @test Base.return_types(f, (Tuple{Int,},)) == Any[String] +end +let f(x) = isdefined(x, :re) ? 1 : "" + @test Base.return_types(f, (Complex64,)) == Any[Int] + @test Base.return_types(f, (Complex,)) == Any[Int] +end +let f(x) = isdefined(x, :NonExistentField) ? 1 : "" + @test Base.return_types(f, (Complex64,)) == Any[String] + @test Union{Int,String} <: Base.return_types(f, (AbstractArray,))[1] +end +import Core.Inference: Const, isdefined_tfunc, ⊑ +@test isdefined_tfunc(Complex64, Const(())) === Union{} +@test isdefined_tfunc(Complex64, Const(1)) === Const(true) +@test isdefined_tfunc(Complex64, Const(2)) === Const(true) +@test isdefined_tfunc(Complex64, Const(3)) === Const(false) +@test isdefined_tfunc(Complex64, Const(0)) === Const(false) +mutable struct SometimesDefined + x + function SometimesDefined() + v = new() + if rand(Bool) + v.x = 0 + end + return v + end +end +@test isdefined_tfunc(SometimesDefined, Const(:x)) == Bool +@test isdefined_tfunc(SometimesDefined, Const(:y)) === Const(false) +@test isdefined_tfunc(Const(Base), Const(:length)) === Const(true) +@test isdefined_tfunc(Const(Base), Symbol) == Bool +@test isdefined_tfunc(Const(Base), Const(:NotCurrentlyDefinedButWhoKnows)) == Bool +@test isdefined_tfunc(SimpleVector, Const(1)) === Const(true) +@test isdefined_tfunc(SimpleVector, Const(:length)) === Const(true) +@test Const(false) ⊑ isdefined_tfunc(Const(:x), Symbol) +@test Const(false) ⊑ isdefined_tfunc(Const(:x), Const(:y)) +@test isdefined_tfunc(Vector{Int}, Const(1)) == Bool +@test isdefined_tfunc(Vector{Any}, Const(1)) == Bool +@test isdefined_tfunc(Module, Any, Any) === Union{} +@test isdefined_tfunc(Module, Int) === Union{}