Skip to content

Commit 4052c19

Browse files
authored
prepare for #196, add recognized problem cases of top-level statement selection (#198)
1 parent 5e8957c commit 4052c19

File tree

1 file changed

+173
-49
lines changed

1 file changed

+173
-49
lines changed

test/test_virtualprocess.jl

Lines changed: 173 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,104 +1396,198 @@ end
13961396
end
13971397

13981398
@testset "top-level statement selection" begin
1399-
@testset "toplevel definitions by `eval` calls" begin
1399+
# simplest example
1400+
let
1401+
# global function
1402+
vmod, res = @analyze_toplevel2 begin
1403+
foo() = return # should be concretized
1404+
end
1405+
@test is_concrete(vmod, :foo)
1406+
1407+
# inner function
1408+
vmod, res = @analyze_toplevel2 analyze_from_definitions=true let
1409+
foo(a) = a # should be concretized, and its signature should have been collected
1410+
end
1411+
@test length(res.toplevel_signatures) == 1
1412+
end
1413+
1414+
@testset "captured variables" begin
14001415
let
14011416
vmod, res = @analyze_toplevel2 begin
1402-
# these definitions shouldn't be abstracted away
1403-
for fname in (:foo, :bar, :baz)
1404-
@eval begin
1405-
@inline ($(Symbol("is", fname)))(a) = a === $(QuoteNode(fname))
1406-
end
1417+
begin
1418+
s = join(rand(Char, 100))
1419+
foo() = return s
14071420
end
1421+
end
1422+
@test is_concrete(vmod, :foo)
1423+
# FIXME ideally we don't want to concretize `s`, but `LoweredCodeUtils.lines_required!`
1424+
# requires it by named dependency traversal
1425+
@test_broken is_abstract(vmod, :s)
1426+
end
14081427

1409-
# these should be abstracted away (i.e. shouldn't throw)
1410-
isfoo(:foo) && throw("foo") # should be reported
1411-
isbar(:foo) && isbaz(:baz) && throw("foobaz") # shouldn't be reported
1412-
isbar(:bar) && isbaz(:baz) && throw("barbaz") # should be reported
1428+
# captured variables for global functions
1429+
let
1430+
# XXX `s` below aren't necessarily concretized, but concretization of `foo` requires
1431+
# it (since `s` will be embedded into `foo`'s body wrapped in `QuoteNode`)
1432+
# and thus we can't abstract it away as far as we depend on JuliaInterpreter ...
1433+
vmod, res = @analyze_toplevel2 let
1434+
s = "julia"
1435+
global foo() = return s
14131436
end
1437+
@test is_concrete(vmod, :foo)
14141438

1415-
@test is_concrete(vmod, :isfoo)
1416-
@test is_concrete(vmod, :isbar)
1417-
@test is_concrete(vmod, :isbaz)
1418-
@test length(res.inference_error_reports) == 2
1419-
@test all(er->isa(er, UncaughtExceptionReport), res.inference_error_reports)
1439+
vmod, res = @analyze_toplevel2 let
1440+
s = undefvar # actual top-level error will happen here
1441+
global foo() = return s
1442+
end
1443+
@test_broken isempty(res.toplevel_error_reports)
1444+
end
1445+
end
1446+
1447+
@testset "conditional control flow" begin
1448+
# ignore last statement of a definition block if possible
1449+
let
1450+
vmod, res = @analyze_toplevel2 if true # force multiple blocks
1451+
foo() = return
1452+
throw("foo")
1453+
end
1454+
@test is_concrete(vmod, :foo)
1455+
@test isempty(res.toplevel_signatures)
14201456
end
14211457
end
14221458

1423-
@testset "try/catch block" begin
1459+
# NOTE here we test "try/catch"-involved control flow in a fine-grained level
1460+
# we want to enrich this test suite as much as possible since this control flow often
1461+
# appears in test files with the test macros, while we will do the e2e level tests
1462+
# with those macros
1463+
@testset "try/catch control flow" begin
14241464
# https://github.com/aviatesk/JET.jl/issues/150
14251465
let
1426-
res = @analyze_toplevel try
1427-
foo(a) = sum(a)
1466+
res = @analyze_toplevel analyze_from_definitions=true try
1467+
foo(a) = sum(a) # essentially same as inner function, should be concretized
14281468

14291469
foo("julia") # shouldn't be concretized
14301470
catch err
14311471
err
14321472
end
14331473

14341474
@test isempty(res.toplevel_error_reports)
1475+
@test length(res.toplevel_signatures) == 1
14351476
test_sum_over_string(res)
14361477
end
14371478

1438-
# closure within a try clause
1479+
# captured variables within a try clause
14391480
let
1440-
res = @analyze_toplevel try
1481+
res = @analyze_toplevel analyze_from_definitions=true try
14411482
s = "julia"
1442-
foo(f) = f(s)
1483+
foo(f) = f(s) # should be concretized
14431484

14441485
foo(sum) # shouldn't be concretized
14451486
catch err
14461487
err
14471488
end
14481489

14491490
@test isempty(res.toplevel_error_reports)
1491+
@test length(res.toplevel_signatures) == 1
14501492
test_sum_over_string(res)
14511493
end
14521494

1453-
# closure within a catch clause
1495+
# captured variables within a catch clause
14541496
let
1455-
res = @analyze_toplevel begin
1456-
try
1457-
s = "julia"
1458-
foo(f) = f(s)
1459-
1460-
foo(sum) # shouldn't be concretized
1461-
catch err
1462-
function shows()
1463-
io = stderr::IO
1464-
showerror(io, err)
1465-
showerror(io, err2)
1466-
end
1467-
shows() # shouldn't be concretized
1497+
res = @analyze_toplevel analyze_from_definitions=true try
1498+
s = "julia"
1499+
foo(f) = f(s) # should be concretized
1500+
1501+
foo(sum) # shouldn't be concretized
1502+
catch err
1503+
function shows() # should be concretized
1504+
io = stderr::IO
1505+
showerror(io, err)
1506+
showerror(io, err2)
14681507
end
1508+
shows() # shouldn't be concretized
14691509
end
14701510

14711511
@test isempty(res.toplevel_error_reports)
1512+
@test length(res.toplevel_signatures) == 2
14721513
test_sum_over_string(res)
14731514
end
1474-
end
14751515

1476-
# ignore last statement of a definition block if possible
1477-
let
1478-
res = @analyze_toplevel begin
1479-
let
1480-
if true # force multiple blocks
1481-
foo() = return
1482-
throw("foo")
1516+
# same as "captured variables for global functions",
1517+
# capture variables within a catch clause for global function will just be an hell
1518+
let
1519+
vmod, res = @analyze_toplevel2 try
1520+
s = "julia"
1521+
foo(f) = f(s) # should be concretized
1522+
foo(sum) # shouldn't be concretized
1523+
catch err
1524+
global function shows() # should be concretized
1525+
io = stderr::IO
1526+
showerror(io, err)
1527+
showerror(io, err2)
14831528
end
1529+
shows() # shouldn't be concretized
14841530
end
1531+
1532+
@test_broken isempty(res.toplevel_error_reports)
1533+
@test_broken is_concrete(vmod, :shows) && length(methods(vmod.shows)) == 1
14851534
end
1486-
@test isempty(res.toplevel_signatures)
14871535
end
14881536

1489-
# end to end (might be very fragile ...)
1490-
let
1491-
res = analyze_file(normpath(FIXTURE_DIR, "error.jl"))
1492-
@test isempty(res.toplevel_error_reports)
1537+
@testset "top-level `eval` statement" begin
1538+
let
1539+
vmod, res = @analyze_toplevel2 let
1540+
sym = :foo
1541+
@eval $sym() = :foo # should be concretized
1542+
end
1543+
@test is_concrete(vmod, :foo)
1544+
end
1545+
end
1546+
1547+
@testset "toplevel definitions involved within a loop" begin
1548+
let
1549+
vmod, res = @analyze_toplevel2 begin
1550+
# these definitions should be concretized
1551+
for fname in (:foo, :bar, :baz)
1552+
@eval begin
1553+
@inline ($(Symbol("is", fname)))(a) = a === $(QuoteNode(fname))
1554+
end
1555+
end
1556+
end
1557+
1558+
@test is_concrete(vmod, :isfoo)
1559+
@test is_concrete(vmod, :isbar)
1560+
@test is_concrete(vmod, :isbaz)
1561+
end
1562+
1563+
let
1564+
vmod, res = @analyze_toplevel2 begin
1565+
let
1566+
# these definitions should be concretized
1567+
stack = [:foo, :bar, :baz]
1568+
while !isempty(stack)
1569+
fname = popfirst!(stack)
1570+
@eval begin
1571+
@inline ($(Symbol("is", fname)))(a) = a === $(QuoteNode(fname))
1572+
end
1573+
end
1574+
end
1575+
1576+
# these should be abstracted away (i.e. shouldn't throw)
1577+
isfoo(:foo) && throw("foo") # should be reported
1578+
isbar(:foo) && isbaz(:baz) && throw("foobaz") # shouldn't be reported
1579+
isbar(:bar) && isbaz(:baz) && throw("barbaz") # should be reported
1580+
end
1581+
1582+
# FIXME control flow traversal fails for iteration
1583+
@test is_concrete(vmod, :isfoo)
1584+
@test_broken is_concrete(vmod, :isbar)
1585+
@test_broken is_concrete(vmod, :isbaz)
1586+
end
14931587
end
14941588
end
14951589

1496-
# in this test suite, we just check usage of test macros doesn't lead to (false positive)
1590+
# in this e2e test suite, we just check usage of test macros doesn't lead to (false positive)
14971591
# top-level errors (e.g. world age, etc.)
14981592
@testset "@test macros" begin
14991593
vmod = Module()
@@ -1604,6 +1698,36 @@ end
16041698
@test isempty(res.toplevel_error_reports)
16051699
test_sum_over_string(res)
16061700
end
1701+
1702+
@static VERSION v"1.7-DEV" && let
1703+
# adapted from https://github.com/JuliaLang/julia/blob/ddeba0ba8aa452cb2064eb2d03bea47fe6b0ebbe/test/dict.jl#L469-L478
1704+
res = @analyze_toplevel begin
1705+
using Test
1706+
@testset "IdDict{Any,Any} and partial inference" begin
1707+
d = Dict('a'=>1, 'b'=>1, 'c'=> 3)
1708+
@test a != d
1709+
@test !isequal(a, d)
1710+
1711+
d = @inferred IdDict{Any,Any}(i=>i for i=1:3)
1712+
@test isa(d, IdDict{Any,Any})
1713+
@test d == IdDict{Any,Any}(1=>1, 2=>2, 3=>3)
1714+
end
1715+
end
1716+
@test_broken isempty(res.toplevel_error_reports)
1717+
end
1718+
end
1719+
1720+
# in this e2e test suite, we run JET against a selection of Julia's base test suite
1721+
# (i.e. from https://github.com/JuliaLang/julia/tree/master/test) and check that it won't
1722+
# produce (false positive) top-level errors (e.g., by actually running test code, etc.)
1723+
# NOTE this could be very fragile ...
1724+
@testset "test file targets" begin
1725+
let
1726+
res = analyze_file(normpath(FIXTURE_DIR, "error.jl"))
1727+
@test isempty(res.toplevel_error_reports)
1728+
end
1729+
1730+
# TODO pass this test against https://github.com/JuliaLang/julia/blob/ddeba0ba8aa452cb2064eb2d03bea47fe6b0ebbe/test/dict.jl
16071731
end
16081732

16091733
@testset "https://github.com/aviatesk/JET.jl/issues/175" begin

0 commit comments

Comments
 (0)