From 2328358a8bf210e840a59209eeee238658daf6f3 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 6 Jan 2024 04:11:40 -0600 Subject: [PATCH] Eval thunks that create methods Fixes #792 --- src/lowered.jl | 19 +++++++++++++++++-- test/backedges.jl | 2 +- test/runtests.jl | 22 +++++++++++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/lowered.jl b/src/lowered.jl index fdf6ba35..d47fa7f9 100644 --- a/src/lowered.jl +++ b/src/lowered.jl @@ -54,13 +54,25 @@ function categorize_stmt(@nospecialize(stmt)) ismeth, haseval, isinclude, isnamespace, istoplevel = false, false, false, false, false if isa(stmt, Expr) haseval = matches_eval(stmt) - ismeth = stmt.head === :method + ismeth = stmt.head === :method || (stmt.head === :thunk && defines_function(only(stmt.args))) istoplevel = stmt.head === :toplevel isnamespace = stmt.head === :export || stmt.head === :import || stmt.head === :using isinclude = stmt.head === :call && stmt.args[1] === :include end return ismeth, haseval, isinclude, isnamespace, istoplevel end +# Check for thunks that define functions (fixes #792) +function defines_function(ci) + isa(ci, CodeInfo) || return false + if length(ci.code) == 1 + stmt = ci.code[1] + if isa(stmt, Core.ReturnNode) + val = stmt.val + isexpr(val, :method) && return true + end + end + return false +end """ isrequired, evalassign = minimal_evaluation!([predicate,] methodinfo, src::Core.CodeInfo, mode::Symbol) @@ -280,6 +292,9 @@ function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, fra end isassign(frame, pc) && assign_this!(frame, value) pc = next_or_nothing!(frame) + elseif head === :thunk && defines_function(only(stmt.args)) + mode !== :sigs && Core.eval(mod, stmt) + pc = next_or_nothing!(frame) # elseif head === :thunk && isanonymous_typedef(stmt.args[1]) # # Anonymous functions should just be defined anew, since there does not seem to be a practical # # way to find them within the already-defined module. @@ -300,7 +315,7 @@ function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, fra add_signature!(methodinfo, sig, lnn) end end - pc += 1 + pc = next_or_nothing!(frame) else pc, pc3 = ret # Get the line number from the body diff --git a/test/backedges.jl b/test/backedges.jl index 9af7397c..440adaf6 100644 --- a/test/backedges.jl +++ b/test/backedges.jl @@ -17,7 +17,7 @@ do_test("Backedges") && @testset "Backedges" begin src2 = src.code[idtype].args[1] methodinfo = Revise.MethodInfo() isrequired = Revise.minimal_evaluation!(methodinfo, frame, :sigs)[1] - @test sum(isrequired) == length(src.code)-2 # skips the `return` at the end + @test sum(isrequired) == length(src.code)-1 # skips the `return` at the end src = """ # issue #249 diff --git a/test/runtests.jl b/test/runtests.jl index 7b83352e..3f743209 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,7 +15,7 @@ using Base.CoreLogging: Debug,Info using Revise.CodeTracking: line_is_decl # In addition to using this for the "More arg-modifying macros" test below, -# this package is used on Travis to test what happens when you have multiple +# this package is used on CI to test what happens when you have multiple # *.ji files for the package. using EponymTuples @@ -41,6 +41,12 @@ macro addint(ex) :($(esc(ex))::$(esc(Int))) end +macro empty_function(name) + return esc(quote + function $name end + end) +end + # The following two submodules are for testing #199 module A f(x::Int) = 1 @@ -1409,6 +1415,20 @@ const issue639report = [] @test MacroLineNos568.my_fun() == 30 rm_precompile("MacroLineNos568") + # Macros that create empty functions (another macro *execution* bug, issue #792) + file = tempname() + write(file, "@empty_function issue792f1\n") + sleep(mtimedelay) + includet(ReviseTestPrivate, file) + sleep(mtimedelay) + @test isempty(methods(ReviseTestPrivate.issue792f1)) + open(file, "a") do f + println(f, "@empty_function issue792f2") + end + yry() + @test isempty(methods(ReviseTestPrivate.issue792f2)) + rm(file) + pop!(LOAD_PATH) end