# Are "inline assignments" inside loop predicates bad for type inference?

**URL:** https://discourse.julialang.org/t/are-inline-assignments-inside-loop-predicates-bad-for-type-inference/81855
**Category:** Internals & Design
**Tags:** type, code\_warntype, type-stability, jet, nothing
**Created:** [May 29, 2022, 5:33am UTC](https://discourse.julialang.org/t/are-inline-assignments-inside-loop-predicates-bad-for-type-inference/81855 "2022-05-29T05:33:14Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![Krastanov](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/krastanov/32/6817_2.png) [@Krastanov](https://discourse.julialang.org/u/Krastanov)
#### Post date: [May 29, 2022, 5:33am UTC](https://discourse.julialang.org/t/are-inline-assignments-inside-loop-predicates-bad-for-type-inference/81855/1 "2022-05-29T05:33:14Z")

</div>

I was playing with JET and noticed that it does not like notation like `while (i=f())!==nothing ...` while it is perfectly happy with `i=f(); while i!==nothing ...`. `f` is a function that returns `Union{Int,Nothing}`. It seems the “inline assignment” inside of the `while` predicate is the reason for this and I was hoping I can get more feedback concerning the root cause. Is this a bad way to write julia code? Here is a full example with two naive versions of `Base.invperm`:

```julia
using JET
using Random

function inline_assign_while_loop(permutation::Vector{Int})
    indices = Int[]
    v = 1
    while (i=findfirst(==(v),permutation)) !== nothing
        push!(indices, i)
        v += 1
    end
    indices
end

function normal_assign_while_loop(permutation::Vector{Int})
    indices = Int[]
    v = 1
    i = findfirst(==(v),permutation)
    while i !== nothing
        push!(indices, i)
        v += 1
        i = findfirst(==(v),permutation)
    end
    indices
end

test_perm = randperm(10)
@assert inline_assign_while_loop(test_perm) == normal_assign_while_loop(test_perm) == invperm(test_perm)

# A possible error reported by JET because i!==nothing is not inferred
@report_call inline_assign_while_loop(test_perm) 

# No possible error reported by JET
@report_call normal_assign_while_loop(test_perm)

```

@code\_warntype is also happier with `normal_assign_while_loop`, so I assume the issue is not with JET, rather with the type inference.

**My question is: Is this something that Julia’s type inference should be doing better or is there something that I did explicitly wrong (or both)?**

I prefer using the problematic inline assignment inside the `while` predicate because that way I need to write it only once (instead of writing it once before the loop and once inside the loop body). I know this is an inefficient quadratic algorithm for `invperm`, I am using it just as an example.

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [May 29, 2022, 7:18am UTC](https://discourse.julialang.org/t/are-inline-assignments-inside-loop-predicates-bad-for-type-inference/81855/2 "2022-05-29T07:18:38Z")

</div>

A very small modification makes the error go away for me (on a somewhat recent master - which version did you test?):

```diff
function inline_assign_while_loop(permutation::Vector{Int})
    indices = Int[]
    v = 1
- while (i=findfirst(==(v),permutation)) !== nothing
+ while ((i=findfirst(==(v),permutation)); i !== nothing)
        push!(indices, i)
        v += 1
    end
    indices
end

```

If we take a look at the lowered representation, the difference is extremely subtle:

```julia
julia> @code_lowered inline_assign_while_loop(test_perm)
CodeInfo(
1 ─ indices = Base.getindex(Main.Int)
└── v = 1
2 ┄ %3 = (==)(v)
│ %4 = Main.findfirst(%3, permutation)
│ i = %4
│ %6 = %4 !== Main.nothing
└── goto #4 if not %6
3 ─ Main.push!(indices, i)
│ v = v + 1
└── goto #2
4 ─ return indices
)

julia> @code_lowered inline_assign_while_loop_extra(test_perm)
CodeInfo(
1 ─ indices = Base.getindex(Main.Int)
└── v = 1
2 ┄ %3 = (==)(v)
│ i = Main.findfirst(%3, permutation)
│ %5 = i !== Main.nothing
└── goto #4 if not %5
3 ─ Main.push!(indices, i)
│ v = v + 1
└── goto #2
4 ─ return indices

```

The reason for the warning by JET seems to be that the SSA slot `%4` is checked against `nothing`, not `i`. So the issue does not lie in inference per se, but in propagating the information we have about `%4` to `i` as well. This seems very interesting, so I’ll open an issue.

EDIT: Issue opened [here](https://github.com/JuliaLang/julia/issues/45499).

---

<div class="post-metadata">

### Author: ![Krastanov](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/krastanov/32/6817_2.png) [@Krastanov](https://discourse.julialang.org/u/Krastanov)
#### Post date: [May 29, 2022, 4:24pm UTC](https://discourse.julialang.org/t/are-inline-assignments-inside-loop-predicates-bad-for-type-inference/81855/3 "2022-05-29T16:24:19Z")

</div>

I was also using “fairly recent master” `1.9.0-DEV.487 (2022-05-08)`. Thank you, this was really clear and interesting explanation. I am surprised that `i` and slot `%4` are two different entities… I thought the whole point of SSA was to have a canonical notation in which they are the same entity (but my understanding is superficial and vague).

---

<div class="post-metadata">

### Author: ![Krastanov](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/krastanov/32/6817_2.png) [@Krastanov](https://discourse.julialang.org/u/Krastanov)
#### Post date: [June 2, 2022, 12:42am UTC](https://discourse.julialang.org/t/are-inline-assignments-inside-loop-predicates-bad-for-type-inference/81855/4 "2022-06-02T00:42:43Z")

</div>

That was a ridiculously quick turnaround! Thank you, it was very impressive! Fixed now by @aviatesk in [inference: improve `ssa_def_slot` tracking by aviatesk · Pull Request #45524 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/45524)

---

<div class="post-metadata">

### Author: ![aviatesk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aviatesk/32/7610_2.png) [@aviatesk](https://discourse.julialang.org/u/aviatesk)
#### Post date: [June 2, 2022, 12:55am UTC](https://discourse.julialang.org/t/are-inline-assignments-inside-loop-predicates-bad-for-type-inference/81855/5 "2022-06-02T00:55:36Z")

</div>

Thx for pointing that out, that kind of issue is very helpful to improve our compiler. Also happy to know you’re using JET🥰
