# Strange type instability

**URL:** <https://discourse.julialang.org/t/strange-type-instability/107912>\
**Category:** General Usage\
**Created:** [December 21, 2023, 3:17pm UTC](https://discourse.julialang.org/t/strange-type-instability/107912 "2023-12-21T15:17:19Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![abraunst](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraunst/32/6880_2.png) [@abraunst](https://discourse.julialang.org/u/abraunst)\
**Post date:** [December 21, 2023, 3:17pm UTC](https://discourse.julialang.org/t/strange-type-instability/107912/1 "2023-12-21T15:17:19Z")

</div>

I’m encountering a strange type instability that I could distill to the following minimal code:

```julia
function unstable(rv,x0)
    x1=copy(x0)
    for t in 1:10
        p::Int = prod(x0[j] for j in rv; init=1)
        x1[1] = p == 1 ? 0 : 1
        x0,x1 = x1,x0
    end
    return x0
end

```

`@code_warntype` gives the following. The instability disappears if:

- If I set `p=1` instead of the `p=prod(... )`
- If I typeassert `x0` and `x1` in the assignment, i.e. `x0::Vector{Int},x1::Vector{Int} = x1,x0` instead of `x0,x1 = x1,x0`.

Any idea or explanation?

```julia
julia> @code_warntype unstable(fill(1,10),fill(1,10))
MethodInstance for unstable(::Vector{Int64}, ::Vector{Int64})
  from unstable(rv, x0) @ Main REPL[103]:1
Arguments
  #self#::Core.Const(unstable)
  rv::Vector{Int64}
  x0@_3::Vector{Int64}
Locals
  @_4::Union{Nothing, Tuple{Int64, Int64}}
  x1::Any
  #109::var"#109#110"
  t::Int64
  p::Int64
  x0@_9::Union{}
  x0@_10::Union{}
  x0@_11::Union{}
  x0@_12::Union{Core.Box, Vector{Int64}}
  @_13::Int64
Body::Any
1 ── (x0@_12 = x0@_3)
│ (x0@_12 = Core.Box(x0@_12::Vector{Int64}))
│ Core.NewvarNode(:(@_4))
│ Core.NewvarNode(:(x1))
│ %5 = Core.isdefined(x0@_12::Core.Box, :contents)::Bool
└─── goto #3 if not %5
2 ── goto #4
3 ── Core.NewvarNode(:(x0@_9))
└─── x0@_9
4 ┄─ %10 = Core.getfield(x0@_12::Core.Box, :contents)::Any
│ (x1 = Main.copy(%10))
│ %12 = (1:10)::Core.Const(1:10)
│ (@_4 = Base.iterate(%12))
│ %14 = (@_4::Core.Const((1, 1)) === nothing)::Core.Const(false)
│ %15 = Base.not_int(%14)::Core.Const(true)
└─── goto #13 if not %15
5 ┄─ %17 = @_4::Tuple{Int64, Int64}
│ (t = Core.getfield(%17, 1))
│ %19 = Core.getfield(%17, 2)::Int64
│ (#109 = %new(Main.:(var"#109#110"), x0@_12::Core.Box))
│ %21 = #109::var"#109#110"
│ %22 = Base.Generator(%21, rv)::Base.Generator{Vector{Int64}, var"#109#110"}
│ %23 = (:init,)::Core.Const((:init,))
│ %24 = Core.apply_type(Core.NamedTuple, %23)::Core.Const(NamedTuple{(:init,)})
│ %25 = Core.tuple(1)::Core.Const((1,))
│ %26 = (%24)(%25)::Core.Const((init = 1,))
│ %27 = Core.kwcall(%26, Main.prod, %22)::Any
│ %28 = Base.convert(Main.Int, %27)::Any
│ (p = Core.typeassert(%28, Main.Int))
│ %30 = (p == 1)::Bool
└─── goto #7 if not %30
6 ── (@_13 = 0)
└─── goto #8
7 ── (@_13 = 1)
8 ┄─ %35 = @_13::Int64
│ Base.setindex!(x1, %35, 1)
│ %37 = x1::Any
│ %38 = Core.isdefined(x0@_12::Core.Box, :contents)::Bool
└─── goto #10 if not %38
9 ── goto #11
10 ─ Core.NewvarNode(:(x0@_10))
└─── x0@_10
11 ┄ %43 = Core.getfield(x0@_12::Core.Box, :contents)::Any
│ Core.setfield!(x0@_12::Core.Box, :contents, %37)
│ (x1 = %43)
│ (@_4 = Base.iterate(%12, %19))
│ %47 = (@_4 === nothing)::Bool
│ %48 = Base.not_int(%47)::Bool
└─── goto #13 if not %48
12 ─ goto #5
13 ┄ %51 = Core.isdefined(x0@_12::Core.Box, :contents)::Bool
└─── goto #15 if not %51
14 ─ goto #16
15 ─ Core.NewvarNode(:(x0@_11))
└─── x0@_11
16 ┄ %56 = Core.getfield(x0@_12::Core.Box, :contents)::Any
└─── return %56

```

---

<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:** [December 21, 2023, 3:56pm UTC](https://discourse.julialang.org/t/strange-type-instability/107912/2 "2023-12-21T15:56:12Z")

</div>

That’s the infamous Julia#15276. `x0[j] for j in rv` is a closure that captures `x0`, but `x0` has complicated lifetime and gets re-assigned. You can fix it (and remove the `::Int` assertion) with:

```julia
p = let x0=x0; prod(x0[j] for j in rv; init=1); end

```

> **full code**
>
> ```Julia-repl
> julia> function stable(rv,x0)
> x1=copy(x0)
> for t in 1:10
> p = let x0=x0; prod(x0[j] for j in rv; init=1); end
> x1[1] = p == 1 ? 0 : 1
> x0,x1 = x1,x0
> end
> return x0
> end
> stable (generic function with 1 method)
> 
> julia> @code_warntype stable(fill(1,10),fill(1,10))
> MethodInstance for stable(::Vector{Int64}, ::Vector{Int64})
> from stable(rv, x0) @ Main REPL[21]:1
> Arguments
> #self#::Core.Const(stable)
> rv::Vector{Int64}
> x0@_3::Vector{Int64}
> Locals
> @_4::Union{Nothing, Tuple{Int64, Int64}}
> x1::Vector{Int64}
> t::Int64
> p::Int64
> #5::var"#5#6"{Vector{Int64}}
> x0@_9::Vector{Int64}
> x0@_10::Vector{Int64}
> @_11::Int64
> Body::Vector{Int64}
> 1 ─ (x0@_10 = x0@_3)
> │ (x1 = Main.copy(x0@_10))
> │ %3 = (1:10)::Core.Const(1:10)
> │ (@_4 = Base.iterate(%3))
> │ %5 = (@_4::Core.Const((1, 1)) === nothing)::Core.Const(false)
> │ %6 = Base.not_int(%5)::Core.Const(true)
> └── goto #7 if not %6
> 2 ┄ %8 = @_4::Tuple{Int64, Int64}
> │ (t = Core.getfield(%8, 1))
> │ %10 = Core.getfield(%8, 2)::Int64
> │ %11 = x0@_10::Vector{Int64}
> │ (x0@_9 = %11)
> │ %13 = Main.:(var"#5#6")::Core.Const(var"#5#6")
> │ %14 = Core.typeof(x0@_9)::Core.Const(Vector{Int64})
> │ %15 = Core.apply_type(%13, %14)::Core.Const(var"#5#6"{Vector{Int64}})
> │ (#5 = %new(%15, x0@_9))
> │ %17 = #5::var"#5#6"{Vector{Int64}}
> │ %18 = Base.Generator(%17, rv)::Base.Generator{Vector{Int64}, var"#5#6"{Vector{Int64}}}
> │ %19 = (:init,)::Core.Const((:init,))
> │ %20 = Core.apply_type(Core.NamedTuple, %19)::Core.Const(NamedTuple{(:init,)})
> │ %21 = Core.tuple(1)::Core.Const((1,))
> │ %22 = (%20)(%21)::Core.Const((init = 1,))
> │ (p = Core.kwcall(%22, Main.prod, %18))
> │ %24 = (p == 1)::Bool
> └── goto #4 if not %24
> 3 ─ (@_11 = 0)
> └── goto #5
> 4 ─ (@_11 = 1)
> 5 ┄ %29 = @_11::Int64
> │ Base.setindex!(x1, %29, 1)
> │ %31 = x1::Vector{Int64}
> │ %32 = x0@_10::Vector{Int64}
> │ (x0@_10 = %31)
> │ (x1 = %32)
> │ (@_4 = Base.iterate(%3, %10))
> │ %36 = (@_4 === nothing)::Bool
> │ %37 = Base.not_int(%36)::Bool
> └── goto #7 if not %37
> 6 ─ goto #2
> 7 ┄ return x0@_10
> 
> ```

---

<div class="post-metadata">

**Author:** ![abraunst](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraunst/32/6880_2.png) [@abraunst](https://discourse.julialang.org/u/abraunst)\
**Post date:** [December 22, 2023, 7:11am UTC](https://discourse.julialang.org/t/strange-type-instability/107912/3 "2023-12-22T07:11:09Z")

</div>

I see… Thanks a lot!
