@@ -1396,104 +1396,198 @@ end
13961396end
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
14941588end
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
16071731end
16081732
16091733@testset " https://github.com/aviatesk/JET.jl/issues/175" begin
0 commit comments