# Type instability with Type{T} selectors in keyword arguments

**URL:** <https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439>\
**Category:** General Usage\
**Created:** [May 29, 2025, 2:02pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439 "2025-05-29T14:02:47Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![marcsgil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marcsgil/32/33908_2.png) [@marcsgil](https://discourse.julialang.org/u/marcsgil)\
**Post date:** [May 29, 2025, 2:02pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/1 "2025-05-29T14:02:47Z")

</div>

I’m trying to write a function that outputs arrays that are filled according to a certain computation. I want the user to be able to select the type of the array according to an argument.

Consider these two example functions:

```julia
function my_fill1(val, array_constructor::Type{AC}=Array) where {AC<:AbstractArray}
    T = typeof(val)
    result = AC{T, 1}(undef, 10)
    result .= val
    result
end

function my_fill2(val; array_constructor::Type{AC}=Array) where {AC<:AbstractArray}
    T = typeof(val)
    result = AC{T, 1}(undef, 10)
    result .= val
    result
end

```

In both versions, I’m using the `Type{T}` selector to avoid type instabilities. The only difference between them is that, in the first version, `array_constructor` is a positional argument, while, in the second, it is a keyword argument. For my use case, the function has many more arguments, so I would really prefer use the version with keyword arguments.

When I test both versions for type stability with the default values for `array_constructor`, I get positive results:

```julia
@code_warntype my_fill1(1) # type stable
@code_warntype my_fill2(1) # type stable

```

Nonetheless, when I overwrite the default values, `my_fill1` remains stable while `my_fill2` becomes unstable:

```julia
@code_warntype my_fill1(1, Array) # type stable
@code_warntype my_fill2(1, array_constructor=Array) # type unstable (output type is Any)

```

What is going on here? Am I doing something wrong? Thanks in advance for any help!

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 29, 2025, 2:19pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/2 "2025-05-29T14:19:16Z")

</div>

Whenever you’re testing something sensitive with types, it’s easy for `@code_warntype` to get the specialization and constant propagation heuristics wrong. If `Array` is often written as a literal in the cases you’re interested in, the more “typical” result you’d see would be reflected with:

```julia
julia> f() = my_fill2(1, array_constructor=Array)
f (generic function with 1 method)

julia> @code_warntype f()
MethodInstance for f()
MethodInstance for f()
  from f() @ Main REPL[4]:1
Arguments
  #self#::Core.Const(Main.f)
Body::Vector{Int64}

```

cf [Can we support a way to make `@code_typed` stop lying to us all? :) · Issue #32834 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/32834)

---

<div class="post-metadata">

**Author:** ![marcsgil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marcsgil/32/33908_2.png) [@marcsgil](https://discourse.julialang.org/u/marcsgil)\
**Post date:** [May 29, 2025, 2:59pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/3 "2025-05-29T14:59:13Z")

</div>

Just to be sure I understood what you said: you mean that there is no problem with the way the functions are implemented, just with `@code_warntype`?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 29, 2025, 3:07pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/4 "2025-05-29T15:07:40Z")

</div>

Yeah, mostly. But it’s also tricky because a function’s performance depends not just upon the function definition itself but also _how it’s called_. `@code_warntype` is trying to _guess_ how it _might_ get called.

Putting it in a simple function that represents how _you know_ it’ll get called is always a good answer.

---

<div class="post-metadata">

**Author:** ![marcsgil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marcsgil/32/33908_2.png) [@marcsgil](https://discourse.julialang.org/u/marcsgil)\
**Post date:** [May 29, 2025, 3:14pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/5 "2025-05-29T15:14:28Z")

</div>

I see. In the setting that I’m imagining, the user will just set this keyword value as `CuArray` or some other GPU array type according to their hardware. Then this should be safe?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 29, 2025, 3:19pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/6 "2025-05-29T15:19:41Z")

</div>

Separate issues.

1. The `code_` reflection methods redo type inference and compilation given the input types, which isn’t always what Julia does. It’s possible for Julia to compile a method once for a wider argument type annotation instead of for each specific input type (`@nospecialize`, some cases of `::Function`, `::Type`, and `::Vararg`), and it’s possible for Julia to infer the call’s return type given the type annotation instead of each input type (`Base.@nospecializeinfer`). The former can occur without the latter, in other words the compiler can figure out a call’s return type but choose to use compiled code that didn’t use the same degree of type inference. `@code_warntype` won’t show those nuances, and while it’s arguable that we shouldn’t assume that the inferred types are always relevant to execution, `@code_llvm` only generates compiled code that specializes completely, hence the “lying.”

2. That’s not what’s making the difference for keyword arguments here, in fact `@code_warntype` is doing the opposite of over-specialization. Keyword input types _are_ specialized for compilation (this is different from the “method specialization” where you _write_ multiple methods with varied positional arguments), but `::Type{AC}` annotations get lost in the process. The implementation puts the keyword arguments in a `NamedTuple` like `(array_constructor = AC,)`, which only retains `AC`’s type (`DataType` or `UnionAll`) for type inference instead of the specific `AC`. Making a positional argument or passing in a dummy instance for an inner `typeof` call are workarounds, though the latter isn’t always feasible like in the `Array` case.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 29, 2025, 3:31pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/7 "2025-05-29T15:31:27Z")

</div>

> [@Benny](#):
>
> but `::Type{AC}` annotations get lost in the process.

That’s technically true (I think), but it’s deceiving because — as demonstrated above in the `@code_warntype f()` — this can be recovered through constant propagation/inlining.

It’s probably also worth noting that `AC{T,1}` isn’t the generic operation that you’d hope, even though `AC <: AbstractArray`. While `AbstractArray` itself has the two type parameters like this — and while all subtypes need to report what those parameters are — the exact parameters on the concrete type can _and do_ vary wildly… and the constructors themselves aren’t a part of the array interface. You might as well pass a function or flag instead of the type in any case.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 29, 2025, 3:44pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/8 "2025-05-29T15:44:22Z")

</div>

Constant propagation is an extra layer of nuance that I think was getting missed next to the `code_` limitations (“there is no problem with the way the functions are implemented”), which is why I pointed out the fundamental loss of `Type{AC}` annotations of keyword arguments via `NamedTuple`s. I don’t actually know how fixable that is, but it’s hypothetically possible to pass `AC`.

`@code_warntype f()`, or as I prefer to avoid using up names with `@code_warntype (()->my_fill2(1, array_constructor=Array))()`, is the appropriate usage of `@code_warntype` when you expect the inputs to be constants or static parameters known by the compiler. The calls in the original post assume the opposite. In either case, I don’t think `@code_warntype` is lying about type inference this time.

---

<div class="post-metadata">

**Author:** ![marcsgil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marcsgil/32/33908_2.png) [@marcsgil](https://discourse.julialang.org/u/marcsgil)\
**Post date:** [May 29, 2025, 3:49pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/9 "2025-05-29T15:49:19Z")

</div>

About the lack of generality, I know that the restriction `AC <: AbstractArray` will not solve it. It will accept `array_constructor=Matrix` but then the constructor `AC{T, 1}` will error. I’m ok with that. The actual calculation that I’m interested only makes sense if can call `AC{T, N}`.

If I used functions or flags, then the result would be type unstable, no? Isn’t that the whole point of the `Type` selector?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 29, 2025, 3:58pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/10 "2025-05-29T15:58:58Z")

</div>

> [@marcsgil](#):
>
> If I used functions or flags, then the result would be type unstable, no? Isn’t that the whole point of the `Type` selector?

Yes, it would be if the flags were not distinguished by their type like `array_type = :gpu`. But if you pass in a function (different functions are different types) or a dedicated `struct GPU; end`, with `array_type = GPU()` then you’d be in the clear.

---

<div class="post-metadata">

**Author:** ![marcsgil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marcsgil/32/33908_2.png) [@marcsgil](https://discourse.julialang.org/u/marcsgil)\
**Post date:** [May 29, 2025, 4:07pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/11 "2025-05-29T16:07:01Z")

</div>

I guess in this case I’d need to include the corresponding GPU packages as dependencies, or at least as extensions, of my package. I’d prefer to avoid that. The actual calculations I’m doing involve `KernelAbstractions`, which allow me to write the kernels without depending on the GPU packages.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 29, 2025, 4:53pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/12 "2025-05-29T16:53:43Z")

</div>

Ideally you’re not writing your own `fill`-like function at all, just calling `genericfill(sufficient_array_type::AbstractArray, value, dims::Integer...)`, but that doesn’t exist. As pointed out, abstract types don’t impose any structure on the type parameters of their subtypes, so it’s not feasible for you to write 1 method or constructor call for arbitrary dense array types, even if just limited to 1D vectors. That’s what multiple dispatch is for.

`fill!(A, x)` however does exist, I just don’t know if all the array types you’re interested in support that, and the users would have to instantiate (a likely uninitialized) `A` themselves.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 29, 2025, 5:15pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/13 "2025-05-29T17:15:09Z")

</div>

If you’re already using KernelAbstractions.jl, you can use what they provide, no? See, e.g., [`KernelAbstractions.allocate`](https://juliagpu.github.io/KernelAbstractions.jl/dev/api/#KernelAbstractions.allocate) and the `Backend` flags.

---

<div class="post-metadata">

**Author:** ![marcsgil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marcsgil/32/33908_2.png) [@marcsgil](https://discourse.julialang.org/u/marcsgil)\
**Post date:** [May 29, 2025, 7:47pm UTC](https://discourse.julialang.org/t/type-instability-with-type-t-selectors-in-keyword-arguments/129439/14 "2025-05-29T19:47:02Z")

</div>

Yes, this is actually exactly what I need, thank you for pointing that out!
