The answer is similar to Non-allocating loop over a set of structs - #6 by kristoffer.carlsson.
A loop is a chunk of code that is repeated. Here, letters
will have a different type in each iteration so the code for the loop body that is compiled need to be general enough to handle this.
And like in the other answer, you can use Unrolled.jl to unroll the loop and thereby avoiding the constraint of a loop.
julia> @unroll function getSumX(letters::Tuple{Vararg{ABCs}})
sum = 0.0
@unroll for i = 1:length(letters)
sum += letters[i].x
end
return sum
end;
julia> @code_warntype getSumX((a1, a2, b))
Body::Float64
1 ─ %1 = π (0.0, Core.Compiler.Const(0.0, false))
│ %2 = π (1, Core.Compiler.Const(1, false))
│ %3 = (Base.getfield)(letters, %2, true)::A
│ %4 = (Base.getfield)(%3, :x)::Float64
│ %5 = (Base.add_float)(%1, %4)::Float64
│ %6 = π (2, Core.Compiler.Const(2, false))
│ %7 = (Base.getfield)(letters, %6, true)::A
│ %8 = (Base.getfield)(%7, :x)::Float64
│ %9 = (Base.add_float)(%5, %8)::Float64
│ %10 = π (3, Core.Compiler.Const(3, false))
│ %11 = (Base.getfield)(letters, %10, true)::B
│ %12 = (Base.getfield)(%11, :x)::Float64
│ %13 = (Base.add_float)(%9, %12)::Float64
└── return %13