diff --git a/src/JET.jl b/src/JET.jl index b6aa4c664..02072f70a 100644 --- a/src/JET.jl +++ b/src/JET.jl @@ -494,6 +494,10 @@ end ignorenotfound(@nospecialize(t)) = t === NOT_FOUND ? Bottom : t +# location + +include("locinfo.jl") + # includes # ======== @@ -505,7 +509,6 @@ include("abstractinterpretation.jl") include("typeinfer.jl") include("analyzer.jl") include("jetcache.jl") -include("locinfo.jl") # top-level analysis include("graph.jl") include("virtualprocess.jl") diff --git a/src/abstractinterpretation.jl b/src/abstractinterpretation.jl index 767ffebc7..880f5b034 100644 --- a/src/abstractinterpretation.jl +++ b/src/abstractinterpretation.jl @@ -109,12 +109,9 @@ end bail_out_toplevel_call(analyzer::AbstractAnalyzer, ...) An overload for `abstract_call_gf_by_type(analyzer::AbstractAnalyzer, ...)`, which keeps - inference on non-concrete call sites in a toplevel frame created by - [`virtual_process`](@ref). +inference on non-concrete call sites in a toplevel frame created by [`virtual_process`](@ref). """ -function CC.bail_out_toplevel_call(analyzer::AbstractAnalyzer, @nospecialize(sig), sv) - return isa(sv.linfo.def, Module) && !isdispatchtuple(sig) && !istoplevel(analyzer, sv) -end +CC.bail_out_toplevel_call(analyzer::AbstractAnalyzer, @nospecialize(sig), sv) = false @doc """ bail_out_call(analyzer::AbstractAnalyzer, ...) @@ -310,7 +307,7 @@ end function update_reports!(analyzer::AbstractAnalyzer, sv::InferenceState) rs = get_to_be_updated(analyzer) if !isempty(rs) - vf = get_virtual_frame(analyzer, sv) + vf = get_virtual_frame(sv) for r in rs pushfirst!(r.vst, vf) end @@ -378,7 +375,7 @@ end end # @static if isdefined(CC, :abstract_invoke) function CC.abstract_eval_special_value(analyzer::AbstractAnalyzer, @nospecialize(e), vtypes::VarTable, sv::InferenceState) - toplevel = istoplevel(analyzer, sv) + toplevel = istoplevel(sv) if toplevel if isa(e, Slot) && is_global_slot(analyzer, e) if get_slottype((sv, get_currpc(sv)), e) === Bottom @@ -544,7 +541,7 @@ function (::BasicPass)(::Type{NonBooleanCondErrorReport}, analyzer::AbstractAnal end function CC.abstract_eval_statement(analyzer::AbstractAnalyzer, @nospecialize(e), vtypes::VarTable, sv::InferenceState) - if istoplevel(analyzer, sv) + if istoplevel(sv) if get_concretized(analyzer)[get_currpc(sv)] return Any # bail out if it has been interpreted by `ConcreteInterpreter` end @@ -556,7 +553,7 @@ end function CC.finish(me::InferenceState, analyzer::AbstractAnalyzer) @invoke finish(me::InferenceState, analyzer::AbstractInterpreter) - if istoplevel(analyzer, me) + if istoplevel(me) # find assignments of abstract global variables, and assign types to them, # so that later analysis can refer to them diff --git a/src/analyzer.jl b/src/analyzer.jl index c6d238d34..6f386d288 100644 --- a/src/analyzer.jl +++ b/src/analyzer.jl @@ -482,9 +482,9 @@ function maybe_initialize_caches!(analyzer::AbstractAnalyzer) end # check if we're in a toplevel module -@inline istoplevel(analyzer::AbstractAnalyzer, sv::InferenceState) = istoplevel(analyzer, sv.linfo) -@inline istoplevel(::AbstractAnalyzer, ::OptimizationState) = false # optimization never happen for top-level code -@inline istoplevel(analyzer::AbstractAnalyzer, linfo::MethodInstance) = get_toplevelmod(analyzer) === linfo.def +@inline istoplevel(sv::InferenceState) = istoplevel(sv.linfo) +@inline istoplevel(::OptimizationState) = false # optimization never happen for top-level code +@inline istoplevel(linfo::MethodInstance) = isa(linfo.def, Module) is_global_slot(analyzer::AbstractAnalyzer, slot::Int) = slot in keys(get_global_slots(analyzer)) is_global_slot(analyzer::AbstractAnalyzer, slot::Slot) = is_global_slot(analyzer, slot_id(slot)) diff --git a/src/interfaces.jl b/src/interfaces.jl index 87515a3ca..7eeb2901e 100644 --- a/src/interfaces.jl +++ b/src/interfaces.jl @@ -287,7 +287,7 @@ get_spec_args(T::Type{<:InferenceErrorReport}) = error("`get_spec # default constructor to create a report from abstract interpretation routine function (T::Type{<:InferenceErrorReport})(analyzer::AbstractAnalyzer, state, @nospecialize(spec_args...)) - vf = get_virtual_frame(analyzer, state) + vf = get_virtual_frame(state) msg = get_msg(T, analyzer, state, spec_args...) return T([vf], msg, vf.sig, spec_args...) end diff --git a/src/legacy/abstractinterpretation b/src/legacy/abstractinterpretation index d6676e2f7..3825cd64f 100644 --- a/src/legacy/abstractinterpretation +++ b/src/legacy/abstractinterpretation @@ -122,7 +122,7 @@ function abstract_call_gf_by_type(interp::AbstractAnalyzer, @nospecialize(f), ar edges = MethodInstance[] nonbot = 0 # the index of the only non-Bottom inference result if > 0 seen = 0 # number of signatures actually inferred - istoplevel = sv.linfo.def isa Module + # istoplevel = sv.linfo.def isa Module multiple_matches = napplicable > 1 if f !== nothing && napplicable == 1 && is_method_pure(applicable[1]::MethodMatch) @@ -142,7 +142,7 @@ function abstract_call_gf_by_type(interp::AbstractAnalyzer, @nospecialize(f), ar method = match.method sig = match.spec_types #=== abstract_call_gf_by_type patch point 2 start ===# - if istoplevel && !isdispatchtuple(sig) && !JET.istoplevel(interp, sv) # keep going for "our" toplevel frame + if #= istoplevel && !isdispatchtuple(sig) =# false # keep going for "our" toplevel frame #=== abstract_call_gf_by_type patch point 2 end ===# # only infer concrete call sites in top-level expressions add_remark!(interp, sv, "Refusing to infer non-concrete call site in top-level expression") diff --git a/src/locinfo.jl b/src/locinfo.jl index 15d5b5577..23748ada2 100644 --- a/src/locinfo.jl +++ b/src/locinfo.jl @@ -2,15 +2,15 @@ # ============= # get location information at the given program counter (or a current counter if not specified) -function get_virtual_frame(analyzer::AbstractAnalyzer, state::StateAtPC) - sig = get_sig(analyzer, state) +function get_virtual_frame(state::StateAtPC) + sig = get_sig(state) file, line = get_file_line(state) linfo = isa(state, MethodInstance) ? state : first(state).linfo return VirtualFrame(file, line, sig, linfo) end -get_virtual_frame(analyzer::AbstractAnalyzer, sv::InferenceState) = get_virtual_frame(analyzer, (sv, get_currpc(sv))) -function get_virtual_frame(analyzer::AbstractAnalyzer, linfo::MethodInstance) - sig = get_sig(analyzer, linfo) +get_virtual_frame(sv::InferenceState) = get_virtual_frame((sv, get_currpc(sv))) +function get_virtual_frame(linfo::MethodInstance) + sig = get_sig(linfo) file, line = get_file_line(linfo) return VirtualFrame(file, line, sig, linfo) end @@ -31,7 +31,7 @@ end # ========= # adapted from https://github.com/JuliaLang/julia/blob/0f11a7bb07d2d0d8413da05dadd47441705bf0dd/base/show.jl#L989-L1011 -function get_sig(analyzer::AbstractAnalyzer, l::MethodInstance) +function get_sig(l::MethodInstance) def = l.def ret = if isa(def, Method) if isdefined(def, :generator) && l === def.generator @@ -66,18 +66,18 @@ end end end -@inline get_sig(analyzer::AbstractAnalyzer, s::StateAtPC) = return _get_sig(analyzer, s, get_stmt(s)) +@inline get_sig(s::StateAtPC) = return _get_sig(s, get_stmt(s)) -@inline _get_sig(analyzer::AbstractAnalyzer, s::StateAtPC, @nospecialize(x)) = return first(_get_sig_type(analyzer, s, x))::Vector{Any} +@inline _get_sig(s::StateAtPC, @nospecialize(x)) = return first(_get_sig_type(s, x))::Vector{Any} -function _get_callsig(analyzer::AbstractAnalyzer, s::StateAtPC, @nospecialize(f), args::Vector{Any}; +function _get_callsig(s::StateAtPC, @nospecialize(f), args::Vector{Any}; splat::Bool = false) - sig = _get_sig(analyzer, s, f) + sig = _get_sig(s, f) push!(sig, '(') nargs = length(args) for (i, arg) in enumerate(args) - arg_sig = _get_sig(analyzer, s, arg) + arg_sig = _get_sig(s, arg) append!(sig, arg_sig) if i ≠ nargs push!(sig, ", ") @@ -90,7 +90,7 @@ function _get_callsig(analyzer::AbstractAnalyzer, s::StateAtPC, @nospecialize(f) return sig end -function _get_sig_type(analyzer::AbstractAnalyzer, s::StateAtPC, expr::Expr) +function _get_sig_type(s::StateAtPC, expr::Expr) head = expr.head if head === :call f = first(expr.args) @@ -103,16 +103,16 @@ function _get_sig_type(analyzer::AbstractAnalyzer, s::StateAtPC, expr::Expr) end f = args[2] args = args[3:end] - return _get_callsig(analyzer, s, f, args; splat = true), nothing + return _get_callsig(s, f, args; splat = true), nothing else - return _get_callsig(analyzer, s, f, args), nothing + return _get_callsig(s, f, args), nothing end elseif head === :invoke f = expr.args[2] args = expr.args[3:end] - return _get_callsig(analyzer, s, f, args), nothing + return _get_callsig(s, f, args), nothing elseif head === :(=) - return _get_sig_type(analyzer, s, last(expr.args)) + return _get_sig_type(s, last(expr.args)) elseif head === :static_parameter typ = widenconst(first(s).sptypes[first(expr.args)]) return Any['_', typ], typ @@ -120,7 +120,7 @@ function _get_sig_type(analyzer::AbstractAnalyzer, s::StateAtPC, expr::Expr) return Any[string(expr)], nothing end end -function _get_sig_type(analyzer::AbstractAnalyzer, (sv, _)::StateAtPC, ssa::SSAValue) +function _get_sig_type((sv, _)::StateAtPC, ssa::SSAValue) news = (sv, ssa.id) if isa(sv, OptimizationState) # when working on `OptimizationState`, the SSA traverse could be really long because @@ -129,22 +129,22 @@ function _get_sig_type(analyzer::AbstractAnalyzer, (sv, _)::StateAtPC, ssa::SSAV sig = Any["%$(ssa.id)", typ] else # XXX the same problem _may_ happen for `InferenceState` too ? - sig, sig_typ = _get_sig_type(analyzer, news, get_stmt(news)) + sig, sig_typ = _get_sig_type(news, get_stmt(news)) typ = widenconst(ignorelimited(ignorenotfound(get_ssavaluetype(news)))) sig_typ == typ || push!(sig, typ) # XXX I forgot why I added this line ... end return sig, typ end -function _get_sig_type(analyzer::AbstractAnalyzer, s::StateAtPC, slot::SlotNumber) +function _get_sig_type(s::StateAtPC, slot::SlotNumber) sv = first(s) name = get_slotname(sv, slot) sig = string(name) if isempty(sig) sig = string(slot) # fallback if no explicit slotname end - if istoplevel(analyzer, sv) + if istoplevel(sv) # this is a abstract global variable, form the global reference - return _get_sig_type(analyzer, s, GlobalRef(get_toplevelmod(analyzer), name)) + return _get_sig_type(s, GlobalRef(sv.linfo.def::Module, name)) else # we can use per-program counter type after inference t = (isa(sv, InferenceState) && sv.inferred) ? get_slottype(sv, slot) : get_slottype(s, slot) @@ -153,31 +153,32 @@ function _get_sig_type(analyzer::AbstractAnalyzer, s::StateAtPC, slot::SlotNumbe end end # NOTE `Argument` is introduced by optimization, and so we don't need to handle abstract global variable here, etc. -function _get_sig_type(analyzer::AbstractAnalyzer, (sv, _)::StateAtPC, arg::Argument) +function _get_sig_type((sv, _)::StateAtPC, arg::Argument) name = get_slotname(sv, arg.n) sig = string(name) typ = widenconst(ignorelimited(get_slottype(sv, arg))) # after optimization we shouldn't use `get_slottype(::StateAtPC, ::Any)` return Any[sig, typ], typ end -_get_sig_type(analyzer::AbstractAnalyzer, _::StateAtPC, gr::GlobalRef) = Any[string(gr.mod, '.', gr.name)], nothing -function _get_sig_type(analyzer::AbstractAnalyzer, s::StateAtPC, name::Symbol) - if istoplevel(analyzer, first(s)) +_get_sig_type(_::StateAtPC, gr::GlobalRef) = Any[string(gr.mod, '.', gr.name)], nothing +function _get_sig_type(s::StateAtPC, name::Symbol) + sv = first(s) + if istoplevel(sv) # this is concrete global variable, form the global reference - return _get_sig_type(analyzer, s, GlobalRef(get_toplevelmod(analyzer), name)) + return _get_sig_type(s, GlobalRef(sv.linfo.def, name)) else return Any[repr(name; context = :compact => true)], nothing end end -function _get_sig_type(analyzer::AbstractAnalyzer, s::StateAtPC, gotoifnot::GotoIfNot) - sig = Any[string("goto %", gotoifnot.dest, " if not "), _get_sig(analyzer, s, gotoifnot.cond)...] +function _get_sig_type(s::StateAtPC, gotoifnot::GotoIfNot) + sig = Any[string("goto %", gotoifnot.dest, " if not "), _get_sig(s, gotoifnot.cond)...] return sig, nothing end -function _get_sig_type(analyzer::AbstractAnalyzer, s::StateAtPC, rn::ReturnNode) - sig = is_unreachable(rn) ? Any["unreachable"] : Any["return ", _get_sig(analyzer, s, rn.val)...] +function _get_sig_type(s::StateAtPC, rn::ReturnNode) + sig = is_unreachable(rn) ? Any["unreachable"] : Any["return ", _get_sig(s, rn.val)...] return sig, nothing end -function _get_sig_type(analyzer::AbstractAnalyzer, ::StateAtPC, qn::QuoteNode) +function _get_sig_type(::StateAtPC, qn::QuoteNode) typ = typeof(qn.value) return Any[string(qn), typ], typ end -_get_sig_type(analyzer::AbstractAnalyzer, ::StateAtPC, @nospecialize(x)) = Any[repr(x; context = :compact => true)], nothing +_get_sig_type(::StateAtPC, @nospecialize(x)) = Any[repr(x; context = :compact => true)], nothing diff --git a/src/tfuncs.jl b/src/tfuncs.jl index 8e6023cac..48720adb3 100644 --- a/src/tfuncs.jl +++ b/src/tfuncs.jl @@ -77,7 +77,7 @@ end get_spec_args(report::SeriousExceptionReport) = (report.err,) function SeriousExceptionReport(analyzer::AbstractAnalyzer, state::InferenceState, err) - vf = get_virtual_frame(analyzer, state) + vf = get_virtual_frame(state) msg = string(first(split(sprint(showerror, err), '\n'))) ret = SeriousExceptionReport([vf], msg, vf.sig, err) push!(get_throw_locs(analyzer), get_lin((state, get_currpc(state)))) @@ -203,7 +203,7 @@ function istoplevel_globalref(analyzer::AbstractAnalyzer, sv::InferenceState) def.name === :getproperty || return false def.sig === Tuple{typeof(getproperty), Module, Symbol} || return false parent = sv.parent - return !isnothing(parent) && istoplevel(analyzer, parent) + return !isnothing(parent) && istoplevel(parent) end # `return_type_tfunc` internally uses `abstract_call` to model `Core.Compiler.return_type` diff --git a/src/typeinfer.jl b/src/typeinfer.jl index 359190413..42670b01b 100644 --- a/src/typeinfer.jl +++ b/src/typeinfer.jl @@ -217,7 +217,7 @@ function report_undefined_local_slots!(analyzer::AbstractAnalyzer, frame::Infere sym = stmt.args[1]::Symbol # slots in toplevel frame may be a abstract global slot - istoplevel(analyzer, frame) && is_global_slot(analyzer, sym) && continue + istoplevel(frame) && is_global_slot(analyzer, sym) && continue if unsound next_idx = idx + 1 @@ -257,12 +257,12 @@ This is reported only when it's not caught by control flow. throw_calls::Vector{Tuple{Int,Expr}} # (pc, call) end function UncaughtExceptionReport(analyzer::AbstractAnalyzer, sv::InferenceState, throw_calls::Vector{Tuple{Int,Expr}}) - vf = get_virtual_frame(analyzer, sv.linfo) + vf = get_virtual_frame(sv.linfo) msg = length(throw_calls) == 1 ? "may throw" : "may throw either of" sig = Any[] ncalls = length(throw_calls) for (i, (pc, call)) in enumerate(throw_calls) - call_sig = _get_sig(analyzer, (sv, pc), call) + call_sig = _get_sig((sv, pc), call) append!(sig, call_sig) i ≠ ncalls && push!(sig, ", ") end @@ -309,7 +309,7 @@ function is_throw_call_expr(analyzer::AbstractAnalyzer, frame::InferenceState, @ if isa(e, Expr) if e.head === :call f = e.args[1] - if istoplevel(analyzer, frame) && isa(f, Symbol) + if istoplevel(frame) && isa(f, Symbol) f = GlobalRef(get_toplevelmod(analyzer), f) end if isa(f, GlobalRef)