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

ToolBox.eval cannot find macro implementation #6636

Closed
scabug opened this issue Nov 9, 2012 · 7 comments
Closed

ToolBox.eval cannot find macro implementation #6636

scabug opened this issue Nov 9, 2012 · 7 comments

Comments

@scabug
Copy link

scabug commented Nov 9, 2012

Macro definition:

// macro.scala
import scala.language.experimental.macros
import scala.reflect.macros.Context

object TestMacro {
  def test[T](t: T): Unit = macro testImpl[T]

  def testImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Unit] = {
    c.error(t.tree.pos, "some error")
    c.universe.reify {
      println(t.splice)
    }
  }
}

Macro use:

// test-macro.scala
object Test extends App {

  import TestMacro._

  test[Any] {
    0
  }

}

Compiling test-macro.scala yields the expected error message:

$ scalac -d classes macro.scala
$ scalac -cp classes -d classes test-macro.scala 
test-macro.scala:6: error: some error
    0
    ^
one error found

However, using eval fails:

// test-macro-eval-crash.scala
import scala.reflect.{ClassTag, classTag}

object Test extends App {

  def intercept[T <: Throwable : ClassTag](body: => Any): T = {
    try {
      body
      throw new Exception(s"Exception of type ${classTag[T]} was not thrown")
    } catch {
      case t: Throwable =>
        if (classTag[T].runtimeClass != t.getClass) throw t
        else t.asInstanceOf[T]
    }
  }

  def eval(code: String, compileOptions: String = ""): Any = {
    val m = scala.reflect.runtime.currentMirror
    import scala.tools.reflect.ToolBox
    val tb: ToolBox[scala.reflect.runtime.universe.type] = m.mkToolBox(options = compileOptions)
    tb.eval(tb.parse(code))
  }

  def expectError(errorSnippet: String, compileOptions: String = "")(code: String) {
    val msg = intercept[scala.tools.reflect.ToolBoxError] {
      eval(code, compileOptions)
    }.getMessage
    assert(msg contains errorSnippet, msg)
  }

  expectError("some error", "-deprecation -Xfatal-warnings") {
    """
      | import TestMacro._
      | test[Any] {
      |   0
      | }
    """.stripMargin
  }
}
$ scalac -d classes macro.scala
$ scalac -cp classes -d classes test-macro-eval-crash.scala
$ scala -cp classes Test
java.lang.AssertionError: assertion failed: reflective compilation has failed: 

macro implementation not found: test (the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
	at scala.Predef$.assert(Predef.scala:179)
	at Test$.expectError(test-macro-eval-crash.scala:27)
	at Test$delayedInit$body.apply(test-macro-eval-crash.scala:30)
	at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
	at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
	at scala.App$$anonfun$main$1.apply(App.scala:71)
	at scala.App$$anonfun$main$1.apply(App.scala:71)
	at scala.collection.immutable.List.foreach(List.scala:309)
	at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
	at scala.App$class.main(App.scala:71)
	at Test$.main(test-macro-eval-crash.scala:3)
@scabug
Copy link
Author

scabug commented Nov 9, 2012

Imported From: https://issues.scala-lang.org/browse/SI-6636?orig=1
Reporter: @phaller
Assignee: @JanBessai
Affected Versions: 2.10.0-RC2, 2.10.0
See #6393

@scabug
Copy link
Author

scabug commented Nov 9, 2012

@xeno-by said:
interestingly enough it works if you don't provide -d explicitly (i.e. when you compile into .)

@scabug
Copy link
Author

scabug commented Feb 3, 2014

Tomer Gabel (holograph) said:
Was hoping to use ToolBox.compile to test compile-time macro assertions (i.e. "macro will not work if..."-type messages) and ran into the same caveat. This hasn't been addressed yet, has it?...

@scabug
Copy link
Author

scabug commented Feb 4, 2014

@xeno-by said:
Didn't have time to look into this one yet. Is this a blocker for you?

@scabug
Copy link
Author

scabug commented Feb 5, 2014

@retronym said:
I use Toolbox for exactly this purpose in tests in scala-async. I also have to provide a -classpath explicitly: https://github.com/scala/async/blob/master/src/test/scala/scala/async/package.scala#L55

Not sure if this represents a bug per se, or just a limitation one has to live with.

@scabug
Copy link
Author

scabug commented Apr 7, 2014

@xeno-by said:
I think I now understand what the problem is.

  protected def findMacroClassLoader(): ClassLoader = {
    val classpath = global.classPath.asURLs
    macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath))
    ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
  }

This logic is obviously incompatible with ReflectGlobal, which doesn't populate global.classPath correctly.

@scabug
Copy link
Author

scabug commented Aug 31, 2015

@adriaanm said:
scala/scala#4692

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant