Skip to content

Macro compilation fails on ScalaJS, ok on JVM #24446

@fede0664

Description

@fede0664

Scala 3.8.0-RC1

The macro below fails compiling on ScalaJs. Compiles and run ok on JVM.

scala-cli --cli-version 1.10.1 Client.scala ClientMacro.scala
Compiles and runs ok

scala-cli --cli-version 1.10.1 Client.scala ClientMacro.scala --js
Fails compilation with error:
Error: Cannot resolve delambdafy target method $anonfun at Client.scala:22

Note, both JVM and ScalaJs compile and run on Scala 3.7.4 by adding //> using options -experimental on Client.scala

File Client.scala

//> using scala 3.8.0-RC1
//> using jvm temurin:25
//> using js-version 1.20.1

trait ApiRun:
  def runInt(a: Int): String

object ClientRunner:
  def main(args: Array[String]): Unit =
    val r = Client().client[ApiRun]
    println(r.runInt(10))

class Client():
  val exec: Runner = new Runner()

  inline def client[API]: API = ${
    ClientMacro.impl[API, Runner]('exec)
  }

class Runner():
  def run[IL, RL](arg: IL, lambda: IL => RL): RL = lambda(arg)

File ClientMacro.scala

object ClientMacro:
  import scala.quoted.*

  def impl[API: Type, RUNNER: Type](runnerImpl: Expr[RUNNER])(using Quotes): Expr[API] = implBase[API, RUNNER](runnerImpl)

  private def implBase[API: Type, RUNNER: Type](runnerImpl: Expr[RUNNER])(using Quotes): Expr[API] =
    import quotes.reflect.*

    val methods = TypeRepr.of[API].typeSymbol.methodMembers.filter(s => s.flags.is(Flags.Deferred))

    def decls(cls: Symbol): List[Symbol] = methods.map { method =>
      val methodType = TypeRepr.of[API].memberType(method)
      Symbol.newMethod(cls, method.name, methodType, flags = Flags.EmptyFlags, privateWithin = method.privateWithin.fold(Symbol.noSymbol)(_.typeSymbol))
    }

    val parents = List(TypeTree.of[Object], TypeTree.of[API])
    val cls = Symbol.newClass(Symbol.spliceOwner, "Anon", parents.map(_.tpe), decls, selfType = None)

    val result = ValDef.let(Symbol.spliceOwner, runnerImpl.asTerm) { runnerRef =>
      val body = (cls.declaredMethods).map { method =>
        DefDef(
          method,
          args =>
            val runnerImplSymb = TypeRepr.of[RUNNER].typeSymbol
            val inputType = TypeTree.of[Int]
            val returnType = TypeTree.of[String]
            val lambda = '{ (v: Int) => s"-$v-" }
            Some(
              Apply(
                TypeApply(
                  Select(runnerRef, runnerImplSymb.declaredMethod("run").head),
                  List(inputType, returnType)
                ),
                List(args.flatten.head.asExpr.asTerm, lambda.asTerm)
              ))
        )
      }

      val clsDef = ClassDef(cls, parents, body = body)
      val newCls = Typed(
        Apply(
          Select(New(TypeIdent(cls)), cls.primaryConstructor),
          Nil
        ),
        TypeTree.of[API]
      )

      Block(List(clsDef), newCls)
    }

    println(result.show)
    result.asExprOf[API]

Metadata

Metadata

Assignees

No one assigned

    Labels

    itype:bugstat:needs triageEvery issue needs to have an "area" and "itype" label

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions