Calculated type parameter warntype

in the following, I’m trying to put some calculated values as type parameters:

julia> struct A{Plus, Minus}
           A(x, y) = new{x + y, x - y}()
       end

julia> A(2, 3)
A{5,-1}()

julia> @code_warntype A(2, 3)
Variables
  #self#::Type{A}
  x::Int64
  y::Int64

Body::A{_A,_B} where _B where _A
1 ─ %1 = (x + y)::Int64
│   %2 = (x - y)::Int64
│   %3 = Core.apply_type(Main.A, %1, %2)::Type{A{_A,_B}} where _B where _A
│   %4 = %new(%3)::A{_A,_B} where _B where _A
└──      return %4

how to avoid the above warntype in construction? thanks. :pray:

As stated it’s impossible since the compiler, with the info given, has absolutely no idea what x and y are and therefore couldn’t possibly infor the type you are constructing.

OTOH, without any change, when x and y are statically known constant propagation should work.

julia> struct A{Plus, Minus}
           A(x, y) = new{x + y, x - y}()
       end

julia> f() = A(1, 2)
f (generic function with 1 method)

julia> @code_warntype f()
Variables
  #self#::Core.Compiler.Const(f, false)

Body::A{3,-1}
1 ─ %1 = Main.A(1, 2)::Core.Compiler.Const(A{3,-1}(), false)
└──      return %1

seems like using Var is the only way?

julia> struct B{Plus, Minus}
           B(::Val{x}, ::Val{y}) where {x, y} = new{x + y, x - y}()
       end

julia> B(Val(2), Val(3))
B{5,-1}()

julia> @code_warntype B(Val(2), Val(3))
Variables
  #self#::Type{B}
  #unused#@_2::Core.Compiler.Const(Val{2}(), false)
  #unused#@_3::Core.Compiler.Const(Val{3}(), false)

Body::B{5,-1}
1 ─ %1 = ($(Expr(:static_parameter, 1)) + $(Expr(:static_parameter, 2)))::Core.Compiler.Const(5, false)
│   %2 = ($(Expr(:static_parameter, 1)) - $(Expr(:static_parameter, 2)))::Core.Compiler.Const(-1, false)
│   %3 = Core.apply_type(Main.B, %1, %2)::Core.Compiler.Const(B{5,-1}, false)
│   %4 = %new(%3)::Core.Compiler.Const(B{5,-1}(), false)
└──      return %4

Sure, if you already have those values in the type parameter you can do whatever you want.

But it’s by all mean not the “only way”. In fact, it’s neither a “way” nor is it the “only” way to achieve the same effect.

It’s not a solution since you are just moving where the type instability is and it won’t help much in real code,

julia> @code_warntype Val(2)
Variables
  #self#::Type{Val}
  x::Int64

Body::Val{_A} where _A
1 ─      nothing
│   %2 = Core.apply_type(Base.Val, x)::Type{Val{_A}} where _A
│   %3 = (%2)()::Val{_A} where _A
└──      return %3

It’s not the only way to achieve this since you can go with whatever type parameter you like,

julia> struct C{P, M}
           C(::NTuple{x}, ::NTuple{y}) where {x, y} = new{x + y, x - y}()
       end

julia> @code_warntype C((1, 2, 3), (2, 3))
Variables
  #self#::Type{C}
  #unused#@_2::Tuple{Int64,Int64,Int64}
  #unused#@_3::Tuple{Int64,Int64}

Body::C{5,1}
1 ─ %1 = ($(Expr(:static_parameter, 1)) + $(Expr(:static_parameter, 2)))::Core.Compiler.Const(5, false)
│   %2 = ($(Expr(:static_parameter, 1)) - $(Expr(:static_parameter, 2)))::Core.Compiler.Const(1, false)
│   %3 = Core.apply_type(Main.C, %1, %2)::Core.Compiler.Const(C{5,1}, false)
│   %4 = %new(%3)::Core.Compiler.Const(C{5,1}(), false)
└──      return %4
2 Likes

oh my god! the fact that Val(x) gives warntype is so shocking as it’s so widely used!!! :dizzy_face:

Most use of it where the argument isn’t a constant or type parameter is wrong.

(deleted). thanks.