Base.getfield() gives warntype?

I have a quite complicated parametric type that contains a simple Int64 field like:

struct A{T, U, V, W, X, Y}
    n::Int64
    ... (a lot of other possibly parametric-typed fields)
end

a = A(3, .....)

when I try to do @code_warntype a.n, I got a warntype like:

Body::Any
1 ─ %1 = Base.getfield(x, f)::Any
└──      return %1

I got this situation in v1.1.1 many times. Going to v1.3.0 seems better, but now it comes again. Is it a bug? Thanks.

Please include a self-contained MWE.

1 Like

No, it is just because @code_warntype does not do constant propagation for your input.

You’ll see that if you wrap the access in a function (which it would be as long as you’re not operating at global scope), then the :x is correctly constant-propagated and everything is fine:

julia> struct Foo
         x::Int
         y::String
       end

julia> function get_x(f)
         f.x
       end
get_x (generic function with 1 method)

julia> f = Foo(1, "hello")
Foo(1, "hello")

julia> @code_warntype get_x(f)
Variables
  #self#::Core.Compiler.Const(get_x, false)
  f::Foo

Body::Int64
1 ─ %1 = Base.getproperty(f, :x)::Int64
└──      return %1
1 Like

I want to include a MWE… but the case is just too complicated (needs a lot of custom types)… I could not reproduce the case in a simpler example…

In essence, the situation is like:

struct A
	npar::Int64
end

const a = A(3)

function f(x::A)
	npar = x.npar
	function g()
		y = 0
		for i in 1:npar
			y += i
		end
		return y
	end
	@code_warntype g()
end

julia> f(a)
Variables
  #self#::var"#g#3"{Int64}
  v::Array{Int64,1}
  @_3::Union{Nothing, Tuple{Int64,Int64}}
  t::Int64

Body::Array{Int64,1}
1 ─ %1  = Core.apply_type(Main.Vector, Main.Int64)::Core.Compiler.Const(Array{Int64,1}, false)
│   %2  = Core.getfield(#self#, :npar)::Int64
...

in this case, everything is fine (that’s why I said I failed to reproduce the problem in a simpler case).

However, in the actual situation, I got the following:

Variables
 ...
  npar::Union{}

Body::Array{Float64,1}
1 ──       Core.NewvarNode(:(threadsfor_fun))
│          Core.NewvarNode(:(grads))
...
│    %18 = Core.getfield(#self#, :npar)::Core.Box
...
8 ┄─ %24 = Core.getfield(%18, :contents)::Any
...

now, Core.getfield(#self#, :npar) has type Core.Box rather than Int64.
Another noticable difference is that npar is a variable (with type Union) in the actual case, but it’s not a variable in the simpler case.

any idea why it happens? thanks.

Probably performance of captured variables in closures · Issue #15276 · JuliaLang/julia · GitHub