Types truncated in v0.6.2 but not in v0.7?

I’m playing around with using type specialization to turn interpreters into compilers. I ran into this roadblock with the types being truncated when they get too deep, even if they are actual concrete types present in the input rather than inferred outputs.

struct Foo{T}
  t::T
end

function f(n)
  if n == 0
    0
  else
    Foo(f(n-1))
  end
end

function t(foo)
  foo.t
end

@code_warntype t(f(20))
Variables:
  #self# <optimized out>
  foo::Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Int64}}}}}}}}}}}}}}}}}}}}

Body:
  begin
      return (Core.getfield)(foo::Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Int64}}}}}}}}}}}}}}}}}}}}, :t)::Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{_}}}}}}}}} where _<:Foo
  end::Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{_}}}}}}}}} where _<:Foo

I can’t reproduce this in v0.7, but it’s sometimes fiddly to trigger in v0.6.2 too. So does anyone know if something fundamental changed or if I’ll still have problems trying to do this?

You’re just seeing inference give up at some point. The correct type is returned, but Julia’s type-inference just can’t prove what that type will be at compile-time.

julia> typeof(f(20))
Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Foo{Int64}}}}}}}}}}}}}}}}}}}}

The fact that this gets inferred at all is actually pretty impressive, since you’re doing something fundamentally type-unstable. The type of the output of the function f() depends on the value of n, which is the definition of type instability. Maybe if you explain what you’re trying to accomplish it will be easier to suggest a solution.

1 Like

The fact that this gets inferred at all is actually pretty impressive, since you’re doing something fundamentally type-unstable.

It’s not inferring the type of f(20), it’s just running that and then calling code_warntype(t, Foo{Foo{Foo{...}}}). And t should absolutely be type-stable:

struct Foo{T}
  t::T
end

function t(foo)
  foo.t
end

But the return type of getfield gets truncated, by a heuristic that is presumably trying to curtail runaway type inference.

Luckily, the heuristic in v0.7 seems to be smarter, but it’s also possible that I just need some more complicated examples to trigger it. I’m asking if anyone knows what changed so I can figure out what I can safely do.

Ah, I see. My mistake.