Vector comprehensions hamper type inference?

I’m new to Julia, so I post now with uncertainty. I’m finding that vector comprehensions hamper the compiler’s type inference–both about 1) the type of the result of the comprehension and 2) about the type of arguments passed to functions inside the comprehension, even when those arguments are obviously not changed.

Here is a simple example of the latter.

function f1()
  if true
    a = fill(1.,1,1)
  else
    a = fill(2.,1,1)

    # following three are equivalent
    # but in last version compiler assigns Any type to return value "a"
    # b = hcat(f2(a)) 
    # b = map(a->f2(a), 1:1)
    b = [f2(a) for _ ∈ 1:1]
  end
 return a
end

f2(a) = 3.

@code_warntype f1()

This code includes three lines in a row that assign b, two of which are commented out, all of which I think are equivalent in this case. However in Julia 1.6.2, when I run @codewarntype f1() with the third line uncommented, the output concludes with:

%20 = Core.getfield(a@_4, :contents)::Any
└──       return %20

with the Any in red. This does not happen with the other two versions. It seems as if the compiler is afraid that f2(a) inside the vector comprehension could change the type of a, as a result of which the final value of a in f1() is of indeterminate type.

I’d appreciate guidance on whether this is something to be concerned about, whether it reflects a limitation in the current compiler, whether there’s a broad intuition for why comprehensions are best avoided, etc.

1 Like

The equivalent map would be map(_->f2(a), 1:1), and like the comprehension this doesn’t infer. I think it’s another case of the famous closure bug #15276. It can be avoided here by making a let a = a block around the map.