Skip to content

Commit dc511d7

Browse files
authored
Fix selective interpretation of struct (#49)
A typo in the implementation caused a difficult-to-diagnose failure. Also adds many new tests to catch problems, including like the ones detected in #47.
1 parent c0beb0a commit dc511d7

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

src/packagedef.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const SSAValues = Union{Core.Compiler.SSAValue, JuliaInterpreter.SSAValue}
99

1010
const structheads = VERSION >= v"1.5.0-DEV.702" ? () : (:struct_type, :abstract_type, :primitive_type)
1111
const trackedheads = (:method, structheads...)
12+
const structdecls = (:_structtype, :_abstracttype, :_primitivetype)
1213

1314
export signature, rename_framemethods!, methoddef!, methoddefs!, bodymethod
1415
export CodeEdges, lines_required, lines_required!, selective_eval!, selective_eval_fromstart!

src/utils.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,14 @@ function istypedef(stmt)
8787
stmt = rhs(stmt)
8888
isa(stmt, Expr) || return false
8989
stmt.head structheads && return true
90-
@static if isdefined(Core, :_structtype)
90+
@static if all(s->isdefined(Core,s), structdecls)
91+
if isexpr(stmt, :(=))
92+
stmt = (stmt::Expr).args[2] # look for lhs
93+
end
9194
if stmt.head === :call
9295
f = stmt.args[1]
9396
if isa(f, GlobalRef)
94-
f.mod === Core && f.name (:_structype, :_abstracttype, :_primitivetype) && return true
97+
f.mod === Core && f.name structdecls && return true
9598
end
9699
if isa(f, QuoteNode)
97100
(f.value === Core._structtype || f.value === Core._abstracttype ||

test/codeedges.jl

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using LoweredCodeUtils
22
using LoweredCodeUtils.JuliaInterpreter
3-
using LoweredCodeUtils: callee_matches
3+
using LoweredCodeUtils: callee_matches, istypedef
44
using JuliaInterpreter: is_global_ref, is_quotenode
55
using Test
66

@@ -352,3 +352,84 @@ end
352352
end
353353
end
354354
end
355+
356+
@testset "selective interpretation of toplevel definitions" begin
357+
gen_mock_module() = Core.eval(@__MODULE__, :(module $(gensym(:LoweredCodeUtilsTestMock)) end))
358+
function check_toplevel_definition_interprete(ex, defs, undefs)
359+
m = gen_mock_module()
360+
lwr = Meta.lower(m, ex)
361+
src = first(lwr.args)
362+
stmts = src.code
363+
edges = CodeEdges(src)
364+
365+
isrq = lines_required!(istypedef.(stmts), src, edges)
366+
frame = Frame(m, src)
367+
selective_eval_fromstart!(frame, isrq, #= toplevel =# true)
368+
369+
for def in defs; @test isdefined(m, def); end
370+
for undef in undefs; @test !isdefined(m, undef); end
371+
end
372+
373+
@testset "case: $(i), interpret: $(defs), ignore $(undefs)" for (i, ex, defs, undefs) in (
374+
(1, :(abstract type Foo end), (:Foo,), ()),
375+
376+
(2, :(struct Foo end), (:Foo,), ()),
377+
378+
(3, quote
379+
struct Foo
380+
val
381+
end
382+
end, (:Foo,), ()),
383+
384+
(4, quote
385+
struct Foo{T}
386+
val::T
387+
Foo(v::T) where {T} = new{T}(v)
388+
end
389+
end, (:Foo,), ()),
390+
391+
(5, :(primitive type Foo 32 end), (:Foo,), ()),
392+
393+
(6, quote
394+
abstract type Foo end
395+
struct Foo1 <: Foo end
396+
struct Foo2 <: Foo end
397+
end, (:Foo, :Foo1, :Foo2), ()),
398+
399+
(7, quote
400+
struct Foo
401+
v
402+
Foo(f) = new(f())
403+
end
404+
405+
foo = Foo(()->throw("don't interpret me"))
406+
end, (:Foo,), (:foo,)),
407+
408+
# https://github.com/JuliaDebug/LoweredCodeUtils.jl/issues/47
409+
(8, quote
410+
struct Foo
411+
b::Bool
412+
Foo(b) = new(b)
413+
end
414+
415+
foo = Foo(false)
416+
end, (:Foo,), (:foo,)),
417+
418+
# https://github.com/JuliaDebug/LoweredCodeUtils.jl/pull/48
419+
# we shouldn't make `add_links!` recur into `QuoteNode`, otherwise the variable
420+
# `bar` will be selected as a requirement for `Bar1` (, which has "bar" field)
421+
(9, quote
422+
abstract type Bar end
423+
struct Bar1 <: Bar
424+
bar
425+
end
426+
427+
r = (throw("don't interpret me"); rand(10000000000000000))
428+
bar = Bar1(r)
429+
show(bar)
430+
end, (:Bar, :Bar1), (:r, :bar))
431+
)
432+
433+
check_toplevel_definition_interprete(ex, defs, undefs)
434+
end
435+
end

0 commit comments

Comments
 (0)