# Non critical type-instability?

**URL:** <https://discourse.julialang.org/t/non-critical-type-instability/39486>\
**Category:** General Usage\
**Tags:** parametric-types\
**Created:** [May 14, 2020, 6:31pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486 "2020-05-14T18:31:38Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [May 14, 2020, 6:31pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/1 "2020-05-14T18:31:38Z")

</div>

Suppose the following

`struct A{T} v::Vector{T} end`

`Base.convert(::Type{A{T}}, a::A{S}) where {T,S} = A(convert(Vector{T}, a.v))`

`Base.promote_rule(::Type{A{T}}, ::Type{A{S}}) where {T,S} = A{promote_type(T,S)}`

> `function *(a::A{T}, b::A{S}) where {T,S}`  
> `a,b = promote(a,b)`  
> `c = typeof(a)(zero(a.v))`  
> `for k in eachindex(a.v)`  
> `my_product!(c, a, b, k)`  
> `end`  
> `return c `  
> `end`

where `my_product!` is some customized inlined operation which updates `c.v[k]`.

Let `a::A{BigFloat}` and `b::A{Float64}`. Looking at `@code_warntype *(a, b)` shows that the type of b in red as the union of `A{Float64}` and `A{BigFloat}` but the type of `a` is correctly inferred as `A{BigFloat}`.

What is the reason for this? I was expecting the compiler to either not know for both or know for both…

Is it critical for performance (assuming `my_product!`is type stable of course)? I do see a large time differences by using BenchmarkTools, but it is not clear if it only due to the `promote` step in the definition of `*`… Is there a macro that can clarify this?

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [May 14, 2020, 6:40pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/2 "2020-05-14T18:40:13Z")

</div>

I suspect part of the problem is that you’re re-assigning the output of `promote` to the same variables `a, b`. The compiler’s pretty good at handling that sort of thing, but it makes reading `@code_warntype` unnecessarily confusing since the values pointed to by those variables will have different types before and after that assignment.

What happens if you do `a_promoted, b_promoted = promote(a, b)` and then do your operations on those?

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [May 14, 2020, 6:54pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/3 "2020-05-14T18:54:30Z")

</div>

Interesting, now it is `c` who is unstable (i.e. Body is in red) despite the fact that `a_promoted` seems to be well predicted and `c = typeof(a_promoted)(zero(a_promoted.v))`…

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [May 14, 2020, 9:33pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/4 "2020-05-14T21:33:18Z")

</div>

I did a bit more reading about @code\_warntype. So, the type of `c` is in fact properly inferred but the returned type is not properly inferred… This seems very odd since we return `c`, no?

In fact, does this falls into the comment of @rdeits:

> I suspect part of the problem is that you’re re-assigning the output of `promote` to the same variables `a, b` . The compiler’s pretty good at handling that sort of thing, but it makes reading `@code_warntype` unnecessarily confusing since the values pointed to by those variables will have different types before and after that assignment.

Subsequently, can I conclude from this answer that the type instability displayed by `@code_warntype` is not “really there”?

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [May 14, 2020, 9:35pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/5 "2020-05-14T21:35:20Z")

</div>

I am just guessing here, but what happens if you then define the type returned by the function in their signature?

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [May 14, 2020, 9:42pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/6 "2020-05-14T21:42:08Z")

</div>

Do you mean `Base.:*(a::A{T}, b::A{S})::A{promote_type(T,S)} where {T,S}`? If so, I get the error `ERROR: TypeError: in Type{...} expression, expected UnionAll, got typeof(promote_type)`

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [May 14, 2020, 10:09pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/7 "2020-05-14T22:09:54Z")

</div>

Sorry, I did miss this detail, if you depend on a function to know the type of the method I do not think you can do that. I am not expert at that, so I can be wrong, but I think people use [`@generated`](https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#Generated-functions-1) for that. Or just typeassert at the call if you know the return type statically in that context.

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [May 14, 2020, 10:33pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/8 "2020-05-14T22:33:31Z")

</div>

Oh yes perhaps `@generated` would solve it… (as I cannot know a priori the return type unless `a` and `b` are specified) Thanks for your help!

Nonetheless, I am still curious if that would matter in the end. Indeed, I understood the comment of @rdeits as: " anyways it is a display problem from `@code_warntype` but the compiler knows what to do".

**EDIT:**  
here is the `@generated` piece:  
`@generated Base.:*(a::A, b::B) = :(*(promote(a, b)...)`  
and the regular multiplication is

> `function *(a::T, b::T) where {T<:A}`  
> `c = T(zero(a.v))`  
> `for k in eachindex(a.v)`  
> `my_product!(c, a, b, k)`  
> `end`  
> `return c `  
> `end`

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [May 14, 2020, 10:54pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/9 "2020-05-14T22:54:27Z")

</div>

> Interesting, now it is `c` who is unstable (i.e. Body is in red) despite the fact that `a_promoted` seems to be well predicted and `c = typeof(a_promoted)(zero(a_promoted.v))` …

I can’t reproduce that–using `a_` and `b_` for the promoted variables, everything looks fine to me:

```julia
julia> function *(a::A{T}, b::A{S}) where {T,S}
         a_, b_ = promote(a,b)
         c = typeof(a_)(zero(a_.v))
         for k in eachindex(a_.v)
           my_product!(c, a_, b_, k)
         end
         return c
       end
* (generic function with 1 method)

```

```julia
julia> @code_warntype a * b
Variables
  #self#::Core.Compiler.Const(*, false)
  a::A{BigFloat}
  b::A{Float64}
  a_::A{BigFloat}
  @_5::Int64
  b_::A{BigFloat}
  c::A{BigFloat}
  @_8::Union{Nothing, Tuple{Int64,Int64}}
  k::Int64

Body::A{BigFloat}

```

By the way, a typical way to implement this sort of promoted math operation is to separate the promotion step from the multiplication. That is, you write out a single `*` function given two inputs of the same type:

```julia
julia> function *(a::A{T}, b::A{T}) where {T}
         c = typeof(a)(zero(a.v))
         for k in eachindex(a.v)
           my_product!(c, a, b, k)
         end
         return c
       end

```

and then a more generic `*` method for arguments of different types, in which you promote the two arguments to a common type and call `*` on the result:

```julia
julia> function *(a::A{T}, b::A{S}) where {T,S}
         *(promote(a, b)...)
       end

```

This avoids needing to worry about mixing up the promoted and un-promoted input arguments within the body of the function, and it helps keep your core `*` function simpler.

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [May 14, 2020, 11:01pm UTC](https://discourse.julialang.org/t/non-critical-type-instability/39486/10 "2020-05-14T23:01:10Z")

</div>

Indeed, I copied and paste your function `*` and it works… I must have made a typo when I wrote it, sorry about that.

So it seems that your suggestion agrees with the edit on my last post; except that I used the `@generated` as suggested by @Henrique_Becker. Now, it seems like a superfluous thing to do as the multiple dispatch should work perfectly, right?

Thanks for your help 🙂
