scala / scala3

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

yakivy/poppet macros failing with "position not set for Int" in new nightly #17610

Closed szymon-rd closed 1 year ago

szymon-rd commented 1 year ago

Compiler version

Failing with 3.3.1-RC1-bin-20230524-5262680-NIGHTLY Successful build with 3.3.1-RC1-bin-20230518-4ffe5af-NIGHTLY

The error

The build of poppet is failing in the new CB run: https://github.com/VirtusLab/community-build3/actions/runs/5094537615/jobs/9160777992#step:3:370

Output

See the link above

Expectation

Successful compilation

andrzejressel commented 1 year ago

Quasi minimized:

CoreDsl.scala ```scala object CoreDsl { trait MyMonad[F[_]] { def pure[A](x: A): F[A] = ??? def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = ??? def tailRecM[A, B](a: A)(f: A => F[Either[A, B]]): F[B] = ??? } object MyMonad { def apply[F[_]](implicit instance: MyMonad[F]): MyMonad[F] = instance } trait Codec[A, B] { def apply(a: A): Either[Unit, B] } object Codec { implicit def codecIdentityInstance[A]: Codec[A, A] = new Codec[A, A] { override def apply(a: A): Either[Unit, A] = Right(a) } } trait ProviderProcessor[F[_], I, S] { def apply(service: S): List[MethodProcessor[F, I]] } class MethodProcessor[F[_], I]( val service: String, val name: String, val arguments: List[String], val f: Map[String, I] => F[I] ) trait CodecK[F[_], G[_]] { def apply[A](a: F[A]): G[A] } object CodecK { implicit def codecKIdentityInstance[F[_]]: CodecK[F, F] = new CodecK[F, F] { override def apply[A](a: F[A]): F[A] = a } } } ```
Macros.scala ```scala import CoreDsl._ import scala.quoted.* import scala.compiletime.* import scala.collection.immutable.Stream.Cons object ProviderProcessorObjectBinCompat { implicit inline def apply[F[_], I, S](using MF: MyMonad[F]): ProviderProcessor[F, I, S] = ${ ProviderProcessorObjectBinCompat.processorExpr('MF) } def getAbstractMethods[S: Type](using q: Quotes): List[q.reflect.DefDef] = { import q.reflect._ TypeRepr.of[S].typeSymbol.memberMethods.filter(s => { s.flags.is(Flags.Method) && (s.flags.is(Flags.Deferred) || s.flags.is(Flags.Abstract)) }).sortBy(_.fullName).map(_.tree).collect { case m: DefDef => m } } def resolveTypeMember( using q: Quotes )( owner: q.reflect.TypeRepr, member: q.reflect.TypeRepr ): q.reflect.TypeRepr = { val declarationOwner = owner.baseType(member.typeSymbol.maybeOwner) member.substituteTypes(declarationOwner.typeSymbol.memberTypes, declarationOwner.typeArgs) } def inferReturnCodecs( using q: Quotes )( fType: q.reflect.TypeRepr, tType: q.reflect.TypeRepr ): q.reflect.Term = { import q.reflect._ TypeRepr.of[CodecK].appliedTo(List(fType, tType)).asType match { case ('[ckt]) => '{ ???.asInstanceOf[ckt] }.asTerm } } def processorExpr[F[_]: Type, I: Type, S: Type]( using q: Quotes )(MF: Expr[MyMonad[F]]): Expr[ProviderProcessor[F, I, S]] = '{ new ProviderProcessor[F, I, S] { override def apply(service: S): List[MethodProcessor[F, I]] = ${ ProviderProcessorObjectBinCompat.methodProcessorsImpl[F, I, S]('service, MF) } } } def methodProcessorsImpl[F[_]: Type, I: Type, S: Type]( using q: Quotes )( service: Expr[S], MF: Expr[MyMonad[F]] ): Expr[List[MethodProcessor[F, I]]] = { import q.reflect._ val decodeArgs: Expr[Map[String, I] => F[List[Any]]] = '{ input => ??? } val methodProcessors = getAbstractMethods[S].map { m => val returnKind = TypeRepr.of[MyMonad] val returnType = resolveTypeMember(TypeRepr.of[S], m.returnTpt.tpe) val returnKindCodec = inferReturnCodecs( returnKind, TypeRepr.of[F] ) val callService: Expr[Map[String, I] => F[I]] = returnType.asType match { case '[rt] => '{ input => $MF.flatMap( $decodeArgs(input) )(ast => // START Removing this part fixes issue $MF.flatMap( ${ Apply( TypeApply( Select.unique(returnKindCodec, "apply"), List(TypeTree.of[rt]) ), List(Literal(NullConstant.apply())) ).asExprOf[F[rt]] } ) ( ??? ) // END Removing this part fixes issue ) ??? } } '{ MethodProcessor[F, I]( "test", "test", List(), input => $callService(input) ) } }.toList Expr.ofList(methodProcessors) } } ```
ProviderProcessorSpec.scala ```scala import CoreDsl._ type MyF[A] given MyMonad[MyF] with override def pure[A](x: A): MyF[A] = ??? override def flatMap[A, B](fa: MyF[A])(f: A => MyF[B]): MyF[B] = ??? override def tailRecM[A, B](a: A)(f: A => MyF[Either[A, B]]): MyF[B] = ??? class ProviderProcessorSpec { trait Simple { def a0: Int } def test(): Unit = { val p = ProviderProcessorObjectBinCompat[MyF, Int, Simple] } } ```
jchyb commented 1 year ago

Wow, thank you so much @andrzejressel, this is very impressive. I've been struggling with this minimization for some time now (got only partway there). This should help out a lot (I'll try to minimize it further, which should be much easier now)

jchyb commented 1 year ago

Further minimized:

// Macros.scala
import Main._
import scala.quoted.*

object Macros {
  inline def apply(): ProviderProcessor =
      ${ Macros.processorExpr }

  def processorExpr[I: Type](using q: Quotes): Expr[ProviderProcessor] = '{
    new ProviderProcessor {
      override def apply(simple: Simple): MyF[Int] =
        ${ Macros.methodProcessorImpl('simple) }
    }
  }

  def methodProcessorImpl[I: Type](using q: Quotes)(service: Expr[Simple]): Expr[MyF[Int]] = {
    import q.reflect._

    val returnType = TypeRepr.of[Int]
    returnType.asType match {
      case '[rt] =>
        '{
          ${
            import quotes.reflect._
            TypeApply(
              Select.unique('{ ???.asInstanceOf[Codec] }.asTerm, "apply"),
              List(TypeTree.of[rt]) // generates the error, directly using Int instead of rt makes it disappear
            ).asExpr
          }
          ???
        }
    } 
  }
}
// Main.scala
object Main {
  type MyF[A]

  trait ProviderProcessor {
    def apply(simple: Simple): MyF[Int]
  }

  trait Codec {
    def apply[A]: MyF[A]
  }

  trait Simple {
    def a0: Int
  }

  def test(): Unit = {
    val p= Macros()
  }
}