diff --git a/src/codeedges.jl b/src/codeedges.jl index ed0c037..d6697f3 100644 --- a/src/codeedges.jl +++ b/src/codeedges.jl @@ -1,5 +1,3 @@ -const NamedVar = Union{Symbol,GlobalRef} - ## Phase 1: direct links # There are 3 types of entities to track: ssavalues (line/statement numbers), slots, and named objects. @@ -9,42 +7,44 @@ const NamedVar = Union{Symbol,GlobalRef} struct Links ssas::Vector{Int} slots::Vector{Int} - names::Vector{NamedVar} + names::Vector{GlobalRef} end -Links() = Links(Int[], Int[], NamedVar[]) +Links() = Links(Int[], Int[], GlobalRef[]) function Base.show(io::IO, l::Links) print(io, "ssas: ", showempty(l.ssas), ", slots: ", showempty(l.slots), ", names: ") - print(IOContext(io, :typeinfo=>Vector{NamedVar}), showempty(l.names)) + print(IOContext(io, :typeinfo=>Vector{GlobalRef}), showempty(l.names)) print(io, ';') end struct CodeLinks + thismod::Module ssapreds::Vector{Links} ssasuccs::Vector{Links} slotpreds::Vector{Links} slotsuccs::Vector{Links} slotassigns::Vector{Vector{Int}} - namepreds::Dict{NamedVar,Links} - namesuccs::Dict{NamedVar,Links} - nameassigns::Dict{NamedVar,Vector{Int}} + namepreds::Dict{GlobalRef,Links} + namesuccs::Dict{GlobalRef,Links} + nameassigns::Dict{GlobalRef,Vector{Int}} end -function CodeLinks(nlines::Int, nslots::Int) +function CodeLinks(thismod::Module, nlines::Int, nslots::Int) makelinks(n) = [Links() for _ = 1:n] - return CodeLinks(makelinks(nlines), + return CodeLinks(thismod, + makelinks(nlines), makelinks(nlines), makelinks(nslots), makelinks(nslots), [Int[] for _ = 1:nslots], - Dict{NamedVar,Links}(), - Dict{NamedVar,Links}(), - Dict{NamedVar,Vector{Int}}()) + Dict{GlobalRef,Links}(), + Dict{GlobalRef,Links}(), + Dict{GlobalRef,Vector{Int}}()) end -function CodeLinks(src::CodeInfo) - cl = CodeLinks(length(src.code), length(src.slotnames)) +function CodeLinks(thismod::Module, src::CodeInfo) + cl = CodeLinks(thismod, length(src.code), length(src.slotnames)) direct_links!(cl, src) end @@ -175,7 +175,7 @@ end function namedkeys(cl::CodeLinks) - ukeys = Set{NamedVar}() + ukeys = Set{GlobalRef}() for c in (cl.namepreds, cl.namesuccs, cl.nameassigns) for k in keys(c) push!(ukeys, k) @@ -203,20 +203,23 @@ function direct_links!(cl::CodeLinks, src::CodeInfo) end end - P = Pair{Union{SSAValue,SlotNumber,NamedVar},Links} + P = Pair{Union{SSAValue,SlotNumber,GlobalRef},Links} for (i, stmt) in enumerate(src.code) if isexpr(stmt, :thunk) && isa(stmt.args[1], CodeInfo) - icl = CodeLinks(stmt.args[1]) + icl = CodeLinks(cl.thismod, stmt.args[1]) add_inner!(cl, icl, i) continue elseif isa(stmt, Expr) && stmt.head ∈ trackedheads if stmt.head === :method && length(stmt.args) === 3 && isa(stmt.args[3], CodeInfo) - icl = CodeLinks(stmt.args[3]) + icl = CodeLinks(cl.thismod, stmt.args[3]) add_inner!(cl, icl, i) end name = stmt.args[1] - if isa(name, Symbol) + if isa(name, GlobalRef) || isa(name, Symbol) + if isa(name, Symbol) + name = GlobalRef(cl.thismod, name) + end assign = get(cl.nameassigns, name, nothing) if assign === nothing cl.nameassigns[name] = assign = Int[] @@ -228,6 +231,10 @@ function direct_links!(cl::CodeLinks, src::CodeInfo) end target = P(name, targetstore) add_links!(target, stmt, cl) + elseif name in (nothing, false) + else + @show stmt + error("name ", typeof(name), " not recognized") end rhs = stmt target = P(SSAValue(i), cl.ssapreds[i]) @@ -240,7 +247,10 @@ function direct_links!(cl::CodeLinks, src::CodeInfo) id = lhs.id target = P(SlotNumber(id), cl.slotpreds[id]) push!(cl.slotassigns[id], i) - elseif isa(lhs, NamedVar) + elseif isa(lhs, GlobalRef) || isa(lhs, Symbol) + if isa(lhs, Symbol) + lhs = GlobalRef(cl.thismod, lhs) + end targetstore = get(cl.namepreds, lhs, nothing) if targetstore === nothing cl.namepreds[lhs] = targetstore = Links() @@ -263,9 +273,9 @@ function direct_links!(cl::CodeLinks, src::CodeInfo) return cl end -function add_links!(target::Pair{Union{SSAValue,SlotNumber,NamedVar},Links}, @nospecialize(stmt), cl::CodeLinks) +function add_links!(target::Pair{Union{SSAValue,SlotNumber,GlobalRef},Links}, @nospecialize(stmt), cl::CodeLinks) _targetid, targetstore = target - targetid = _targetid::Union{SSAValue,SlotNumber,NamedVar} + targetid = _targetid::Union{SSAValue,SlotNumber,GlobalRef} # Adds bidirectional edges if @isssa(stmt) stmt = stmt::AnySSAValue @@ -275,7 +285,10 @@ function add_links!(target::Pair{Union{SSAValue,SlotNumber,NamedVar},Links}, @no stmt = stmt::AnySlotNumber push!(targetstore, SlotNumber(stmt.id)) push!(cl.slotsuccs[stmt.id], targetid) - elseif isa(stmt, Symbol) || isa(stmt, GlobalRef) # NamedVar + elseif isa(stmt, GlobalRef) || isa(stmt, Symbol) + if isa(stmt, Symbol) + stmt = GlobalRef(cl.thismod, stmt) + end push!(targetstore, stmt) namestore = get(cl.namesuccs, stmt, nothing) if namestore === nothing @@ -311,7 +324,7 @@ function Base.push!(l::Links, id) k = id.id k ∉ l.slots && push!(l.slots, k) else - id = id::NamedVar + id = id::GlobalRef id ∉ l.names && push!(l.names, id) end return id @@ -355,9 +368,9 @@ end struct CodeEdges preds::Vector{Vector{Int}} succs::Vector{Vector{Int}} - byname::Dict{NamedVar,Variable} + byname::Dict{GlobalRef,Variable} end -CodeEdges(n::Integer) = CodeEdges([Int[] for i = 1:n], [Int[] for i = 1:n], Dict{Union{GlobalRef,Symbol},Variable}()) +CodeEdges(n::Integer) = CodeEdges([Int[] for i = 1:n], [Int[] for i = 1:n], Dict{GlobalRef,Variable}()) function Base.show(io::IO, edges::CodeEdges) println(io, "CodeEdges:") @@ -383,10 +396,10 @@ Analyze `src` and determine the chain of dependencies. - `edges.preds[i]` lists the preceding statements that statement `i` depends on. - `edges.succs[i]` lists the succeeding statements that depend on statement `i`. - `edges.byname[v]` returns information about the predecessors, successors, and assignment statements - for an object `v::$NamedVar`. + for an object `v::GlobalRef`. """ -function CodeEdges(src::CodeInfo) - cl = CodeLinks(src) +function CodeEdges(mod::Module, src::CodeInfo) + cl = CodeLinks(mod, src) CodeEdges(src, cl) end @@ -412,7 +425,10 @@ function CodeEdges(src::CodeInfo, cl::CodeLinks) id = lhs.id linkpreds, linksuccs, listassigns = cl.slotpreds[id], cl.slotsuccs[id], cl.slotassigns[id] else - lhs = lhs::NamedVar + lhs = lhs::Union{GlobalRef,Symbol} + if lhs isa Symbol + lhs = GlobalRef(cl.thismod, lhs) + end linkpreds = get(cl.namepreds, lhs, emptylink) linksuccs = get(cl.namesuccs, lhs, emptylink) listassigns = get(cl.nameassigns, lhs, emptylist) @@ -546,7 +562,7 @@ function terminal_preds(i::Int, edges::CodeEdges) end """ - isrequired = lines_required(obj::$NamedVar, src::CodeInfo, edges::CodeEdges) + isrequired = lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges) isrequired = lines_required(idx::Int, src::CodeInfo, edges::CodeEdges) Determine which lines might need to be executed to evaluate `obj` or the statement indexed by `idx`. @@ -556,16 +572,16 @@ will end up skipping a subset of such statements, perhaps while repeating others See also [`lines_required!`](@ref) and [`selective_eval!`](@ref). """ -function lines_required(obj::NamedVar, src::CodeInfo, edges::CodeEdges; kwargs...) +function lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges; kwargs...) isrequired = falses(length(edges.preds)) - objs = Set{NamedVar}([obj]) + objs = Set{GlobalRef}([obj]) return lines_required!(isrequired, objs, src, edges; kwargs...) end function lines_required(idx::Int, src::CodeInfo, edges::CodeEdges; kwargs...) isrequired = falses(length(edges.preds)) isrequired[idx] = true - objs = Set{NamedVar}() + objs = Set{GlobalRef}() return lines_required!(isrequired, objs, src, edges; kwargs...) end @@ -583,7 +599,7 @@ For example, use `norequire = LoweredCodeUtils.exclude_named_typedefs(src, edges extracting method signatures and not evaluating new definitions. """ function lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges; kwargs...) - objs = Set{NamedVar}() + objs = Set{GlobalRef}() return lines_required!(isrequired, objs, src, edges; kwargs...) end @@ -643,7 +659,7 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo, end function add_requests!(isrequired, objs, edges::CodeEdges, norequire) - objsnew = Set{NamedVar}() + objsnew = Set{GlobalRef}() for obj in objs add_obj!(isrequired, objsnew, obj, edges, norequire) end diff --git a/src/packagedef.jl b/src/packagedef.jl index 8fb0de7..75833e8 100644 --- a/src/packagedef.jl +++ b/src/packagedef.jl @@ -42,10 +42,10 @@ if ccall(:jl_generating_output, Cint, ()) == 1 end lwr = Meta.lower(@__MODULE__, ex) src = lwr.args[1] - edges = CodeEdges(src) - isrequired = lines_required(:s, src, edges) - lines_required(:s, src, edges; norequire=()) - lines_required(:s, src, edges; norequire=exclude_named_typedefs(src, edges)) + edges = CodeEdges(@__MODULE__, src) + isrequired = lines_required(GlobalRef(@__MODULE__, :s), src, edges) + lines_required(GlobalRef(@__MODULE__, :s), src, edges; norequire=()) + lines_required(GlobalRef(@__MODULE__, :s), src, edges; norequire=exclude_named_typedefs(src, edges)) for isreq in (isrequired, convert(Vector{Bool}, isrequired)) lines_required!(isreq, src, edges; norequire=()) lines_required!(isreq, src, edges; norequire=exclude_named_typedefs(src, edges)) diff --git a/src/utils.jl b/src/utils.jl index 17afd3c..384dc5a 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -85,6 +85,8 @@ function ismethod_with_name(src, stmt, target::AbstractString; reentrant::Bool=f name = src.code[name.id] elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.svec) name = name.args[2] + elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.Typeof) + name = name.args[2] elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.apply_type) for arg in name.args[2:end] ismethod_with_name(src, arg, target; reentrant=true) && return true @@ -120,7 +122,11 @@ function isanonymous_typedef(stmt) stmt = isa(stmt.args[3], Core.SSAValue) ? src.code[end-3] : src.code[end-2] isexpr(stmt, :(=)) || return false name = stmt.args[1] - isa(name, Symbol) || return false + if isa(name, GlobalRef) + name = name.name + else + isa(name, Symbol) || return false + end else name = stmt.args[2]::Symbol end diff --git a/test/codeedges.jl b/test/codeedges.jl index 7910d3d..85c44c1 100644 --- a/test/codeedges.jl +++ b/test/codeedges.jl @@ -61,17 +61,17 @@ module ModSelective end end frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModSelective, src) # Check that the result of direct evaluation agrees with selective evaluation Core.eval(ModEval, ex) - isrequired = lines_required(:x, src, edges) + isrequired = lines_required(GlobalRef(ModSelective, :x), src, edges) # theere is too much diversity in lowering across Julia versions to make it useful to test `sum(isrequired)` selective_eval_fromstart!(frame, isrequired) @test ModSelective.x === ModEval.x @test allmissing(ModSelective, (:y, :z, :a, :b, :k)) @test !allmissing(ModSelective, (:x, :y)) # add :y here to test the `all` part of the test itself # To evaluate z we need to do all the computations for y - isrequired = lines_required(:z, src, edges) + isrequired = lines_required(GlobalRef(ModSelective, :z), src, edges) selective_eval_fromstart!(frame, isrequired) @test ModSelective.y === ModEval.y @test ModSelective.z === ModEval.z @@ -82,7 +82,7 @@ module ModSelective end @test ModSelective.b === ModEval.b # Test that we get two separate evaluations of k @test allmissing(ModSelective, (:k,)) - isrequired = lines_required(:k, src, edges) + isrequired = lines_required(GlobalRef(ModSelective, :k), src, edges) selective_eval_fromstart!(frame, isrequired) @test ModSelective.k != ModEval.k @@ -101,8 +101,8 @@ module ModSelective end end frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) - isrequired = lines_required(:a2, src, edges) + edges = CodeEdges(ModSelective, src) + isrequired = lines_required(GlobalRef(ModSelective, :a2), src, edges) selective_eval_fromstart!(frame, isrequired, #=istoplevel=#true) Core.eval(ModEval, ex) @test ModSelective.a2 === ModEval.a2 == 1 @@ -122,8 +122,8 @@ module ModSelective end end frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) - isrequired = lines_required(:a3, src, edges) + edges = CodeEdges(ModSelective, src) + isrequired = lines_required(GlobalRef(ModSelective, :a3), src, edges) selective_eval_fromstart!(frame, isrequired) Core.eval(ModEval, ex) @test ModSelective.a3 === ModEval.a3 == 2 @@ -141,8 +141,8 @@ module ModSelective end end frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) - isrequired = lines_required(:valcf, src, edges) + edges = CodeEdges(ModSelective, src) + isrequired = lines_required(GlobalRef(ModSelective, :valcf), src, edges) selective_eval_fromstart!(frame, isrequired) @test ModSelective.valcf == 4 @@ -158,8 +158,8 @@ module ModSelective end end frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) - isrequired = lines_required(:c_os, src, edges) + edges = CodeEdges(ModSelective, src) + isrequired = lines_required(GlobalRef(ModSelective, :c_os), src, edges) @test sum(isrequired) >= length(isrequired) - 3 selective_eval_fromstart!(frame, isrequired) Core.eval(ModEval, ex) @@ -179,7 +179,7 @@ module ModSelective end @test ModEval.bar() == 1 frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModSelective, src) # Mark just the load of Core.eval haseval(stmt) = (isa(stmt, Expr) && JuliaInterpreter.hasarg(isequal(:eval), stmt.args)) || (isa(stmt, Expr) && stmt.head === :call && is_quotenode(stmt.args[1], Core.eval)) @@ -210,8 +210,8 @@ module ModSelective end @test ModSelective.k11 == 11 @test 3 <= ModSelective.s11 <= 15 Core.eval(ModSelective, :(k11 = 0; s11 = -1)) - edges = CodeEdges(frame.framecode.src) - isrequired = lines_required(:s11, frame.framecode.src, edges) + edges = CodeEdges(ModSelective, frame.framecode.src) + isrequired = lines_required(GlobalRef(ModSelective, :s11), frame.framecode.src, edges) selective_eval_fromstart!(frame, isrequired, true) @test ModSelective.k11 == 0 @test 3 <= ModSelective.s11 <= 15 @@ -220,9 +220,9 @@ module ModSelective end ex = :(abstract type StructParent{T, N} <: AbstractArray{T, N} end) frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModSelective, src) # Check that the StructParent name is discovered everywhere it is used - var = edges.byname[:StructParent] + var = edges.byname[GlobalRef(ModSelective, :StructParent)] isrequired = minimal_evaluation(hastrackedexpr, src, edges) selective_eval_fromstart!(frame, isrequired, true) @test supertype(ModSelective.StructParent) === AbstractArray @@ -230,7 +230,7 @@ module ModSelective end Core.eval(ModEval, ex) frame = Frame(ModEval, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModEval, src) isrequired = minimal_evaluation(hastrackedexpr, src, edges) selective_eval_fromstart!(frame, isrequired, true) @test supertype(ModEval.StructParent) === AbstractArray @@ -240,7 +240,7 @@ module ModSelective end ex = :(struct NoParam end) frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModSelective, src) isrequired = minimal_evaluation(stmt->(LoweredCodeUtils.ismethod_with_name(src, stmt, "NoParam"),false), src, edges) # initially mark only the constructor selective_eval_fromstart!(frame, isrequired, true) @test isa(ModSelective.NoParam(), ModSelective.NoParam) @@ -252,7 +252,7 @@ module ModSelective end end frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModSelective, src) isrequired = minimal_evaluation(stmt->(LoweredCodeUtils.ismethod_with_name(src, stmt, "Struct"),false), src, edges) # initially mark only the constructor selective_eval_fromstart!(frame, isrequired, true) @test isa(ModSelective.Struct([1,2,3]), ModSelective.Struct{Int}) @@ -269,7 +269,7 @@ module ModSelective end end frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModSelective, src) isrequired = minimal_evaluation(stmt->(LoweredCodeUtils.ismethod3(stmt),false), src, edges) # initially mark only the constructor selective_eval_fromstart!(frame, isrequired, true) kws = ModSelective.KWStruct(y=5.0f0) @@ -279,7 +279,7 @@ module ModSelective end ex = :(max_values(T::Union{map(X -> Type{X}, Base.BitIntegerSmall_types)...}) = 1 << (8*sizeof(T))) frame = Frame(ModSelective, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModSelective, src) isrequired = fill(false, length(src.code)) j = length(src.code) - 1 if !Meta.isexpr(src.code[end-1], :method, 3) @@ -301,7 +301,7 @@ module ModSelective end Core.eval(ModEval, ex) frame = Frame(ModEval, ex) src = frame.framecode.src - edges = CodeEdges(src) + edges = CodeEdges(ModEval, src) isrequired = minimal_evaluation(stmt->(LoweredCodeUtils.ismethod3(stmt),false), src, edges; norequire=exclude_named_typedefs(src, edges)) # initially mark only the constructor bbs = Core.Compiler.compute_basic_blocks(src.code) for (iblock, block) in enumerate(bbs.blocks) @@ -324,8 +324,8 @@ module ModSelective end end end) src = thk.args[1] - edges = CodeEdges(src) - lr = lines_required(:revise538, src, edges) + edges = CodeEdges(ModEval, src) + lr = lines_required(GlobalRef(ModEval, :revise538), src, edges) selective_eval_fromstart!(Frame(ModEval, src), lr, #=istoplevel=#true) @test isdefined(ModEval, :revise538) && length(methods(ModEval.revise538, (Float32,))) == 1 @@ -338,7 +338,7 @@ module ModSelective end end end) src = thk.args[1] - edges = CodeEdges(src) + edges = CodeEdges(Main, src) idx = findfirst(stmt->Meta.isexpr(stmt, :method), src.code) lr = lines_required(idx, src, edges; norequire=exclude_named_typedefs(src, edges)) idx = findfirst(stmt->Meta.isexpr(stmt, :(=)) && Meta.isexpr(stmt.args[2], :call) && is_global_ref(stmt.args[2].args[1], Core, :Box), src.code) @@ -349,7 +349,7 @@ module ModSelective end primitive type WindowsRawSocket sizeof(Ptr) * 8 end end) src = thk.args[1] - edges = CodeEdges(src) + edges = CodeEdges(Main, src) idx = findfirst(istypedef, src.code) r = LoweredCodeUtils.typedef_range(src, idx) @test last(r) == length(src.code) - 1 @@ -359,7 +359,7 @@ module ModSelective end # worth testing because this has proven quite crucial for debugging and # ensuring that these structures are as "self-documenting" as possible. io = IOBuffer() - l = LoweredCodeUtils.Links(Int[], [3, 5], LoweredCodeUtils.NamedVar[:hello]) + l = LoweredCodeUtils.Links(Int[], [3, 5], LoweredCodeUtils.GlobalRef[GlobalRef(Main, :hello)]) show(io, l) str = String(take!(io)) @test occursin('∅', str) @@ -375,7 +375,7 @@ module ModSelective end end lwr = Meta.lower(Main, ex) src = lwr.args[1] - cl = LoweredCodeUtils.CodeLinks(src) + cl = LoweredCodeUtils.CodeLinks(Main, src) show(io, cl) str = String(take!(io)) @test occursin(r"slot 1:\n preds: ssas: \[\d+, \d+\], slots: ∅, names: ∅;\n succs: ssas: \[\d+, \d+, \d+\], slots: ∅, names: ∅;\n assign @: \[\d+, \d+\]", str) @@ -386,7 +386,7 @@ module ModSelective end occursin(r"s:\n preds: ssas: \[\d+, \d+\], slots: ∅, names: ∅;\n succs: ssas: \[\d+, \d+, \d+\], slots: ∅, names: ∅;\n assign @: \[\d, \d+\]", str) # with global var inference end if Base.VERSION < v"1.8" - @test occursin(r"\d+ preds: ssas: \[\d+\], slots: ∅, names: \[:s\];\n\d+ succs: ssas: ∅, slots: ∅, names: \[:s\];", str) + @test occursin(r"\d+ preds: ssas: \[\d+\], slots: ∅, names: \[\:\(Main\.s\)\];\n\d+ succs: ssas: ∅, slots: ∅, names: \[\:\(Main\.s\)\];", str) end LoweredCodeUtils.print_with_code(io, src, cl) str = String(take!(io)) @@ -395,13 +395,13 @@ module ModSelective end @test occursin("# see name s", str) @test occursin("# see slot 1", str) if Base.VERSION < v"1.8" # changed by global var inference - @test occursin(r"# preds: ssas: \[\d+\], slots: ∅, names: \[:s\]; succs: ssas: ∅, slots: ∅, names: \[:s\];", str) + @test occursin(r"# preds: ssas: \[\d+\], slots: ∅, names: \[\:\(Main\.s\)\]; succs: ssas: ∅, slots: ∅, names: \[\:\(Main\.s\)\];", str) end else @test occursin("No IR statement printer", str) end # CodeEdges - edges = CodeEdges(src) + edges = CodeEdges(Main, src) show(io, edges) str = String(take!(io)) if Base.VERSION < v"1.10" @@ -428,7 +428,7 @@ module ModSelective end end # Works with Frames too frame = Frame(ModSelective, ex) - edges = CodeEdges(frame.framecode.src) + edges = CodeEdges(ModSelective, frame.framecode.src) LoweredCodeUtils.print_with_code(io, frame, edges) str = String(take!(io)) if isdefined(Base.IRShow, :show_ir_stmt) @@ -468,7 +468,7 @@ end lwr = Meta.lower(m, ex) src = first(lwr.args) stmts = src.code - edges = CodeEdges(src) + edges = CodeEdges(m, src) isrq = lines_required!(istypedef.(stmts), src, edges) frame = Frame(m, src)