One problem I wanted to point at is:
julia> sumN(n) = Union{[ Tuple{[ifelse(i & (1<<j) != 0, Int8, UInt8 ) for j=0:n-1]...} for i = 0:(2^n-1)]...}
sumN (generic function with 1 method)
julia> prodN(n) = NTuple{n, Union{Int8, UInt8}}
prodN (generic function with 1 method)
julia> A0=Vector{sumN(3)}(undef, 1)
1-element Vector{Union{Tuple{Int8, Int8, Int8}, Tuple{Int8, Int8, UInt8}, Tuple{Int8, UInt8, Int8}, Tuple{Int8, UInt8, UInt8}, Tuple{UInt8, Int8, Int8}, Tuple{UInt8, Int8, UInt8}, Tuple{UInt8, UInt8, Int8}, Tuple{UInt8, UInt8, UInt8}}}:
(0, 0, 0)
julia> A1=Vector{prodN(3)}(undef, 1)
1-element Vector{Union{Tuple{Int8, Int8, Int8}, Tuple{Int8, Int8, UInt8}, Tuple{Int8, UInt8, Int8}, Tuple{Int8, UInt8, UInt8}, Tuple{UInt8, Int8, Int8}, Tuple{UInt8, Int8, UInt8}, Tuple{UInt8, UInt8, Int8}, Tuple{UInt8, UInt8, UInt8}}}:
(0, 0, 0)
vs
julia> sumN(n) = Union{[ Tuple{[ifelse(i & (1<<j) != 0, Int8, UInt8 ) for j=0:n-1]...} for i = 0:(2^n-1)]...}
sumN (generic function with 1 method)
julia> prodN(n) = NTuple{n, Union{Int8, UInt8}}
prodN (generic function with 1 method)
julia> A1=Vector{prodN(3)}(undef, 1)
1-element Vector{Tuple{Union{Int8, UInt8}, Union{Int8, UInt8}, Union{Int8, UInt8}}}:
#undef
julia> A0=Vector{sumN(3)}(undef, 1)
1-element Vector{Tuple{Union{Int8, UInt8}, Union{Int8, UInt8}, Union{Int8, UInt8}}}:
#undef
julia> r = Ref{sumN(3)}()
Base.RefValue{Union{Tuple{Int8, Int8, Int8}, Tuple{Int8, Int8, UInt8}, Tuple{Int8, UInt8, Int8}, Tuple{Int8, UInt8, UInt8}, Tuple{UInt8, Int8, Int8}, Tuple{UInt8, Int8, UInt8}, Tuple{UInt8, UInt8, Int8}, Tuple{UInt8, UInt8, UInt8}}}((0, 0, 0))
From that example we learn that the data layout of arrays / types is not deterministic in julia (e.g. suppose that the first compile/call events for A0/A1 raced against each other). Unfortunate!
It depends on the description that is first seen and then cached in the method tables. If we later come with a different description, the compiler must look whether it already knows an equivalent description of the type and use the data layout it already fixed. It must do this because “equivalent type” is, to some extent, speced in stone – we can’t really decide before 2.0 that sumN(10) != prodN(10)
.