LoweredCodeUtils.jl icon indicating copy to clipboard operation
LoweredCodeUtils.jl copied to clipboard

LoweredCodeUtils does not compose with CassetteOverlay

Open Keno opened this issue 2 years ago • 4 comments

┌ Error: Failed to revise /home/keno/.julia/dev/InterfaceSpecs/src/engines/property.jl
│   exception =
│    KeyError: key :PropCheckOverlay not found
│    Stacktrace:
│     [1] getindex
│       @ Base ./dict.jl:504 [inlined]
│     [2] identify_framemethod_calls(frame::JuliaInterpreter.Frame)
│       @ LoweredCodeUtils ~/.julia/packages/LoweredCodeUtils/30gbF/src/signatures.jl:168
└ @ Revise ~/.julia/packages/Revise/7HQ7u/src/packagedef.jl:725

while trying to use revise.

@MethodTable PropCheckOverlay

@overlay PropCheckOverlay function forall(prop, T)
    instances = try
        nonoverlay(testinstances, T)
    catch err
        @error "Error during test instance creation" err=err
        rethrow(err)
    end
    for x in instances
        prop(x)()
    end
    return
end

Keno avatar Sep 09 '23 04:09 Keno

Here's why this happens: typically there's a "registration" of the :method (the Expr(:method, :f)) before defining it:

julia> Meta.lower(Main, :(f() = nothing))
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─      $(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─     return $(Expr(:method, :f))
)))
│        $(Expr(:method, :f))
│   %3 = Core.Typeof(f)
│   %4 = Core.svec(%3)
│   %5 = Core.svec()
│   %6 = Core.svec(%4, %5, $(QuoteNode(:(#= REPL[1]:1 =#))))
│        $(Expr(:method, :f, :(%6), CodeInfo(
    @ REPL[1]:1 within `none`
1 ─     return nothing
)))
└──      return f
))))

But @overlay isn't doing that:

julia> Meta.lower(Main, :(Base.Experimental.@overlay SomeMT f() = nothing))
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = Core.Typeof(f)
│   %2 = Core.svec(%1)
│   %3 = Core.svec()
│   %4 = Core.svec(%2, %3, $(QuoteNode(:(#= REPL[4]:1 =#))))
│        $(Expr(:method, :SomeMT, :(%4), CodeInfo(
    @ REPL[4]:1 within `none`
1 ─     return nothing
)))
└──      return nothing
))))

I wonder if the actual bug is in @overlay rather than here? Or is it basically because of the split between @MethodTable and @overlay?

timholy avatar Sep 12 '23 16:09 timholy

There isn't a full reproducer here, but one option might be to try this diff:

$ git diff
diff --git a/src/signatures.jl b/src/signatures.jl
index 4ae33d5..3318b75 100644
--- a/src/signatures.jl
+++ b/src/signatures.jl
@@ -165,7 +165,7 @@ function identify_framemethod_calls(frame)
             key = stmt.args[1]
             key = normalize_defsig(key, frame)
             if key isa Symbol
-                mi = methodinfos[key]
+                mi = get(methodinfos, key, MethodInfo(1))
                 mi.stop = i
             elseif key isa Expr   # this is a module-scoped call. We don't have to worry about these because they are named
                 continue

and see how far that gets you.

timholy avatar Sep 12 '23 18:09 timholy

In the variant of :method with more than one arg, the fname is ignored semantically, so it was re-used for the MethodTable argument. If LoweredCodeUtils is making use of it for something other than the mt, it's probably doing something incorrect:

https://github.com/JuliaLang/julia/blob/master/src/interpreter.c#L100-L112

Keno avatar Sep 13 '23 04:09 Keno

That's essentially what this package is doing, too: https://github.com/JuliaDebug/LoweredCodeUtils.jl/blob/ef1249561081b182e8958e8ac56b4bfdfb6c9d4e/src/signatures.jl#L154-L172

But it expects to see a 1-arg Expr(:method) first, followed by a 3-arg Expr(:method). That's where the get option above might be the right fix, but I can't really test because I don't have a way of reproducing this yet.

If you can provide a recipe for reproducing it, I can fix it.

timholy avatar Sep 13 '23 12:09 timholy