# Getproperty/getfield type instability?

**URL:** <https://discourse.julialang.org/t/getproperty-getfield-type-instability/89095>\
**Category:** New to Julia\
**Created:** [October 22, 2022, 1:41am UTC](https://discourse.julialang.org/t/getproperty-getfield-type-instability/89095 "2022-10-22T01:41:01Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [October 22, 2022, 1:41am UTC](https://discourse.julialang.org/t/getproperty-getfield-type-instability/89095/1 "2022-10-22T01:41:02Z")

</div>

I’m trying to understand type stability when accessing composite types.

Consider the custom datatype:

```julia
struct MyStruct
    a::Int
    b::Number
    c::AbstractDict
end

```

I create an instance,

```julia
obj = MyStruct(1, 2.0, Dict())

```

Running `@code_warntype` on `obj.a` shows that `getproperty` is not type stable:

```julia
julia> @code_warntype obj.a
MethodInstance for getproperty(::MyStruct, ::Symbol)
  from getproperty(x, f::Symbol) in Base at Base.jl:38
Arguments
  #self#::Core.Const(getproperty)
  x::MyStruct
  f::Symbol
Body::Any
1 ─ nothing
│ %2 = Base.getfield(x, f)::Any
└── return %2

```

of course, `::Any` is highlighted in red in the REPL.

Next, if I try `@code_warntype` on `getfield`, I get this:

```julia
julia> @code_warntype getfield(obj, :a)
getfield(...) in Core
  failed to infer

```

When passing such objects as arguments to functions which access their properties, obviously the type instability is resolved somehow when the function attempts to access `obj.a`. For example,

```julia
julia> function g(x) x.a+1 end

julia> @code_warntype g(obj)
MethodInstance for g(::MyStruct)
  from g(x) in Main at REPL[9]:1
Arguments
  #self#::Core.Const(g)
  x::MyStruct
Body::Int64
1 ─ %1 = Base.getproperty(x, :a)::Int64
│ %2 = (%1 + 1)::Int64
└── return %2

```

What’s happening here? I’ve tried to look for applicable definitions of `getproperty` and `getfield` to see how the type instability is resolved, but so far I couldn’t seem to find them.

Thanks~

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 22, 2022, 1:50am UTC](https://discourse.julialang.org/t/getproperty-getfield-type-instability/89095/2 "2022-10-22T01:50:22Z")

</div>

Observe:

```julia
julia> struct MyStruct
           a::Int
       end

julia> obj = MyStruct(1)
MyStruct(1)

julia> @code_warntype getproperty(obj, :a)
MethodInstance for getproperty(::MyStruct, ::Symbol)
  from getproperty(x, f::Symbol) in Base at Base.jl:38
Arguments
  #self#::Core.Const(getproperty)
  x::MyStruct
  f::Symbol
Body::Int64
1 ─ nothing
│ %2 = Base.getfield(x, f)::Int64
└── return %2

julia> struct MyStruct2
           a::Int
           b::Float64
       end

julia> obj2 = MyStruct2(1, 2)
MyStruct2(1, 2.0)

julia> @code_warntype getproperty(obj2, :a)
MethodInstance for getproperty(::MyStruct2, ::Symbol)
  from getproperty(x, f::Symbol) in Base at Base.jl:38
Arguments
  #self#::Core.Const(getproperty)
  x::MyStruct2
  f::Symbol
Body::Union{Float64, Int64}
1 ─ nothing
│ %2 = Base.getfield(x, f)::Union{Float64, Int64}
└── return %2

```

basically, `getproperty` can only see two types, one is the `MyStruct`, the other is merely a `Symbol`, nominally, there’s no way for `getproperty` to know which field you will access purely based on the Symbol being Symbol – it could be any field.

However, I think in this case the output of `code_warntype` may be misleading, it should have constant propagation (just like what you see when you put this into a function), but for some reason the result is not showing the effect of constant propagation

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [October 22, 2022, 7:39am UTC](https://discourse.julialang.org/t/getproperty-getfield-type-instability/89095/3 "2022-10-22T07:39:13Z")

</div>

> [@jling](#):
>
> However, I think in this case the output of `code_warntype` may be misleading, it should have constant propagation (just like what you see when you put this into a function), but for some reason the result is not showing the effect of constant propagation

It’s not showing the result of constant propagation because that’s not how `@code_warntype` works - it can’t propagate constants from the top level. This:

> [@uniment](#):
>
> `julia> @code_warntype obj.a`

is equivalent to

```julia
@code_warntype getproperty(obj, :a)

```

which is the same as

```julia
InteractiveUtils.code_warntype(getproperty, (Base.typesof)(obj, :a))

```

which ultimately is

```julia
InteractiveUtils.code_warntype(getproperty, Tuple{MyStruct, Symbol})

```

So there’s no `Symbol` object to propagate because `code_warntype` only cares about the input types, not their values. Not even `obj` survives. For inspecting what syntax transforms macros perform, `@macroexpand` is very useful.

If you use `obj.a` in a function, the `:a` is of course again a constant inside a call, which is propagated. Just as can be observed in the `g(x)` example.

> [@uniment](#):
>
> What’s happening here? I’ve tried to look for applicable definitions of `getproperty` and `getfield` to see how the type instability is resolved, but so far I couldn’t seem to find them.

Both `getproperty` and `getfield` rely on constant propagation of the input symbol to be type stable. Using them in a function, where the accessed fields are hardcoded, allows them to be propagated as constants and thus resolve the field access in a type stable manner.

Further, only `getproperty` is configurable. By default it just forwards to `getfield`, which is the builtin version that’s typically used when overloading `getproperty` for a custom type.

As to why `@code_warntype` works this way - it’s intended for figuring out whether or not the function that’s called is type stable, not whether the `Expr` that it’s used on is.

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [October 23, 2022, 9:08pm UTC](https://discourse.julialang.org/t/getproperty-getfield-type-instability/89095/4 "2022-10-23T21:08:50Z")

</div>

To add to the previous comments: It is also good practice to make all fields of your struct concrete instead of abstract types. So instead of

```julia
struct MyStruct
    a::Int
    b::Number
    c::AbstractDict
end

```

you might want to use

```julia
struct MyStruct{NT<:Number,DT<:AbstractDict}
    a::Int
    b::NT
    c::DT
end

```

This ensures that when the compiler encounters a function `f(x::MyStruct)` that accesses the fields of `x` it compile a nice and fast method, depending on what exactly `NT` and `DT` are.
