diff --git a/Project.toml b/Project.toml index d9786244..b21ad157 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Cthulhu" uuid = "f68482b8-f384-11e8-15f7-abe071a5a75f" authors = ["Valentin Churavy and contributors"] -version = "2.13.0" +version = "2.14.0" [deps] CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" diff --git a/src/Cthulhu.jl b/src/Cthulhu.jl index ed166a7e..99bd3e0c 100644 --- a/src/Cthulhu.jl +++ b/src/Cthulhu.jl @@ -880,6 +880,9 @@ function ascend(term, mi; interp::AbstractInterpreter=NativeInterpreter(), kwarg end end end + if !isa(mi, MethodInstance) + error("You can only descend into known calls. If you tried to descend into a runtime-dispatched signature, try its caller instead.") + end # The main application of `ascend` is finding cases of non-inferrability, so the # warn highlighting is useful. interp′ = CthulhuInterpreter(interp) diff --git a/src/backedges.jl b/src/backedges.jl index ba4aab44..b33cec35 100644 --- a/src/backedges.jl +++ b/src/backedges.jl @@ -147,6 +147,8 @@ specTypes(mi::MethodInstance) = mi.specTypes instance(mi::MethodInstance) = mi nextnode(mi, edge) = edge +instance(@nospecialize(tt::Type)) = tt + instance(sfs::Vector{StackTraces.StackFrame}) = isempty(sfs) ? CC.Timings.ROOTmi : sfs[end].linfo::MethodInstance # we checked this type condition within `buildframes` method(sfs::Vector{StackTraces.StackFrame}) = method(instance(sfs)) backedges(sframes::Vector{StackTraces.StackFrame}) = (ret = sframes[2:end]; isempty(ret) ? () : (ret,)) @@ -188,7 +190,7 @@ function treelist!(parent::Node, io::IO, mi, indent::AbstractString, visited::Ba indent *= " " for edge in backedges(mi) str = indent * callstring(io, edge) - child = Node(Data(str, instance(edge)), parent) + child = Node(typeof(parent.data)(str, instance(edge)), parent) treelist!(child, io, nextnode(mi, edge), indent, visited) end return parent diff --git a/src/callsite.jl b/src/callsite.jl index afa7ce93..a8959a83 100644 --- a/src/callsite.jl +++ b/src/callsite.jl @@ -506,6 +506,7 @@ end # maybe_callsite for higher-level inputs maybe_callsite(cs::Callsite, callee::MethodInstance) = maybe_callsite(cs.info, callee) +maybe_callsite(cs::Callsite, @nospecialize(tt::Type)) = maybe_callsite(cs.info, tt) maybe_callsite(info::CallInfo, callee::MethodInstance) = maybe_callsite(get_mi(info), callee) # Special CallInfo cases: function maybe_callsite(info::MultiCallInfo, callee::MethodInstance) @@ -517,4 +518,19 @@ end maybe_callsite(info::PureCallInfo, mi::MethodInstance) = mi.specTypes <: Tuple{mapany(Core.Typeof ∘ unwrapconst, info.argtypes)...} maybe_callsite(info::RTCallInfo, mi::MethodInstance) = false +function maybe_callsite(info::RTCallInfo, @nospecialize(tt::Type)) + isa(tt, Union) && return maybe_callsite(info, tt.a) || maybe_callsite(info, tt.b) + isa(tt, DataType) || return false + typeof(info.f) === tt.parameters[1] || return false + for (a, b) in zip(info.argtyps, tt.parameters[2:end]) + a === b || return false + end + return true +end +function maybe_callsite(info::MICallInfo, @nospecialize(tt::Type)) + return tt <: info.mi.specTypes +end + +maybe_callsite(info::CallInfo, @nospecialize(tt::Type)) = false + unwrapconst(@nospecialize(arg)) = arg isa Core.Const ? arg.val : arg diff --git a/src/reflection.jl b/src/reflection.jl index 34eeb374..ca544988 100644 --- a/src/reflection.jl +++ b/src/reflection.jl @@ -299,7 +299,7 @@ function callinfo(sig, rt, max_methods=-1; world=get_world_counter()) return MultiCallInfo(sig, rt, callinfos) end -function find_caller_of(interp::AbstractInterpreter, callee::MethodInstance, caller::MethodInstance; allow_unspecialized::Bool=false) +function find_caller_of(interp::AbstractInterpreter, callee::Union{MethodInstance,Type}, caller::MethodInstance; allow_unspecialized::Bool=false) interp′ = CthulhuInterpreter(interp) do_typeinf!(interp′, caller) locs = Tuple{Core.LineInfoNode,Int}[]