Are composite types type-unstable?

I define a composite type with fields of different types:

struct MyType
    i::Int
    f::Float64
end

And then:

julia> a = MyType(1,1.0)
MyType(1, 1.0)

julia> @code_warntype a.i
Body::Union{Float64, Int64}
18 1 ─ %1 = (Base.getfield)(x, f)::Union{Float64, Int64}                    │
   └──      return %1

Does this mean that any code querying any of the fields of a MyField instance would be type-unstable?

And if that’s the case, is there a way to avoid it?

No, composite types are perfectly fine with type inference. What you’re seeing here is misleading because of the way @code_warntype interprets expressions.

The macro is showing you the code for getproperty(::MyType, ::Symbol) for unknown symbols:

julia> name = :i

julia> @code_warntype getproperty(a, name)
Body::Union{Float64, Int64}
1 ─ %1 = (Base.getfield)(x, f)::Union{Float64, Int64}
└──      return %1

Instead, you want the code for a function which uses getproperty with a particular symbol which may be const propagated into getfield:

julia> foo(a) = a.i
foo (generic function with 1 method)

julia> @code_warntype foo(a)
Body::Int64
1 ─ %1 = (Base.getfield)(a, :i)::Int64
└──      return %1
4 Likes

Sometimes I find it easier to use the regular (non-macro) versions of the code_* methods:

julia> code_warntype(x -> x.i, (MyType,))
Body::Int64
1 ─ %1 = (Base.getfield)(x, :i)::Int64
└──      return %1
4 Likes

You can also put it through an anonymous function, and it’s the same as first creating a function like foo.

julia> @code_warntype (x->x.i)(a)
Body::Int64
1 ─ %1 = (Base.getfield)(x, :i)::Int64
└──      return %1
2 Likes