# Possible bug in type inference? (4 line MWE)

**URL:** <https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353>\
**Category:** General Usage\
**Tags:** inference, type\
**Created:** [September 10, 2020, 1:19am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353 "2020-09-10T01:19:09Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)\
**Post date:** [September 10, 2020, 1:19am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/1 "2020-09-10T01:19:10Z")

</div>

Hello!

I came across a weird type inference failure when using generators. Here is a MWE.

```julia
function example()
    col = 2
    ss = (col for _ in 1:5)
    col += 3
    return col, ss
end

function example2()
    col = 2
    ss = (col for _ in 1:5)
    return col, ss
end

@code_warntype example()
@code_warntype example2()

```

`example` has type instability:

```julia
Variables
  #self#::Core.Compiler.Const(example, false)
  #611::var"#611#612"
  ss::Base.Generator{UnitRange{Int64},var"#611#612"}
  col@_4::Core.Box
  col@_5::Union{}
  col@_6::Union{}

Body::Tuple{Any,Base.Generator{UnitRange{Int64},var"#611#612"}}
1 ─ (col@_4 = Core.Box())
│ Core.setfield!(col@_4, :contents, 2)
│ (#611 = %new(Main.:(var"#611#612"), col@_4))
│ %4 = #611::var"#611#612"
│ %5 = (1:5)::Core.Compiler.Const(1:5, false)
│ (ss = Base.Generator(%4, %5))
│ %7 = Core.isdefined(col@_4, :contents)::Bool
└── goto #3 if not %7
2 ─ goto #4
3 ─ Core.NewvarNode(:(col@_5))
└── col@_5
4 ┄ %12 = Core.getfield(col@_4, :contents)::Any
│ %13 = (%12 + 3)::Any
│ Core.setfield!(col@_4, :contents, %13)
│ %15 = Core.isdefined(col@_4, :contents)::Bool
└── goto #6 if not %15
5 ─ goto #7
6 ─ Core.NewvarNode(:(col@_6))
└── col@_6
7 ┄ %20 = Core.getfield(col@_4, :contents)::Any
│ %21 = Core.tuple(%20, ss::Core.Compiler.PartialStruct(Base.Generator{UnitRange{Int64},var"#611#612"}, Any[var"#611#612", Core.Compiler.Const(1:5, false)]))::Core.Compiler.PartialStruct(Tuple{Any,Base.Generator{UnitRange{Int64},var"#611#612"}}, Any[Any, Core.Compiler.PartialStruct(Base.Generator{UnitRange{Int64},var"#611#612"}, Any[var"#611#612", Core.Compiler.Const(1:5, false)])])
└── return %21

```

However, `example2` does not:

```julia
Variables
  #self#::Core.Compiler.Const(example2, false)
  #613::var"#613#614"{Int64}
  col::Int64
  ss::Base.Generator{UnitRange{Int64},var"#613#614"{Int64}}

Body::Tuple{Int64,Base.Generator{UnitRange{Int64},var"#613#614"{Int64}}}
1 ─ (col = 2)
│ %2 = Main.:(var"#613#614")::Core.Compiler.Const(var"#613#614", false)
│ %3 = Core.typeof(col::Core.Compiler.Const(2, false))::Core.Compiler.Const(Int64, false)
│ %4 = Core.apply_type(%2, %3)::Core.Compiler.Const(var"#613#614"{Int64}, false)
│ (#613 = %new(%4, col::Core.Compiler.Const(2, false)))
│ %6 = #613::Core.Compiler.Const(var"#613#614"{Int64}(2), false)::Core.Compiler.Const(var"#613#614"{Int64}(2), false)
│ %7 = (1:5)::Core.Compiler.Const(1:5, false)
│ (ss = Base.Generator(%6, %7))
│ %9 = Core.tuple(col::Core.Compiler.Const(2, false), ss::Core.Compiler.Const(Base.Generator{UnitRange{Int64},var"#613#614"{Int64}}(var"#613#614"{Int64}(2), 1:5), false))::Core.Compiler.Const((2, Base.Generator{UnitRange{Int64},var"#613#614"{Int64}}(var"#613#614"{Int64}(2), 1:5)), false)
└── return %9

```

I cannot understand this in terms of usual type instability rules. Could this be a julia type inference bug?

---

<div class="post-metadata">

**Author:** ![lucas711642](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lucas711642/32/12051_2.png) [@lucas711642](https://discourse.julialang.org/u/lucas711642)\
**Post date:** [September 10, 2020, 1:40am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/2 "2020-09-10T01:40:16Z")

</div>

This is not a bug. The problem is that `col` is not constant in `example` (but it is in `example2`), so it must be boxed because `col` must have the same value both in `ss` and in `example` (see the `col@_4::Core.Box` line in the output of `@code_warntype example()`).

You can solve this problem using a `let` block as described in the [documentation](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured):

```julia
function example()
    col = 2
    ss = let col = col
		(col for _ in 1:5)
	end
    col += 3
    return col, ss
end

```

---

<div class="post-metadata">

**Author:** ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)\
**Post date:** [September 10, 2020, 1:55am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/3 "2020-09-10T01:55:50Z")

</div>

Ah I see! I was misunderstanding what generators were supposed to. I thought

```julia
col = 2
a = (col for _ in 1:3)

```

will evaluate the value for col at the time of creation, such that it would be equivalent to writing `a = (2 for _ in 1:3)`. But I guess I should really think of `col` as a captured variable just like in the case of local functions. Is that correct?

---

<div class="post-metadata">

**Author:** ![lucas711642](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lucas711642/32/12051_2.png) [@lucas711642](https://discourse.julialang.org/u/lucas711642)\
**Post date:** [September 10, 2020, 2:00am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/4 "2020-09-10T02:00:11Z")

</div>

> [@tomohiro\_soejima](#):
>
> But I guess I should really think of `col` as a captured variable just like in the case of local functions. Is that correct?

Exactly. Indeed, a generator implicitly creates an anonymous function to apply to each element of an iterable.

---

<div class="post-metadata">

**Author:** ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)\
**Post date:** [September 10, 2020, 2:03am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/5 "2020-09-10T02:03:13Z")

</div>

Great, thanks!

Incidentally, is there any good reference for learning generators, closure, captured variables etc. in Julia? I come across these whenever I try to do something complicated, but I have not been able to find good documentation for their behavior, as they seem rather scattered in the documentation.

Many thanks!

---

<div class="post-metadata">

**Author:** ![lucas711642](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lucas711642/32/12051_2.png) [@lucas711642](https://discourse.julialang.org/u/lucas711642)\
**Post date:** [September 10, 2020, 2:13am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/6 "2020-09-10T02:13:13Z")

</div>

When you create a generator, it basically calls `Base.Generator`:

```julia
julia> Meta.@lower (fun(x) for x in y)
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─ %1 = Base.Generator(fun, y)
└── return %1
))))

```

So you can call `Base.Generator` yourself:

```julia
help?> Base.Generator
  Generator(f, iter)

  Given a function f and an iterator iter, construct an iterator that yields the values of f applied to the elements of iter. The syntax for constructing
  an instance of this type is f(x) for x in iter [if cond(x)::Bool]. The [if cond(x)::Bool] expression is optional and acts as a "guard", effectively
  filtering out values where the condition is false.

  julia> g = (abs2(x) for x in 1:5 if x != 3);

  julia> for x in g
             println(x)
         end
  1
  4
  16
  25

  julia> collect(g)
  4-element Array{Int64,1}:
    1
    4
   16
   25

```

So, in your original code, you may write something like (note the `let` block):

```julia
function example()
	col = 2
	f = let col = col
		_ -> col
	end
	ss = Base.Generator(f, 1:5)
	col += 3
	return col, ss
end

```

Edit: A good discussion on closures and their performance is the [documentation that I mentioned earlier](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured).

---

<div class="post-metadata">

**Author:** ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)\
**Post date:** [September 10, 2020, 2:19am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/7 "2020-09-10T02:19:05Z")

</div>

Great, thank you!

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [September 10, 2020, 2:30am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/8 "2020-09-10T02:30:52Z")

</div>

> [@lucas711642](#):
>
> This is not a bug

Well, it’s kind of a bug. In theory the compiler could do escape analysis and figure out that the capture dies before the modification happens and thus doesn’t need to be captured in the first place. It’s just that the analysis is hard.

---

<div class="post-metadata">

**Author:** ![lucas711642](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lucas711642/32/12051_2.png) [@lucas711642](https://discourse.julialang.org/u/lucas711642)\
**Post date:** [September 10, 2020, 3:12am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/9 "2020-09-10T03:12:26Z")

</div>

But in this case the function returns the generator. The captured variable changes between the creation of the generator and its use. Of course, a similar argument could be applied in this case: the generator does not use the original value of the captured variable, so the compiler could, in theory, figure out that it only needs the updated value of `col`.

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [September 10, 2020, 3:17am UTC](https://discourse.julialang.org/t/possible-bug-in-type-inference-4-line-mwe/46353/10 "2020-09-10T03:17:11Z")

</div>

Yes, I misread the example. You are absolutely right that the analysis is still possible but harder this way.
