It’s a performance trick.
When broadcasting, it checks Base.issingletontype(typeof(my_fun)), and concludes that the function is pure enough that it can use ForwardDiff within the broadcast, rather than Zygote’s own differentiation (which calls ChainRules). This is often 100x faster, because Zygote’s differentiation tends to break type stability.
You can write a method my_fun(x::ForwardDiff.Dual) to customise this. Or you can define a rule for the broadcasting, Zygote has many rules which look like this:
@adjoint function broadcasted(::typeof(tanh), x::Numeric)
y = tanh.(x)
y, ȳ -> (nothing, ȳ .* conj.(1 .- y.^2))
end