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.
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.
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?