Skip to content

Commit 8fecf35

Browse files
authored
Fix kw pattern matching, other changes on 1.9+ (#568)
With the introduction of Core.kwcall, we need to adjust our pattern-matching for the statements that prepare to dispatch on kw-handling functions. This also: * Implements `==` for Variable * Fixes for get_staged & get_source on nightly * Fixes printing-test failures
1 parent cf7f437 commit 8fecf35

File tree

7 files changed

+61
-22
lines changed

7 files changed

+61
-22
lines changed

src/commands.jl

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ which execution should start.
214214
"""
215215
function maybe_step_through_wrapper!(@nospecialize(recurse), frame::Frame)
216216
code = frame.framecode
217-
stmts, scope = code.src.code, code.scope::Method
217+
src = code.src
218+
stmts, scope = src.code, code.scope::Method
218219
length(stmts) < 2 && return frame
219220
last = stmts[end-1]
220221
isexpr(last, :(=)) && (last = last.args[2])
@@ -225,13 +226,14 @@ function maybe_step_through_wrapper!(@nospecialize(recurse), frame::Frame)
225226
if unwrap1 isa DataType
226227
param1 = Base.unwrap_unionall(unwrap1.parameters[1])
227228
if param1 isa DataType
228-
is_kw = endswith(String(param1.name.name), "#kw")
229+
is_kw = isdefined(Core, :kwcall) ? param1.name.name === Symbol("#kwcall") :
230+
endswith(String(param1.name.name), "#kw")
229231
end
230232
end
231233
end
232234

233235
has_selfarg = isexpr(last, :call) && any(@nospecialize(x) -> isa(x, SlotNumber) && x.id == 1, last.args) # isequal(SlotNumber(1)) vulnerable to invalidation
234-
issplatcall, _callee = unpack_splatcall(last)
236+
issplatcall, _callee = unpack_splatcall(last, src)
235237
if is_kw || has_selfarg || (issplatcall && is_bodyfunc(_callee))
236238
# If the last expr calls #self# or passes it to an implementation method,
237239
# this is a wrapper function that we might want to step through
@@ -253,6 +255,13 @@ function maybe_step_through_wrapper!(@nospecialize(recurse), frame::Frame)
253255
end
254256
maybe_step_through_wrapper!(frame::Frame) = maybe_step_through_wrapper!(finish_and_return!, frame)
255257

258+
if isdefined(Core, :kwcall)
259+
const kwhandler = Core.kwcall
260+
const kwextrastep = 0
261+
else
262+
const kwhandler = Core.kwfunc
263+
const kwextrastep = 1
264+
end
256265

257266
"""
258267
frame = maybe_step_through_kwprep!(recurse, frame)
@@ -267,19 +276,19 @@ function maybe_step_through_kwprep!(@nospecialize(recurse), frame::Frame, istopl
267276
stmt = pc_expr(frame, pc)
268277
if isa(stmt, Tuple{Symbol,Vararg{Symbol}})
269278
# Check to see if we're creating a NamedTuple followed by kwfunc call
270-
pccall = pc + 5
279+
pccall = pc + 4 + kwextrastep
271280
if pccall <= n
272281
stmt1 = src.code[pc+1]
273282
# We deliberately check isexpr(stmt, :call) rather than is_call(stmt): if it's
274283
# assigned to a local, it's *not* kwarg preparation.
275284
if isexpr(stmt1, :call) && is_quotenode_egal(stmt1.args[1], Core.apply_type) && is_quoted_type(stmt1.args[2], :NamedTuple)
276285
stmt4, stmt5 = src.code[pc+4], src.code[pc+5]
277-
if isexpr(stmt4, :call) && is_quotenode_egal(stmt4.args[1], Core.kwfunc)
286+
if isexpr(stmt4, :call) && is_quotenode_egal(stmt4.args[1], kwhandler)
278287
while pc < pccall
279288
pc = step_expr!(recurse, frame, istoplevel)
280289
end
281290
return frame
282-
elseif isexpr(stmt5, :call) && is_quotenode_egal(stmt5.args[1], Core.kwfunc) && pccall+1 <= n
291+
elseif isexpr(stmt5, :call) && is_quotenode_egal(stmt5.args[1], kwhandler) && pccall+1 <= n
283292
# This happens when the call is scoped by a module
284293
pccall += 1
285294
while pc < pccall
@@ -300,7 +309,7 @@ function maybe_step_through_kwprep!(@nospecialize(recurse), frame::Frame, istopl
300309
# No supplied kwargs
301310
pcsplat = pc + 3
302311
if pcsplat <= n
303-
issplatcall, callee = unpack_splatcall(src.code[pcsplat])
312+
issplatcall, callee = unpack_splatcall(src.code[pcsplat], src)
304313
if issplatcall && is_bodyfunc(callee)
305314
while pc < pcsplat
306315
pc = step_expr!(recurse, frame, istoplevel)
@@ -321,12 +330,12 @@ function maybe_step_through_kwprep!(@nospecialize(recurse), frame::Frame, istopl
321330
end
322331
elseif is_quotenode_egal(f, Base.merge) && ((pccall = pc + 7) <= n)
323332
stmtk = src.code[pccall-1]
324-
if isexpr(stmtk, :call) && is_quotenode_egal(stmtk.args[1], Core.kwfunc)
333+
if isexpr(stmtk, :call) && is_quotenode_egal(stmtk.args[1], kwhandler)
325334
for i = 1:4
326335
pc = step_expr!(recurse, frame, istoplevel)
327336
end
328337
stmti = src.code[pc]
329-
if isexpr(stmti, :call) && is_quotenode_egal(stmti.args[1], Core.kwfunc)
338+
if isexpr(stmti, :call) && is_quotenode_egal(stmti.args[1], #= deliberately not kwhandler =# Core.kwfunc)
330339
pc = step_expr!(recurse, frame, istoplevel)
331340
end
332341
end

src/construct.jl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,18 @@ end
8888

8989
get_source(meth::Method) = Base.uncompressed_ast(meth)
9090

91-
function get_source(g::GeneratedFunctionStub, env)
92-
b = g(env..., g.argnames...)
93-
b isa CodeInfo && return b
94-
return eval(b)
91+
if Base.VERSION < v"1.10.0-DEV.873" # julia#48766
92+
function get_source(g::GeneratedFunctionStub, env, file, line)
93+
b = g(env..., g.argnames...)
94+
b isa CodeInfo && return b
95+
return eval(b)
96+
end
97+
else
98+
function get_source(g::GeneratedFunctionStub, env, file, line::Int)
99+
b = g(Base.get_world_counter(), LineNumberNode(line, file), env..., g.argnames...)
100+
b isa CodeInfo && return b
101+
return eval(b)
102+
end
95103
end
96104

97105
"""
@@ -148,12 +156,12 @@ function prepare_framecode(method::Method, @nospecialize(argtypes); enter_genera
148156
# If we're stepping into a staged function, we need to use
149157
# the specialization, rather than stepping through the
150158
# unspecialized method.
151-
code = Core.Compiler.get_staged(Core.Compiler.specialize_method(method, argtypes, lenv))
159+
code = get_staged(Core.Compiler.specialize_method(method, argtypes, lenv))
152160
code === nothing && return nothing
153161
generator = false
154162
else
155163
if is_generated(method)
156-
code = get_source(method.generator, lenv)
164+
code = get_source(method.generator, lenv, method.file, Int(method.line))
157165
generator = true
158166
else
159167
code = get_source(method)

src/optimize.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ function extract_inner_call!(stmt::Expr, idx, once::Bool=false)
2525
return nothing
2626
end
2727

28-
function replace_ssa(@nospecialize(stmt), ssalookup)
29-
isa(stmt, Expr) || return stmt
28+
function replace_ssa(stmt::Expr, ssalookup)
3029
return Expr(stmt.head, Any[
3130
if isa(a, SSAValue)
3231
SSAValue(ssalookup[a.id])
3332
elseif isa(a, NewSSAValue)
3433
SSAValue(a.id)
35-
else
34+
elseif isa(a, Expr)
3635
replace_ssa(a, ssalookup)
36+
else
37+
a
3738
end
3839
for a in stmt.args
3940
]...)

src/types.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ Base.show(io::IO, var::Variable) = (print(io, var.name, " = "); show(io,var.valu
348348
Base.isequal(var1::Variable, var2::Variable) =
349349
var1.value == var2.value && var1.name === var2.name && var1.isparam == var2.isparam &&
350350
var1.is_captured_closure == var2.is_captured_closure
351+
Base.:(==)(var1::Variable, var2::Variable) = isequal(var1, var2)
351352

352353
# A type that is unique to this package for which there are no valid operations
353354
struct Unassigned end

src/utils.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ function unpack_splatcall(stmt)
194194
end
195195
return false, nothing
196196
end
197+
function unpack_splatcall(stmt, src::CodeInfo)
198+
issplatcall, callee = unpack_splatcall(stmt)
199+
if isa(callee, SSAValue)
200+
callee = src.code[callee.id]
201+
end
202+
return issplatcall, callee
203+
end
197204

198205
function is_bodyfunc(@nospecialize(arg))
199206
if isa(arg, QuoteNode)
@@ -217,6 +224,12 @@ end
217224

218225
is_generated(meth::Method) = isdefined(meth, :generator)
219226

227+
if Base.VERSION < v"1.10.0-DEV.873" # julia#48766
228+
get_staged(mi::MethodInstance) = Core.Compiler.get_staged(mi)
229+
else
230+
get_staged(mi::MethodInstance) = Core.Compiler.get_staged(mi, Base.get_world_counter())
231+
end
232+
220233
"""
221234
is_doc_expr(ex)
222235

test/breakpoints.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ struct Squarer end
198198
if VERSION < v"1.9.0-DEV.846" # https://github.com/JuliaLang/julia/pull/45069
199199
LOC = " in $(@__MODULE__) at $(@__FILE__)"
200200
else
201-
LOC = "\n @ $(@__MODULE__) $(contractuser(@__FILE__))"
201+
LOC = " @ $(@__MODULE__) $(contractuser(@__FILE__))"
202202
end
203203
bp = JuliaInterpreter.BreakpointRef(frame.framecode, 1)
204204
@test repr(bp) == "breakpoint(loop_radius2(n)$LOC:$(3-Δ), line 3)"

test/eval_code.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,17 @@ eval_code(fr, "non_accessible_variable = 5.0")
7777
# Evaluating SSAValues
7878
f(x) = x^2
7979
frame = JuliaInterpreter.enter_call(f, 5)
80-
JuliaInterpreter.step_expr!(frame)
81-
JuliaInterpreter.step_expr!(frame)
80+
id = let
81+
pc, n = frame.pc, length(frame.framecode.src.code)
82+
while pc < n - 1
83+
pc = JuliaInterpreter.step_expr!(frame)
84+
end
85+
# Extract the SSAValue that corresponds to the power
86+
stmt = frame.framecode.src.code[pc]::Expr # the `literal_pow` call
87+
stmt.args[end].id
88+
end
8289
# This could change with changes to Julia lowering
83-
@test eval_code(frame, "var\"%2\"") == Val(2)
90+
@test eval_code(frame, "var\"%$(id)\"") == Val(2)
8491
@test eval_code(frame, "var\"@_1\"") == f
8592

8693
function fun(;output=:sym)

0 commit comments

Comments
 (0)