@generated function not picking up all the methods

The code:

module Test

func(x) = false

@generated function test(x::T) where T
    if Base.invokelatest(func, T)
        :( 1 )
    else
        :( 0 )
    end
end

function test2(x::T) where T
    if func(T)
        1
    else
        0
    end
end

end


Test.func(::Type{UInt64}) = true

println(Test.test(UInt64(1))) # outputs 0
println(Test.test2(UInt64(1))) # outputs 1

Basically, the underlying problem is that there’s a function test that needs to do a heavy computation, but only once for type T. In the process it calls func for type T, which also needs to do a heavy computation, but for some types T the user can provide a precomputed result.

As seen from the code, the @generated function does not pick up the user-defined method of func even with invokelatest(), while a non-generated test2() has no problem with it. Moreover, if one prints methods(func) in test(), it shows the user-defined method, but it still does not get called. Is it a bug or an intended behavior?

It’s intended - generated functions always execute in the world age that they are defined in. The reason behind this is that we don’t track backedges to generated functions, so we wouldn’t know when changes to the global state would affect the output of the generated function and without being guaranteed that the output of the generated function doesn’t change the system would be unsound and could potentially cause crashes. Imagine one version of the function returns a pointer and the next an integer - if we didn’t guarantee that the output doesn’t change, we’d now interpret the integer as a pointer and bad things would happen.

3 Likes

I see, thanks. I guess I’ll have to work around that.