From 6540021f09429f954c0ff6bed2a08d70cff9eb74 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 21 Feb 2017 15:40:09 -0500 Subject: [PATCH] fix #20704, `pure` annotation should not skip method errors --- base/inference.jl | 74 +++++++++++++++++++++++++---------------------- test/inference.jl | 22 ++++++++++++++ 2 files changed, 61 insertions(+), 35 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index eb52cbde5f204..41b1fab6c6089 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -64,7 +64,9 @@ end # The type of a value might be constant struct Const val - Const(v::ANY) = new(v) + actual::Bool # if true, we obtained `val` by actually calling a @pure function + Const(v::ANY) = new(v, false) + Const(v::ANY, a::Bool) = new(v, a) end # The type of a value might be Bool, @@ -191,7 +193,7 @@ mutable struct InferenceState vararg_type = rewrap(vararg_type, linfo.specTypes) end s_types[1][la] = VarState(vararg_type, false) - src.slottypes[la] = widenconst(vararg_type) + src.slottypes[la] = vararg_type la -= 1 end end @@ -220,11 +222,11 @@ mutable struct InferenceState end i == laty && (lastatype = atyp) s_types[1][i] = VarState(atyp, false) - src.slottypes[i] = widenconst(atyp) + src.slottypes[i] = atyp end for i = (atail + 1):la s_types[1][i] = VarState(lastatype, false) - src.slottypes[i] = widenconst(lastatype) + src.slottypes[i] = lastatype end else @assert la == 0 # wrong number of arguments @@ -1544,7 +1546,7 @@ function return_type_tfunc(argtypes::ANY, vtypes::VarTable, sv::InferenceState) return NF end -function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, vtypes::VarTable, sv::InferenceState) +function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, sv::InferenceState) for i = 2:length(argtypes) a = argtypes[i] if !(isa(a,Const) || isconstType(a)) @@ -1569,7 +1571,7 @@ function pure_eval_call(f::ANY, argtypes::ANY, atype::ANY, vtypes::VarTable, sv: try value = Core._apply_pure(f, args) # TODO: add some sort of edge(s) - return abstract_eval_constant(value) + return Const(value,true) catch return false end @@ -1804,7 +1806,7 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect end atype = argtypes_to_type(argtypes) - t = pure_eval_call(f, argtypes, atype, vtypes, sv) + t = pure_eval_call(f, argtypes, atype, sv) t !== false && return t if istopfunction(tm, f, :promote_type) || istopfunction(tm, f, :typejoin) @@ -1976,14 +1978,7 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState) return t end -const Type_Array = Const(Array) - -function abstract_eval_constant(x::ANY) - if x === Array - return Type_Array - end - return Const(x) -end +const abstract_eval_constant = Const function abstract_eval_global(M::Module, s::Symbol) if isdefined(M,s) && isconst(M,s) @@ -2927,28 +2922,33 @@ function optimize(me::InferenceState) if isa(me.bestguess, Const) || isconstType(me.bestguess) me.const_ret = true - ispure = me.src.pure - if !ispure && length(me.src.code) < 10 - ispure = true + proven_pure = false + # must be proven pure to use const_api; otherwise we might skip throwing errors + # (issue #20704) + # TODO: Improve this analysis; if a function is marked @pure we should really + # only care about certain errors (e.g. method errors and type errors). + if length(me.src.code) < 10 + proven_pure = true for stmt in me.src.code if !statement_effect_free(stmt, me.src, me.mod) - ispure = false + proven_pure = false break end end - if ispure + if proven_pure for fl in me.src.slotflags if (fl & Slot_UsedUndef) != 0 - ispure = false + proven_pure = false break end end end end - me.src.pure = ispure + if proven_pure + me.src.pure = true + end - do_coverage = coverage_enabled() - if ispure && !do_coverage + if proven_pure && !coverage_enabled() # use constant calling convention # Do not emit `jlcall_api == 2` if coverage is enabled # so that we don't need to add coverage support @@ -3079,7 +3079,7 @@ function annotate_slot_load!(e::Expr, vtypes::VarTable, sv::InferenceState, unde undefs[id] = true end # add type annotations where needed - if !(sv.src.slottypes[id] <: vt) + if !(sv.src.slottypes[id] ⊑ vt) e.args[i] = TypedSlot(id, vt) end end @@ -3222,6 +3222,7 @@ end # we also need to preserve the type for any untyped load of a DataType # since codegen optimizations of functions like `is` will depend on knowing it function widen_slot_type(ty::ANY, untypedload::Bool) + ty = widenconst(ty) if isa(ty, DataType) if untypedload || isbits(ty) || isdefined(ty, :instance) return ty @@ -3767,16 +3768,6 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference atype_unlimited, method.sig) methsp = methsp::SimpleVector end - # check whether call can be inlined to just a quoted constant value - if isa(f, widenconst(ft)) && !method.isstaged && (method.source.pure || f === return_type) - if isconstType(e.typ) - return inline_as_constant(e.typ.parameters[1], argexprs, sv, - invoke_data) - elseif isa(e.typ,Const) - return inline_as_constant(e.typ.val, argexprs, sv, - invoke_data) - end - end methsig = method.sig if !(atype <: metharg) @@ -3784,6 +3775,19 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference invoke_data) end + # check whether call can be inlined to just a quoted constant value + if isa(f, widenconst(ft)) && !method.isstaged + if f === return_type + if isconstType(e.typ) + return inline_as_constant(e.typ.parameters[1], argexprs, sv, invoke_data) + elseif isa(e.typ,Const) + return inline_as_constant(e.typ.val, argexprs, sv, invoke_data) + end + elseif method.source.pure && isa(e.typ,Const) && e.typ.actual + return inline_as_constant(e.typ.val, argexprs, sv, invoke_data) + end + end + argexprs0 = argexprs na = Int(method.nargs) # check for vararg function diff --git a/test/inference.jl b/test/inference.jl index 0f637df2ea25a..d2b94f93c079f 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -648,3 +648,25 @@ let A = 1:2, z = zip(A, A, A, A, A, A, A, A, A, A, A, A) @test z isa Core.Inference.limit_type_depth(typeof(z), 0) @test start(z) == (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, 1))))))))))) end + +# issue #20704 +f20704(::Int) = 1 +Base.@pure b20704(x::ANY) = f20704(x) +@test b20704(42) === 1 +@test_throws MethodError b20704(42.0) + +bb20704() = b20704(Any[1.0][1]) +@test_throws MethodError bb20704() + +v20704() = Val{b20704(Any[1.0][1])} +@test_throws MethodError v20704() +@test Base.return_types(v20704, ()) == Any[Type{Val{1}}] + +Base.@pure g20704(::Int) = 1 +h20704(x::ANY) = g20704(x) +@test g20704(1) === 1 +@test_throws MethodError h20704(1.2) + +Base.@pure c20704() = (f20704(1.0); 1) +d20704() = c20704() +@test_throws MethodError d20704()