Allocations when using FunctionWrapper and StaticArrays

I’ve simplified my unnecessarily complicated post to show a simple case where FunctionWrappers allocate whereas using a closure without function argument types doesn’t allocate. I’m not sure why this happens. Any help on this topic is appreciated.

# %%
using StaticArrays
using BenchmarkTools
import FunctionWrappers
# %%

struct DummyModel{I}
    invoker::FunctionWrappers.FunctionWrapper{Float32,Tuple{MVector{I,Float32}}}
    modulecode
end

function compilemodel(y::Vector{Float32})
    I = length(y)
    
    runbody = quote
        function run(x::MVector{$I,Float32})
            m = SVector{$I,Float32}($y)
            first(x-m)
        end
    end
    
    runinvokerbody = quote
        runinvoker() = run
    end

    modulecode = Expr(:block, :(using StaticArrays), runbody, runinvokerbody)
    moduleexpr = Expr(:module, true, gensym(:CompiledModel), modulecode)
    eventmodule = eval(moduleexpr)


    DummyModel{I}(Base.invokelatest(eventmodule.runinvoker), modulecode)
end

# %%
MT = MVector{3,Float32}

modelvec = Vector{Float32}([1,2,3])
model = compilemodel(modelvec)

x = MT([1,2,3])

@btime $(model).invoker($x)

# %%

struct DummyModel1
    invoker::Function
end

function compilemodel1(y::Vector{Float32})    
    I = length(y)
    func = function (x::MVector{I,Float32}) where {I}
        m = SVector{I,Float32}(y)
        first(x-m)
    end
   DummyModel1(func) 
end


model1 = compilemodel1(modelvec)

@btime $(model1.invoker)($x)

Original Post:

I am trying to unroll some matrix operations using StaticArrays based on some model parameters without allocations. Following is a simplified example of how I was going about it in Julia 0.6. However, in Julia 1.1 the same code allocates.

What changes in Julia 1.x are causing this behavior and what is the recommended way to resolve this issue.

using StaticArrays
using BenchmarkTools
import FunctionWrappers

struct DummyModel{I}
    invoker::FunctionWrappers.FunctionWrapper{Float32,Tuple{MVector{I,Float32}}}
    modulecode
end

function compilemodel(y::Vector{Float32})
    I = length(y)
    
    runbody = quote
        function run(x::MVector{$I,Float32})
            m = SVector{$I,Float32}($y)
            first(x-m)
        end
    end
    
    runinvokerbody = quote
        runinvoker() = run
    end

    modulecode = Expr(:block, :(using StaticArrays), runbody, runinvokerbody)
    moduleexpr = Expr(:module, true, gensym(:CompiledModel), modulecode)
    eventmodule = eval(moduleexpr)


    DummyModel{I}(Base.invokelatest(eventmodule.runinvoker), modulecode)
end

function measure_allocations(model, input)
    @allocated model.invoker(input)
end

# %%

MT = MVector{3,Float32}

model = compilemodel(Vector{Float32}([1,2,3]))
x = MT([1,2,3])

# measure_allocations(model, x)
@btime $(model).invoker($x)  # 20.849 ns (1 allocation: 16 bytes)