diff --git a/src/macros.jl b/src/macros.jl index 64c6537..a46526b 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -362,20 +362,23 @@ macro grad_from_chainrules(fcall) # `track` and the exec methods only see positional types track_whereparams = map(esc, positional_whereparams(d[:whereparams], d[:args])) - # argument names are escaped and may shadow `$f` in the bodies, so bind the function via its type + # argument names are escaped and may shadow `$f` in the bodies, so bind the function via its type, + # under a generated name: the self-argument of a callable-object method is not renamed by macro + # hygiene, so a fixed name would clash with an argument of the same name (#334) + self = gensym(:func) return quote - (func::$ReverseDiff._selftype($f))($(sig_args...); $(sig_kwargs...)) where {$(whereparams...)} = - $ReverseDiff.track(func, $(call_args...); $(call_kwargs...)) - function $ReverseDiff.track(func::$ReverseDiff._selftype($f), $(track_args...); kwargs...) where {$(track_whereparams...)} + ($self::$ReverseDiff._selftype($f))($(sig_args...); $(sig_kwargs...)) where {$(whereparams...)} = + $ReverseDiff.track($self, $(call_args...); $(call_kwargs...)) + function $ReverseDiff.track($self::$ReverseDiff._selftype($f), $(track_args...); kwargs...) where {$(track_whereparams...)} args = ($(call_args...),) tp = $ReverseDiff.tape(args...) - output_value, back = $ChainRulesCore.rrule(func, map($ReverseDiff.value, args)...; kwargs...) + output_value, back = $ChainRulesCore.rrule($self, map($ReverseDiff.value, args)...; kwargs...) output = $ReverseDiff.track(output_value, tp) - closure(cls_args...; cls_kwargs...) = $ChainRulesCore.rrule(func, map($ReverseDiff.value, cls_args)...; cls_kwargs...) + closure(cls_args...; cls_kwargs...) = $ChainRulesCore.rrule($self, map($ReverseDiff.value, cls_args)...; cls_kwargs...) $ReverseDiff.record!( tp, $ReverseDiff.SpecialInstruction, - func, + $self, args, output, (back, closure, kwargs), diff --git a/test/ChainRulesTests.jl b/test/ChainRulesTests.jl index 633a321..e812f69 100644 --- a/test/ChainRulesTests.jl +++ b/test/ChainRulesTests.jl @@ -565,6 +565,21 @@ ReverseDiff.@grad_from_chainrules f_names( @test results[2] == fill(5, size(y)) end +# an argument named `func`, the name of the generated self-argument in 1.18.8 (#334) +f_func(func, x) = func(sum(4x)) + +function ChainRulesCore.rrule(::typeof(f_func), func, x) + pb_f_func(Δ) = (NoTangent(), NoTangent(), fill(3 * Δ, size(x))) + return f_func(func, x), pb_f_func +end + +ReverseDiff.@grad_from_chainrules f_func(func, x::ReverseDiff.TrackedArray) + +@testset "argument named func" begin + x = rand(3, 3) + @test ReverseDiff.gradient(x -> f_func(identity, x), x) == fill(3, size(x)) +end + @testset "invalid signatures" begin @test_throws "has to be applied to a function signature" @eval ReverseDiff.@grad_from_chainrules f_where @test_throws "at least one positional argument" @eval ReverseDiff.@grad_from_chainrules f_where()