Avoiding potential self-recursion should help (if allowed in your real use case):
julia> module Baz
export Bar, Foo
struct Bar{U}
u::U
end
struct Foo{B, T, ABC}
bar::B
xy::T
abc::ABC
end
macro _getproperty(x, s)
return :(
if $(esc(s)) === :x
return getfield($(esc(x)), :xy)[1]
elseif $(esc(s)) === :a
return getfield($(esc(x)), :abc)[1]
elseif $(esc(s)) === :b
return getfield($(esc(x)), :abc)[2]
else
return getfield($(esc(x)), $(esc(s)))
end
)
end
# `bar`-specific (and type unstable) `getproperty` code
function Base.getproperty(x::Foo{<:Bar}, s::Symbol)
if s === :u
return length(getfield(getfield(x, :bar), :u))
else
@_getproperty(x, s)
end
end
end # module
Main.Baz
julia> using .Baz
julia> bar = Bar(1)
Bar{Int64}(1)
julia> x = Foo(bar, (1., 2), "ab")
Foo{Bar{Int64}, Tuple{Float64, Int64}, String}(Bar{Int64}(1), (1.0, 2), "ab")
julia> using Test
julia> function f(x::Foo)
x.x, x.xy, x.a, x.u
end
f (generic function with 1 method)
julia> @inferred f(x)
(1.0, (1.0, 2), 'a', 1)
julia> g(x) = x.u
g (generic function with 1 method)
julia> @code_warntype g(x)
MethodInstance for g(::Foo{Bar{Int64}, Tuple{Float64, Int64}, String})
from g(x) in Main at REPL[8]:1
Arguments
#self#::Core.Const(g)
x::Foo{Bar{Int64}, Tuple{Float64, Int64}, String}
Body::Int64
1 ─ %1 = Base.getproperty(x, :u)::Core.Const(1)
└── return %1
Note that I used return length(getfield(getfield(x, :bar), :u)) instead of return length(getfield(x.bar, :u)).