# Check type stability of a struct

We usually speak of type stability for functions and we also have the tools to determine that (like `@code_warntype`).
But say I would like to check whether a struct is type-stable, in the sense that it is well defined and no field is of an abstract type.
For the small structs it is trivial, but for long composite nested structs it is quite hard to inspect it manually.
Is there a tool to handle this automatically ?

example:

``````struct Alpha{T,R}
x::T
y::R
end

a1 = Alpha{Real, Int}(1,2)
a2 = Alpha{Int,Int}(1,2)

# desired hypothetical function
istypestable(a1) #returns false
istypestable(a2) #returns true
``````
1 Like

Not sure what exactly mean with fields being â€śwell definedâ€ť (they always are), but this should do what youâ€™re looking for:

``````julia> allconcrete(x::T) where T = all(isconcretetype, fieldtypes(T))
allconcrete (generic function with 1 method)

julia> allconcrete(a1)
false

julia> allconcrete(a2)
true
``````

Note that this works through reflection via `fieldtypes`.

â€śtype stableâ€ť as a term only makes sense in the context of a function using an object, since type stability only refers to some function whose return type depends on the value of its arguments instead of its types. Itâ€™s true that abstractly typed fields can lead to type instabilities in functions using that type, but Iâ€™m not sure we should refer to structs with non-concretely typed fields as â€śtype unstableâ€ť. There are valid reasons for having a field typed as e.g. `Any` - for example when the field can take on a diverse number of types and specializing on it would not help (or even hinder) performance.

4 Likes

It better be recursive, like `allconcrete(::Type{T}) where T = isconcretetype(T) && all(allconcrete, fieldtypes(T))`.

yes, there are cases like

``````cc = Alpha{Int, Alpha{Int, Real}}(1, Alpha{Int,Real}(2,3))
``````

where only the recursive function will give the correct result.

However both fail to detect

``````julia> abv = Alpha{Vector{Real}, Int}(Vector{Real}([1,2,3]), 3)
Alpha{Vector{Real}, Int64}(Real[1, 2, 3], 3
``````

because `Vector{Real} |> fieldtypes` is empty.

Youâ€™re not going to get around specializing the code in general, since type parameters are not necessarily linked to the field types.

I understand that there can be the case that a parameter type might not be linked to a specific field, and thus there will be no negative impact in performance for abstract types.
e.g.:

``````struct Beta{T} end;
b = Beta{Number}()

#desired behavior
istypestable(b) # returns true
``````

In these cases, the parameter is mostly used for specialization.

Maybe itâ€™s useful to translate the problem in the function domain.
E.g. I could write something like this, in order to recursively evaluate all fields from the struct:

``````@inline function teststab(a::R) where R
if isprimitivetype(R)
return a
else
for x in getfield.([a], fieldnames(R))
testalpha(x)
end
end
end
``````

upon which I can call `code_warntype`

Unfortunately this version doesnâ€™t really work.
E.g. the following should be red-free since itâ€™s type stable

The problem is that the compiler inside the `for` loop doesnâ€™t specialize on the type of each field `x` although it should be statically retrievable.
Any ideas how to proceed ?