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
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:
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.