FunctionWrapper somewhat dodges world age restrictions

MWE below, it appears that rather than using a function in a preexisting world age, FunctionWrapper uses the first evaled method inside a function but not any after that; EDIT unless you redefine the function after a call even with identical code, or if the inner evaled function was called before. If someone has an explanation that would be great, but otherwise this could just be an FYI. EDIT: Just in case, you should never do this without invokelatest because there’s no benefit to making a method invalidate its own compiled code on every call.

julia> function foo(n)
         for i in n:n+1
           @eval fooi() = $i
           println(fooi())
         end
       end
           
julia> foo(1)
ERROR: MethodError: no method matching fooi()
The applicable method may be too new: running in world age 32434, while current world is 32435.
Closest candidates are:
  fooi() at REPL[1]:3 (method too new to be called from this world context.)
...
julia> fooi() # foo(1) made fooi()=1 before error
1

julia> foo(2) # uses preexisting fooi()=1
1
1
julia> using FunctionWrappers: FunctionWrapper as Fw

julia> function bar(n)
         for i in n:n+1
           @eval bari() = $i
           println(Fw{Int, Tuple{}}(bari)())
         end
       end
bar (generic function with 1 method)

julia> bar(1) # doesn't error, but only uses 1st bari()=1
1
1

julia> bari() # last run made final bari()=2
2

julia> bar(3) # uses 1st bari()=3, not preexisting bari()=2
3
3

julia> function bar(n)
         for i in n:n+1
           @eval bari() = $i
           println(Fw{Int, Tuple{}}(bari)())
         end
       end
bar (generic function with 1 method)

julia> bari()
4

julia> bar(5) # now uses preexisting bari()=4
4
4

Now evading this effect to be more in line with world age issues:

julia> using FunctionWrappers: FunctionWrapper as Fw

julia> bari() = 0
bari (generic function with 1 method)

julia> function bar(n)
         for i in n:n+1
           @eval bari() = $i
           println(Fw{Int, Tuple{}}(bari)())
         end
       end
bar (generic function with 1 method)

julia> bari() # this call is crucial to avoid the previous effect
0

julia> bar(1) # uses preexisting bari()=0
0
0

World age was made for optimization reasons, for example, by inlining then doing a bunch of stuff. Function wrapper was likely implemented as a function pointer and thus can use the newest version of the function.

It doesn’t, if it did then the looped println would show two adjacent integers, not the same one twice.

I think some weird optimization issues. I don’t think it’s a good idea to rely on these internal behaviors then.

That sounds like a codegen bug if reproduceable, as FunctionWrappers always should see the latest world when the code is called

Edit: I think I recall now that we didn’t add support for functions that are inferred to be constant. We should, but haven’t yet, and might not have an issue open for it?