scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.83k stars 1.05k forks source link

Cannot summon Type[T] from Quotes #21696

Open tribbloid opened 20 hours ago

tribbloid commented 20 hours ago

Compiler version

3.5.1

Minimized code

import scala.quoted.{Expr, Quotes, Type}

case class Thing[T](vs: List[String])

class BugDemo {

  object TASTyLowering {

    override protected def deserialize[T](expr: Expr[List[String]])(
        using
        qt: Quotes
    ): Expr[Thing[T]] = {

      val tt = Type.of[T](
        using
        qt
      )

      '{ Thing[T]($expr) }
    }
  }
}

Output

/home/peng/git/dottyspike/src/main/scala/com/tribbloids/spike/dotty/quoted/autolift/spike/BugDemo.scala:16:24
Reference to T within quotes requires a given scala.quoted.Type[T] in scope.

      val tt = Type.of[T](

/home/peng/git/dottyspike/src/main/scala/com/tribbloids/spike/dotty/quoted/autolift/spike/BugDemo.scala:21:16
Reference to T within quotes requires a given scala.quoted.Type[T] in scope.

      '{ Thing[T]($expr) }

the signature of Type.of[T] is:

  /** Return a quoted.Type with the given type */
  @compileTimeOnly("Reference to `scala.quoted.Type.of` was not handled by PickleQuotes")
  given of[T <: AnyKind](using Quotes): Type[T] = ???

so clearly something is wrong here

Expectation

both summoning should succeed

hamzaremmal commented 13 hours ago

Hi @tribbloid,

This is actually the correct behavior. Since Scala is subject to the erasure of type parameters, we don't have enough information at runtime to be able to execute val tt = Type.of[T](using qt) or compute anything related to T. To be able to use any information on the type parameter at runtime, you should include a witness (using Type[T]) (or using the following syntax [T: Type]) that describe the type parameter T.

To fix the provided snippet, here is the change to do:

import scala.quoted.{Expr, Quotes, Type}

case class Thing[T](vs: List[String])

def deserialize[T: Type](expr: Expr[List[String]])(using Quotes): Expr[Thing[T]] = {
      '{ Thing[T]($expr) }
}
hamzaremmal commented 12 hours ago

I'm keeping the ticket open to see if we might benefit from a better error reporting

tribbloid commented 1 hour ago

@hamzaremmal I doubt if it could be that simple, everything defined in the example is indeed compile-time only.

But you are right it is not revealing enough, may I suggest a better example?

comparing the following 2 cases:

(error)


object Demo1 {

  object TASTyLowering {

    def deserialize[T](expr: Expr[List[String]])(
        using
        qt: Quotes,
        tt: Type[T]
    ): Expr[Thing[T]] = {

      '{ Thing[T]($expr) }
    }
  }

  def deserialize[T](expr: Expr[List[String]])(
      using
      Quotes
  ): Expr[Thing[T]] = {

    TASTyLowering.deserialize(expr)
  }
}

(success)


object Demo2 {

  object TASTyLowering {

    def deserialize[T](expr: Expr[List[String]])(
        using
        qt: Quotes,
        tt: Type[T]
    ): Expr[T] = {

      '{ ($expr).asInstanceOf[T] }
    }
  }

  def deserialize[T](expr: Expr[List[String]])(
      using
      Quotes
  ): Expr[T] = {

    TASTyLowering.deserialize(expr)
  }
}

The successful case only differs in return type of the Expr, yet Type can be summoned from Quotes using given Type.of, how did this happen?