Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
szymon-rd opened this issue May 29, 2023 · 3 comments · Fixed by #18178
Closed

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

szymon-rd opened this issue May 29, 2023 · 3 comments · Fixed by #18178
Assignees
Labels
area:metaprogramming:quotes Issues related to quotes and splices itype:bug regression This worked in a previous version but doesn't anymore
Milestone

Comments

@szymon-rd
Copy link
Contributor

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

@szymon-rd szymon-rd added itype:bug area:metaprogramming:quotes Issues related to quotes and splices labels May 29, 2023
@szymon-rd szymon-rd added this to the 3.3.1 milestone May 29, 2023
@szymon-rd szymon-rd added regression This worked in a previous version but doesn't anymore stat:needs minimization Needs a self contained minimization labels May 29, 2023
@andrzejressel
Copy link
Contributor

Quasi minimized:

CoreDsl.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
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
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
Copy link
Contributor

jchyb commented Jun 4, 2023

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
Copy link
Contributor

jchyb commented Jun 5, 2023

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()
  }
}

@jchyb jchyb removed their assignment Jun 20, 2023
@jchyb jchyb removed the stat:needs minimization Needs a self contained minimization label Jun 20, 2023
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jul 10, 2023
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jul 12, 2023
nicolasstucki added a commit that referenced this issue Jul 14, 2023
Kordyjan pushed a commit that referenced this issue Nov 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:metaprogramming:quotes Issues related to quotes and splices itype:bug regression This worked in a previous version but doesn't anymore
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants