From aef9933d87fe0d14149675a12e1dcb6df40a9cf7 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Sat, 14 Jun 2014 12:37:25 +0200 Subject: [PATCH 1/2] Add new pending test for sbt/sbt#1237 This test shows that macros that simply return their argument produce a stack overflow during extraction of used names. Consider a macro `foo(c: Context)(a: c.Expr[Any]) = a`. An application of this macro such as `foo(someVal)` will lead to the expansion `someVal`. sbt will extract the `original` tree from it, and find `foo(someVal)`, and recurse infinitely on `someVal`. --- .../macro-client/Client.scala | 6 ++++ .../macro-provider/Foo.scala | 9 ++++++ .../macro-arg-dep-stackoverflow/pending | 2 ++ .../project/build.scala | 29 +++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/macro-client/Client.scala create mode 100644 sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/macro-provider/Foo.scala create mode 100644 sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/pending create mode 100644 sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/project/build.scala diff --git a/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/macro-client/Client.scala b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/macro-client/Client.scala new file mode 100644 index 000000000..facd1062e --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/macro-client/Client.scala @@ -0,0 +1,6 @@ +package macros + +object Client { + val a = 1 + def test = Foo.bar(a) +} diff --git a/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/macro-provider/Foo.scala b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/macro-provider/Foo.scala new file mode 100644 index 000000000..52be7021f --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/macro-provider/Foo.scala @@ -0,0 +1,9 @@ +package macros + +import scala.language.experimental.macros +import scala.reflect.macros.Context + +object Foo { + def bar(a: Any): Any = macro impl + def impl(c: Context)(a: c.Expr[Any]): c.Expr[Any] = a +} diff --git a/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/pending b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/pending new file mode 100644 index 000000000..450fa9cf7 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/pending @@ -0,0 +1,2 @@ +# We only want to make sure we can compile this without stack overflow +> compile \ No newline at end of file diff --git a/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/project/build.scala b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/project/build.scala new file mode 100644 index 000000000..a5382240f --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/project/build.scala @@ -0,0 +1,29 @@ +import sbt._ +import Keys._ + +object build extends Build { + val defaultSettings = Seq( + libraryDependencies <+= scalaVersion("org.scala-lang" % "scala-reflect" % _ ), + incOptions := incOptions.value.withNameHashing(true) + ) + + lazy val root = Project( + base = file("."), + id = "macro", + aggregate = Seq(macroProvider, macroClient), + settings = Defaults.defaultSettings ++ defaultSettings + ) + + lazy val macroProvider = Project( + base = file("macro-provider"), + id = "macro-provider", + settings = Defaults.defaultSettings ++ defaultSettings + ) + + lazy val macroClient = Project( + base = file("macro-client"), + id = "macro-client", + dependencies = Seq(macroProvider), + settings = Defaults.defaultSettings ++ defaultSettings + ) +} From 26e2449263d423c18530db6f5591a04842d30e3e Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Sat, 14 Jun 2014 16:58:10 +0200 Subject: [PATCH 2/2] Never inspect twice the same macro application In Scala 2.10.4, this macro can produce a stack overflow : def foo(a: Any): Any = macro impl def impl(c: Context)(a: c.Expr[Any]): c.Expr[Any] = a Here, an application such as `foo(someVal)` will produce the expansion `someVal`. As expected, `someVal` has `original` tree `foo(someVal)`, but if we inspect this tree, we will find that `someVal` has an original tree, but it shouldn't. Moreover, in Scala 2.11, some macros have their own application as `original` trees. See sbt/sbt#1237 for a description of these problems. This commit fixes these two problems. Fixes sbt/sbt#1237 --- .../src/main/scala/xsbt/ExtractUsedNames.scala | 10 +++++----- .../macro-arg-dep-stackoverflow/{pending => test} | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/{pending => test} (100%) diff --git a/compile/interface/src/main/scala/xsbt/ExtractUsedNames.scala b/compile/interface/src/main/scala/xsbt/ExtractUsedNames.scala index 85b78e0d9..4d69f0d9b 100644 --- a/compile/interface/src/main/scala/xsbt/ExtractUsedNames.scala +++ b/compile/interface/src/main/scala/xsbt/ExtractUsedNames.scala @@ -56,12 +56,12 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext def handleTreeNode(node: Tree): Unit = { def handleMacroExpansion(original: Tree): Unit = { - // Some macros seem to have themselves registered as original tree. - // In this case, we only need to handle the children of the original tree, - // because we already handled the expanded tree. + // Some macros seem to be their own orignal tree, or appear in the children of their + // original tree. To prevent infinite loops, we need to filter out nodes that we already + // handled. + // This is only relevant for Scala 2.10.4 // See https://issues.scala-lang.org/browse/SI-8486 - if (original == node) original.children.foreach(handleTreeNode) - else original.foreach(handleTreeNode) + original.filter(_ ne node).foreach(handleTreeNode) } def handleClassicTreeNode(node: Tree): Unit = node match { diff --git a/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/pending b/sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/test similarity index 100% rename from sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/pending rename to sbt/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/test