Does `@inbounds` work for functions with keyword arguments?

Consider the following functions:

@inline function f1(n)
    @boundscheck n == 0 && error("zero")
    n
end

@inline function f2(n; m = 0)
    @boundscheck n == 0 && error("zero")
    n+m
end

g1(n) = @inbounds f1(n)
g2(n) = @inbounds f2(n)
g3(n) = @inbounds f2(n; m = 0)

I thought that g1(0), g2(0) and g3(0) would all return 0. However,

julia> g1(0)
0

julia> g2(0)
ERROR: zero

julia> g3(0)
ERROR: zero

Is this a bug, or am I missing anything here? I couldn’t find anything in the documentation suggesting that @inbounds doesn’t work for functions with keyword arguments.

I think @inline is not sufficient to ellide the boundchecks. Try with @propagate_inbounds instead.

1 Like

Yes, with @propagate_inbounds I get g2(0) == g3(0) == 0. But at the same time, this applies @inbounds to all functions called in f2. This may not be desired. Example: Keep g1, g2 and g3 and define

@inline function f0(n)
    @boundscheck n == 0 && error("zero")
    n
end

@inline function f1(n)
    @boundscheck n == 0 && error("zero")
    f0(n)
end

Base.@propagate_inbounds function f2(n; m = 0)
    @boundscheck n == 0 && error("zero")
    f0(n+m)
end

Now

julia> g1(0)
ERROR: zero

julia> g2(0)
0

I think there should be a way to disable bounds checking in f2 without disabling it in all functions called from f2. In this sense, I’m still asking myself whether the current behavior is intended or a bug.

I think this is effectively Functions with default arguments with `@boundscheck` can be confusing · Issue #30411 · JuliaLang/julia · GitHub@inbounds only propagates through one function call but kwargs (and default positional args) are implemented with two function calls.

4 Likes

Yes, that explains it. Since the issue you referenced is still open, I wonder what the developers are planning. Wouldn’t the problem disappear if the compiler automatically added @propagate_inbounds to every additional function it needs to handle keyword and default arguments?