Skip to content

Compiler does not add type parameter in recursive inline function call, but sets it to Nothing  #16599

Closed as not planned
@hmf

Description

@hmf

Compiler version

Versions 3.2.1, 3.2.1-RC2 and 3.2.0

Minimized code

Code is available in scastie. This may be related to #16596.

import scala.compiletime.ops.int

type Count0[N,T] <: Tuple = (N,T) match
  case (0,_)      => EmptyTuple
  case (N,String)      => String *: Count0[int.-[N, 1], String]
  case (N,Int)         => Int *: Count0[int.-[N, 1], Int]
  case (N,Float)       => Float *: Count0[int.-[N, 1], Float]  
  case (N,Double)      => Double *: Count0[int.-[N, 1], Double]  


type Count1[N,T] <: Tuple = (N,T) match
  case (0,T)      => EmptyTuple
  case (N,String)      => String *: Count1[int.-[N, 1], String]
  case (N,Int)         => Int *: Count1[int.-[N, 1], Int]
  case (N,Float)       => Float *: Count1[int.-[N, 1], Float]  
  case (N,Double)      => Double *: Count1[int.-[N, 1], Double]  

transparent inline def replicateT2Fail[T](n:Int): Count1[n.type,T] =
  inline if n == 0
  then
    EmptyTuple.asInstanceOf[Count1[n.type,T]]
  else
    inline val next = n - 1    
    // Fails, T made explicit
    val tail = replicateT2Fail[T](next)
    val acc = Int *: tail
    acc.asInstanceOf[Count1[n.type,T]]


transparent inline def replicateT2Ok[T](n:Int): Count1[n.type,T] =
  inline if n == 0
  then
    EmptyTuple.asInstanceOf[Count1[n.type,T]]
  else
     // val tail = replicate(n-1)
    inline val next = n - 1
    // OK, T' == Nothing?
    val tail = replicateT2Ok(next)
    val acc = Int *: tail
    acc.asInstanceOf[Count1[n.type,T]]

val rep1Tc = replicateT2Ok[Int](3)
//summon[rep1Tc.type <:< Int *: Int *: Int *: EmptyTuple] 
println(rep1Tc)

val rep1Td = replicateT2Fail[Int](3)
//summon[rep1Td.type <:< Int *: Int *: Int *: EmptyTuple] 
println(rep1Td)

Note: when compiling with 3.2.1 the commented summons above compile, but in Scastie, it fails to compile.

Output

    Found:    (Int, Int)
    Required: Int *: Playground.Count1[(next : (2 : Int)) - (1 : Int), Int]

    Found:    Int.type *: Int *: Int *: Playground.Count1[(1 : Int) - (1 : Int), Int]
    Required: Int.type *: Int *: Playground.Count1[(next : (2 : Int)) - (1 : Int), Int]

Expectation

As described in #16596, if Count1[N,T] fails for N > 2, I would expect any function that uses this match type, to fail also. Strangely enough, it doesn't.

In the example above, replicateT2Ok[T](n:Int) I do not set [T] explicitly in the recursive call. I would expect this to be set to to T. However, it seems like it s set to Nothing, hence making Count1[N,T] and Count0[N,T] have the same final case. I don't know why, but compilation succeeds.

In the example above, replicateT2Fail[T](n:Int) I set [T] explicitly in the recursive call. As expected, I get a compilation error. This error seems to be the one I get in #16596, as is expected. What I do not expect, is to have to set T explicitly in the recursive call. I assumed the compiler would do it automatically.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions