Skip to content
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

Mixing macros doesn't work if the scala 2 macro needs a WeakTypeTag of a type parameter to an enclosing class #16630

Closed
coreywoodfield opened this issue Jan 7, 2023 · 3 comments · Fixed by #18663
Assignees
Labels

Comments

@coreywoodfield
Copy link

Compiler version

3.2.1

Minimized code

import scala.language.experimental.macros
import scala.quoted.{Quotes, Expr, Type}

class Foo[A]
object Foo {
  def apply[A] = new Foo[A]
}

trait TraitWithTypeParam[A] {
  protected inline def foo: Foo[A] = ${ MacrosImpl.fooImpl[A] }
  protected def foo: Foo[A] = macro MacrosImpl.compatFooImpl[A]
}

object MacrosImpl {
  def fooImpl[A: Type](using quotes: Quotes): Expr[Foo[A]] = '{new Foo[A]}

  def compatFooImpl[A: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context): c.Tree = {
    import c.universe._
    TypeApply(
      Select(
        Ident(c.mirror.staticModule(classOf[Foo[A]].getName)),
        TermName("apply")
      ),
      List(TypeTree(weakTypeOf[A])),
    )
  }
}

build.sbt:

lazy val macroImpl = project.in(file("macro-impl"))
  .settings(
    scalaVersion := "3.2.1",
    libraryDependencies ++= Seq(
      "org.scala-lang" % "scala-reflect" % "2.13.10",
      "org.scala-lang" %% "scala3-compiler" % scalaVersion.value % Provided,
    )
  )

Output

[error] -- Error: /home/cwoodfield/lucid/macrotest/macro-impl/src/main/scala/location/Location.scala:11:25 
[error] 11 |trait TraitWithTypeParam[A] {
[error]    |                         ^
[error]    |                Something's wrong: missing original symbol for type tree
[error] -- Error: /home/cwoodfield/lucid/macrotest/macro-impl/src/main/scala/location/Location.scala:12:61 
[error] 12 |  protected inline def foo: Foo[A] = ${ MacrosImpl.fooImpl[A] }
[error]    |                                                             ^
[error]    |No given instance of type quoted.Type[A] was found for an implicit parameter of method fooImpl in object MacrosImpl
[error]    |
[error]    |where:    A is a type in trait TraitWithTypeParam which is an alias of <error Something's wrong: missing original symbol for type tree>
[error] two errors found

Expectation

This should compile and work as described in https://docs.scala-lang.org/scala3/guides/migration/tutorial-macro-mixing.html. Instead it doesn't compile.

Note that moving the [A] type parameter to the methods (i.e.

trait TraitWithTypeParam {
  protected inline def foo[A]: Foo[A] = ${ MacrosImpl.fooImpl[A] }
  protected def foo[A]: Foo[A] = macro MacrosImpl.compatFooImpl[A]
}

) or commenting out the scala 2 macro results in the code compiling. So it seems like attempting to call a scala 2 macro that takes a WeakTypeTag for a type that's a parameter to a class messes up the type tree for the class type parameter somehow

@coreywoodfield coreywoodfield added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 7, 2023
coreywoodfield added a commit to lucidsoftware/enumeratum that referenced this issue Jan 7, 2023
Doesn't actually work because of a bug in scala 3:
scala/scala3#16630
@prolativ prolativ added compat:scala2 area:metaprogramming:quotes Issues related to quotes and splices and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 9, 2023
@prolativ
Copy link
Contributor

prolativ commented Jan 9, 2023

Another workaround seems to be to make A a type member and add a proxy which has it as a type parameter:

import scala.language.experimental.macros
import scala.quoted.{Quotes, Expr, Type}

class Foo[A]
object Foo {
  def apply[A] = new Foo[A]
}

trait TraitWithTypeMember {
  type A
  protected inline def foo: Foo[A] = ${ MacrosImpl.fooImpl[A] }
  protected def foo: Foo[A] = macro MacrosImpl.compatFooImpl[A]
}

trait TraitWithTypeParam[A0] extends TraitWithTypeMember {
  type A = A0
}

object MacrosImpl {
  def fooImpl[A: Type](using quotes: Quotes): Expr[Foo[A]] = '{new Foo[A]}

  def compatFooImpl[A: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context): c.Tree = {
    import c.universe._
    TypeApply(
      Select(
        Ident(c.mirror.staticModule(classOf[Foo[A]].getName)),
        TermName("apply")
      ),
      List(TypeTree(weakTypeOf[A])),
    )
  }
}

This compiles however I haven't tested it at use site

@coreywoodfield
Copy link
Author

Hmm that does compile, but when you call the macro from scala 2, the WeakTypeTag is just a scala.reflect.internal.Types#AbstractNoArgsTypeRef, the typeSymbol.fullName is just TraitWithTypeMember.A, and nothing I've tried (dealias, resultType, etc) has been able to get any information about the actual type parameter: all it knows is that it's the type member A of TraitWithTypeMember. Changing the : c.WeakTypeTag to : c.TypeTag results in

[error] .../macrotest/test/Test.scala:4:3: exception during macro expansion: 
[error] java.lang.IllegalArgumentException: wrong number of arguments
[error]   foo
[error]   ^
[error] one error found

at the call-site in scala 2. I appreciate the suggestion but it doesn't appear to actually be effective 🙃

@johnduffell
Copy link
Contributor

This is still happening in scala 3.3.1, it's blocking scala-logging to be updated to work on mixed scala 2.13/3 projects.

bishabosha added a commit that referenced this issue Oct 12, 2023
Fixes #16630

This PR fixes the above issue which affects cross scala2/3 projects that
use a common macro library as per
https://docs.scala-lang.org/scala3/guides/migration/tutorial-macro-mixing.html
, e.g. scala-logging is blocked from fixing this issue
lightbend-labs/scala-logging#317

The fix makes the scala 2 macro check read the Erased flag from the
initial flags rather than completing the RHS first. This will work in
the case of scala 2 macros because the erased flag is explicitly added
rather than being in the source code. However it relies on using an
"UNSAFE" value.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants