# Keyword arguments preventing eliding boundscheck

**URL:** <https://discourse.julialang.org/t/keyword-arguments-preventing-eliding-boundscheck/95405>\
**Category:** Performance\
**Tags:** inbounds\
**Created:** [March 1, 2023, 7:59pm UTC](https://discourse.julialang.org/t/keyword-arguments-preventing-eliding-boundscheck/95405 "2023-03-01T19:59:36Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![OmarElrefaei](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/omarelrefaei/32/43002_2.png) [@OmarElrefaei](https://discourse.julialang.org/u/OmarElrefaei)\
**Post date:** [March 1, 2023, 7:59pm UTC](https://discourse.julialang.org/t/keyword-arguments-preventing-eliding-boundscheck/95405/1 "2023-03-01T19:59:36Z")

</div>

So I’ve got a MWE here with a weird behavior.  
Note: this seems to be actually fixed in v1.9. But I couldn’t track any relevant issues to PR.

I’m trying to elide some checking code with @boundscheck. (yes, the real use case is a numerical hot loop)

At first I was experimenting with positional arguments:

```julia
f2(x, a=0) = @boundscheck "not elided!"
Base.@propagate_inbounds f3(x, a=0) = @boundscheck "not elided!"

function caller(x)
    @inbounds @show f2(x)
    @inbounds @show f2(x, 2)

    @inbounds @show f3(x)
    @inbounds @show f3(x, 2)
    nothing
end
caller(0)

# f2(x) = "not elided!"
# f2(x, 2) = nothing
# f3(x) = nothing
# f3(x, 2) = nothing

```

After I figured out how to work with `@inbounds` and `@boundscheck` properly, I faced some trouble with optional arguments (`f2`).  
But I figured it was because `f2(x)` was calling `f2(x, a)` without the “inbound-dedness” so `f2(x)` boundschecks was not elided. This was fixed with `propagate_inbounds`.

* * *

But now when I also found out the hard way that a similar thing appears to happen with keyword arguments and is not easily fixed.

```julia

f4(x; k=0) = @boundscheck "not elided!"
@propagate_inbounds f5(x; k=0) = @boundscheck "not elided!"

function caller(x)
    @inbounds @show f4(x)
    @inbounds @show f4(x, k=2)

    @inbounds @show f5(x)
    @inbounds @show f5(x, k=2)
    nothing
end
caller(0)

# f4(x) = "not elided!"
# f4(x, k = 2) = "not elided!"
# f5(x) = nothing
# f5(x, k = 2) = "not elided!"

```

Does anyone know of a possible cause for specifying keyword argument to prevent proper inlining or skipping boundchecks?  
Or maybe there is a common workaround?  
I’ve tried removing the default value of `2` and/or specifying type `k::Int64` but neither changes the behavior.

> **@code\_typed does show very obviously the difference between v1.8.5 and v1.9**
>
> ```julia
> # V1.8.5
> julia> @code_typed f5(1, k=2)
> CodeInfo(
> 1 ─ nothing::Nothing
> │ nothing::Nothing
> │ nothing::Nothing
> └── goto #3 if not true
> 2 ─ goto #4
> 3 ─ goto #4
> 4 ┄ %7 = φ (#2 => "not ellided!!!", #3 => nothing)::Union{Nothing, String}
> └── return %7
> ) => Union{Nothing, String}
> 
> # v1.9
> julia> @code_typed f5(1, k=2)
> CodeInfo(
> 1 ─ nothing::Nothing
> │ nothing::Nothing
> │ nothing::Nothing
> └── goto #3 if not $(Expr(:boundscheck))
> 2 ─ goto #4
> 3 ─ goto #4
> 4 ┄ %7 = φ (#2 => "not ellided!!!", #3 => nothing)::Union{Nothing, String}
> └── return %7
> ) => Union{Nothing, String}
> 
> ```

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [March 1, 2023, 8:37pm UTC](https://discourse.julialang.org/t/keyword-arguments-preventing-eliding-boundscheck/95405/2 "2023-03-01T20:37:52Z")

</div>

Keyword arguments get handled in an auto-generated keyword sorting function that translates a keyword-using function call to a purely positional function call.

```julia-repl
julia> foo(;x) = stacktrace()
foo (generic function with 1 method)

julia> foo(;x=3)
15-element Vector{Base.StackTraces.StackFrame}:
 #foo#12 at REPL[25]:1 [inlined]
 (::var"#foo##kw")(::NamedTuple{(:x,), Tuple{Int64}}, ::typeof(foo)) at REPL[25]:1
 top-level scope at REPL[26]:1
 ...

```

In this example, `(::var"#foo##kw")` is the keyword sorter and `#foo#12` is the keyword-less internal function that it maps to.

I’m guessing that the keyword sorter was not propagating the `@inbounds`. Unfortunately, I have no idea how you might fix this locally. You might need to use `v1.9`, since you say it’s fixed there.

---

<div class="post-metadata">

**Author:** ![OmarElrefaei](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/omarelrefaei/32/43002_2.png) [@OmarElrefaei](https://discourse.julialang.org/u/OmarElrefaei)\
**Post date:** [March 1, 2023, 8:59pm UTC](https://discourse.julialang.org/t/keyword-arguments-preventing-eliding-boundscheck/95405/3 "2023-03-01T20:59:42Z")

</div>

I see …  
I’ve seen some mentions of that sorter function in the docs.

regarding the specific explanation tho: defining with @noinline shows foo as `foo(; x::Int64)`…which I think is just my normal method, right? not a keyword-less internal version.

```plaintext
julia> @noinline foo(;x) = stacktrace();

julia> foo(x=1)
15-element Vector{Base.StackTraces.StackFrame}:
 foo(; x::Int64) at REPL[17]:1
 (::var"#foo##kw")(::NamedTuple{(:x,), Tuple{Int64}}, ::typeof(foo)) at REPL[17]:1
 top-level scope at REPL[18]:1
 eval at boot.jl:373 [inlined]
...

```

Unfortunately I won’t be able to use 1.9 for quite some time for IT bureaucratic reasons.

Your explanation makes sense though, I’ll have to live with it.  
I guess that is one of the remaining reasons keyword args aren’t commonly used in numerical code.
