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

Keyword arguments overlay not called

Open pepijndevos opened this issue 2 years ago • 7 comments

Ok I reduced my actual bug down to a MWE

using CassetteOverlay

struct ParamLens
end

function (🔍::ParamLens)(;kwargs...)
    @show "ParamLens", kwargs
end

struct ParamInterp{M, S} <: CassetteOverlay.AbstractBindingOverlay{M, S}
end

function (self::ParamInterp)(lens::ParamLens; kwargs...)
    @show "ParamInterp", kwargs
end


interp = ParamInterp{nothing, nothing}()

inst = ParamLens()

test1() = inst()
test1()
interp(test1)

test2() = inst(; foo=3)
test2()
interp(test2);

This prints

("ParamLens", kwargs) = ("ParamLens", Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}())
("ParamInterp", kwargs) = ("ParamInterp", Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}())
("ParamLens", kwargs) = ("ParamLens", Base.Pairs(:foo => 3))
("ParamLens", kwargs) = ("ParamLens", Base.Pairs(:foo => 3))

I would expect the last line to be ParamInterp but it just bypasses the method

pepijndevos avatar May 15 '23 08:05 pepijndevos

function (self::ParamInterp)(lens::ParamLens; kwargs...)
    @show "ParamInterp", kwargs
end

is not the expected interface for overlaying a method (and it does not make sense to execute code with this package if you do not use external method table).

You should do something like:

@MethodTable LensTable;

@overlay LensTable function (🔍::ParamLens)(;kwargs...)
    @show "ParamInterp", kwargs
end

interp = ParamInterp{@__MODULE__, :LensTable}()

aviatesk avatar May 16 '23 04:05 aviatesk

You can do something like

using CassetteOverlay

struct ParamLens end

function (🔍::ParamLens)(;kwargs...)
    @show "ParamLens", kwargs
end

struct ParamInterp{M, S} <: CassetteOverlay.AbstractBindingOverlay{M, S} end

# HACK get the keyword sorter of `ParamLens()` and define the overlayed keyword method with it...
let src = only(code_lowered(ParamLens()))
    call = src.code[end-1]
    @assert Meta.isexpr(call, :call) && call.args[1] isa Core.GlobalRef
    kwsorter = Core.Compiler.abstract_eval_globalref(call.args[1]::Core.GlobalRef).val
    global function (interp::ParamInterp)(::Core.Typeof(kwsorter), kwargs::Base.pairs(NamedTuple), 🔍::ParamLens)
        @show "ParamInterp", kwargs
    end
end

interp = ParamInterp{nothing, nothing}()

const 🔍 = ParamLens()

test1() = 🔍()
test1()
interp(test1)

test2() = 🔍(; foo=3)
test2()
interp(test2);

aviatesk avatar May 17 '23 08:05 aviatesk

After #34, you should switch to the following pattern:

using CassetteOverlay

struct ParamLens end

function (🔍::ParamLens)(; kwargs...)
    @show "ParamLens", kwargs
    nothing
end

@MethodTable ParamInterpTable
mutable struct ParamInterp <: AbstractBindingOverlay{@__MODULE__, :ParamInterpTable} 
    some_state
end
@overlay ParamInterpTable function (🔍::ParamLens)(; kwargs...)
    @show "ParamLens", kwargs
    @show "ParamInterp", getpass()
    nothing
end

interp = ParamInterp(nothing)

const 🔍 = ParamLens()

test1() = 🔍()
test1()
interp(test1);

test2() = 🔍(; foo=3)
test2()
interp(test2);

Can you switch your use case to this pattern?

aviatesk avatar May 19 '23 09:05 aviatesk

Yes this would work but the example you give fails with the following error on 0.1.6

julia> interp(test1);
ERROR: type DataType has no field body

pepijndevos avatar May 25 '23 14:05 pepijndevos

It seems to be another bug.

aviatesk avatar May 25 '23 14:05 aviatesk

Is there a fix for this bug on the horizon?

pepijndevos avatar Jun 05 '23 15:06 pepijndevos

For the time being, you can define the following overload:

# a temporal fix for #33
@inline (::ParamInterp)(::typeof(Base.typename), @nospecialize args...) =
    Base.typename(args...)

to circumvent the issue.

Hopefully we will not hit the root issue that often.

aviatesk avatar Jun 06 '23 06:06 aviatesk

I opened a new issue #45 with a reduced example. Let's focus on this issue there.

aviatesk avatar Jun 18 '24 15:06 aviatesk