Skip to content

Commit d2f10bb

Browse files
authored
Fix on Julia 1.11 (#108)
Adjust for changes in lowering in Julia 1.11: - Previously, calls to kw body methods had the name of the method embedding in the statement with the call. Now they can be an SSAValue, which must be looked up. - Fix detection of anonymous typedefs on 1.11
1 parent 34f7f11 commit d2f10bb

File tree

4 files changed

+77
-49
lines changed

4 files changed

+77
-49
lines changed

src/signatures.jl

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,19 @@ function identify_framemethod_calls(frame)
189189
key = key::Union{Symbol,Bool,Nothing}
190190
for (j, mstmt) in enumerate(msrc.code)
191191
isa(mstmt, Expr) || continue
192+
jj = j
192193
if mstmt.head === :call
193194
mkey = mstmt.args[1]
195+
if isa(mkey, SSAValue) || isa(mkey, Core.SSAValue)
196+
refstmt = msrc.code[mkey.id]
197+
if isa(refstmt, Symbol)
198+
jj = mkey.id
199+
mkey = refstmt
200+
end
201+
end
194202
if isa(mkey, Symbol)
195203
# Could be a GlobalRef but then it's outside frame
196-
haskey(methodinfos, mkey) && push!(selfcalls, SelfCall(i, j, mkey, key))
204+
haskey(methodinfos, mkey) && push!(selfcalls, SelfCall(i, jj, mkey, key))
197205
elseif is_global_ref(mkey, Core, isdefined(Core, :_apply_iterate) ? :_apply_iterate : :_apply)
198206
ssaref = mstmt.args[end-1]
199207
if isa(ssaref, JuliaInterpreter.SSAValue)
@@ -202,15 +210,15 @@ function identify_framemethod_calls(frame)
202210
end
203211
mkey = mstmt.args[end-2]
204212
if isa(mkey, Symbol)
205-
haskey(methodinfos, mkey) && push!(selfcalls, SelfCall(i, j, mkey, key))
213+
haskey(methodinfos, mkey) && push!(selfcalls, SelfCall(i, jj, mkey, key))
206214
end
207215
end
208216
elseif mstmt.head === :meta && mstmt.args[1] === :generated
209217
newex = mstmt.args[2]
210218
if isa(newex, Expr)
211219
if newex.head === :new && length(newex.args) >= 2 && is_global_ref(newex.args[1], Core, :GeneratedFunctionStub)
212220
mkey = newex.args[2]::Symbol
213-
haskey(methodinfos, mkey) && push!(selfcalls, SelfCall(i, j, mkey, key))
221+
haskey(methodinfos, mkey) && push!(selfcalls, SelfCall(i, jj, mkey, key))
214222
end
215223
end
216224
end
@@ -356,7 +364,7 @@ function find_name_caller_sig(@nospecialize(recurse), frame, pc, name, parentnam
356364
end
357365
if length(body.code) > 1
358366
bodystmt = body.code[end-1] # the line before the final return
359-
iscallto(bodystmt, name) && return signature_top(frame, stmt, pc), false
367+
iscallto(bodystmt, name, body) && return signature_top(frame, stmt, pc), false
360368
end
361369
end
362370
pc = next_or_nothing(frame, pc)
@@ -426,6 +434,9 @@ function get_running_name(@nospecialize(recurse), frame, pc, name, parentname)
426434
bodystmt = bodyparent.code[end-1]
427435
@assert isexpr(bodystmt, :call)
428436
ref = getcallee(bodystmt)
437+
if isa(ref, SSAValue) || isa(ref, Core.SSAValue)
438+
ref = bodyparent.code[ref.id]
439+
end
429440
isa(ref, GlobalRef) || @show ref typeof(ref)
430441
@assert isa(ref, GlobalRef)
431442
@assert ref.mod == moduleof(frame)
@@ -581,7 +592,7 @@ function _methoddefs!(@nospecialize(recurse), signatures, frame::Frame, pc; defi
581592
return pc
582593
end
583594

584-
function is_self_call(@nospecialize(stmt), slotnames, argno=1)
595+
function is_self_call(@nospecialize(stmt), slotnames, argno::Integer=1)
585596
if isa(stmt, Expr)
586597
if stmt.head == :call
587598
a = stmt.args[argno]
@@ -612,44 +623,54 @@ Return the "body method" for a method `m`. `mbody` contains the code of the func
612623
when `m` was defined.
613624
"""
614625
function bodymethod(mkw::Method)
615-
m = mkw
616-
local src
617-
while true
618-
framecode = JuliaInterpreter.get_framecode(m)
619-
fakeargs = Any[nothing for i = 1:(framecode.scope::Method).nargs]
620-
frame = JuliaInterpreter.prepare_frame(framecode, fakeargs, isa(m.sig, UnionAll) ? sparam_ub(m) : Core.svec())
621-
src = framecode.src
622-
(length(src.code) > 1 && is_self_call(src.code[end-1], src.slotnames)) || break
623-
# Build the optional arg, so we can get its type
624-
pc = frame.pc
625-
while pc < length(src.code) - 1
626-
pc = step_expr!(frame)
626+
@static if isdefined(Core, :kwcall)
627+
Base.unwrap_unionall(mkw.sig).parameters[1] !== typeof(Core.kwcall) && isempty(Base.kwarg_decl(mkw)) && return mkw
628+
mths = methods(Base.bodyfunction(mkw))
629+
if length(mths) != 1
630+
@show mkw
631+
display(mths)
627632
end
628-
val = pc > 1 ? frame.framedata.ssavalues[pc-1] : (src.code[1]::Expr).args[end]
629-
sig = Tuple{(Base.unwrap_unionall(m.sig)::DataType).parameters..., typeof(val)}
630-
m = whichtt(sig)
631-
end
632-
length(src.code) > 1 || return m
633-
stmt = src.code[end-1]
634-
if isexpr(stmt, :call) && (f = (stmt::Expr).args[1]; isa(f, QuoteNode))
635-
if f.value === (isdefined(Core, :_apply_iterate) ? Core._apply_iterate : Core._apply)
636-
ssaref = stmt.args[end-1]
637-
if isa(ssaref, JuliaInterpreter.SSAValue)
638-
id = ssaref.id
639-
has_self_call(src, src.code[id]) || return m
640-
end
641-
f = stmt.args[end-2]
642-
if isa(f, JuliaInterpreter.SSAValue)
643-
f = src.code[f.id]
633+
return only(mths)
634+
else
635+
m = mkw
636+
local src
637+
while true
638+
framecode = JuliaInterpreter.get_framecode(m)
639+
fakeargs = Any[nothing for i = 1:(framecode.scope::Method).nargs]
640+
frame = JuliaInterpreter.prepare_frame(framecode, fakeargs, isa(m.sig, UnionAll) ? sparam_ub(m) : Core.svec())
641+
src = framecode.src
642+
(length(src.code) > 1 && is_self_call(src.code[end-1], src.slotnames)) || break
643+
# Build the optional arg, so we can get its type
644+
pc = frame.pc
645+
while pc < length(src.code) - 1
646+
pc = step_expr!(frame)
644647
end
645-
else
646-
has_self_call(src, stmt) || return m
648+
val = pc > 1 ? frame.framedata.ssavalues[pc-1] : (src.code[1]::Expr).args[end]
649+
sig = Tuple{(Base.unwrap_unionall(m.sig)::DataType).parameters..., typeof(val)}
650+
m = whichtt(sig)
647651
end
648-
f = f.value
649-
mths = methods(f)
650-
if length(mths) == 1
651-
return first(mths)
652+
length(src.code) > 1 || return m
653+
stmt = src.code[end-1]
654+
if isexpr(stmt, :call) && (f = (stmt::Expr).args[1]; isa(f, QuoteNode))
655+
if f.value === (isdefined(Core, :_apply_iterate) ? Core._apply_iterate : Core._apply)
656+
ssaref = stmt.args[end-1]
657+
if isa(ssaref, JuliaInterpreter.SSAValue)
658+
id = ssaref.id
659+
has_self_call(src, src.code[id]) || return m
660+
end
661+
f = stmt.args[end-2]
662+
if isa(f, JuliaInterpreter.SSAValue)
663+
f = src.code[f.id]
664+
end
665+
else
666+
has_self_call(src, stmt) || return m
667+
end
668+
f = f.value
669+
mths = methods(f)
670+
if length(mths) == 1
671+
return first(mths)
672+
end
652673
end
674+
return m
653675
end
654-
return m
655676
end

src/utils.jl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ macro issslotnum(stmt)
1414
end
1515

1616
"""
17-
iscallto(stmt, name)
17+
iscallto(stmt, name, src)
1818
1919
Returns `true` is `stmt` is a call expression to `name`.
2020
"""
21-
function iscallto(@nospecialize(stmt), name)
21+
function iscallto(@nospecialize(stmt), name, src)
2222
if isa(stmt, Expr)
2323
if stmt.head === :call
2424
a = stmt.args[1]
25+
if isa(a, SSAValue) || isa(a, Core.SSAValue)
26+
a = src.code[a.id]
27+
end
2528
a === name && return true
2629
is_global_ref(a, Core, :_apply) && stmt.args[2] === name && return true
2730
is_global_ref(a, Core, :_apply_iterate) && stmt.args[3] === name && return true
@@ -110,15 +113,15 @@ function isanonymous_typedef(stmt)
110113
if isa(stmt, CodeInfo)
111114
src = stmt # just for naming consistency
112115
length(src.code) >= 4 || return false
116+
stmt = src.code[end-1]
117+
isexpr(stmt, :call) || return false
118+
is_global_ref(stmt.args[1], Core, :_typebody!) || return false
113119
@static if VERSION v"1.9.0-DEV.391"
114-
stmt = src.code[end-2]
120+
stmt = isa(stmt.args[3], Core.SSAValue) ? src.code[end-3] : src.code[end-2]
115121
isexpr(stmt, :(=)) || return false
116122
name = stmt.args[1]
117123
isa(name, Symbol) || return false
118124
else
119-
stmt = src.code[end-1]
120-
isexpr(stmt, :call) || return false
121-
is_global_ref(stmt.args[1], Core, :_typebody!) || return false
122125
name = stmt.args[2]::Symbol
123126
end
124127
return startswith(String(name), "#")

test/codeedges.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,12 @@ module ModSelective end
281281
src = frame.framecode.src
282282
edges = CodeEdges(src)
283283
isrequired = fill(false, length(src.code))
284-
@assert Meta.isexpr(src.code[end-1], :method, 3)
285-
isrequired[end-1] = true
284+
j = length(src.code) - 1
285+
if !Meta.isexpr(src.code[end-1], :method, 3)
286+
j -= 1
287+
end
288+
@assert Meta.isexpr(src.code[j], :method, 3)
289+
isrequired[j] = true
286290
lines_required!(isrequired, src, edges)
287291
selective_eval_fromstart!(frame, isrequired, true)
288292
@test ModSelective.max_values(Int16) === 65536

test/signatures.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module signatures
1+
module Signatures
22

33
using LoweredCodeUtils
44
using InteractiveUtils

0 commit comments

Comments
 (0)