Skip to content
Browse files

[nomaster] macro expansions are now auto-duplicated

The fix still requires macro developers to be careful about sharing trees
by references, because attributed DefTrees will still bring trouble.

However this is an improvement, because it doesn't make matters worse
and automatically fixes situations similar to one in the test.

A much more thorough discussion with a number of open questions left:
http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc

Was fixed ages ago in master in one of the paradise backports.
Never got to 2.10.x, but it's very useful, so I'm backporting it now.
  • Loading branch information...
1 parent ed5c1ab commit 75b44a6f723762cc3ebc911483beb2aec4cfee78 @xeno-by xeno-by committed Dec 25, 2012
View
16 bincompat-backward.whitelist.conf
@@ -240,5 +240,21 @@ filter {
matchName="scala.reflect.internal.StdAttachments.isMacroExpansionSuppressed"
problemName=MissingMethodProblem
}
+ {
+ matchName="scala.reflect.internal.Trees.scala$reflect$internal$Trees$$duplicator"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.Trees.duplicateAndKeepPositions"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.Trees.scala$reflect$internal$Trees$$duplicator"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.SymbolTable.scala$reflect$internal$Trees$$duplicator"
+ problemName=IncompatibleResultTypeProblem
+ }
]
}
View
24 bincompat-forward.whitelist.conf
@@ -516,5 +516,29 @@ filter {
matchName="scala.reflect.runtime.JavaMirrors#JavaMirror#FromJavaClassCompleter.scala$reflect$runtime$JavaMirrors$JavaMirror$FromJavaClassCompleter$$enterEmptyCtorIfNecessary$1"
problemName=MissingMethodProblem
}
+ {
+ matchName="scala.reflect.internal.Trees$Duplicator"
+ problemName=MissingClassProblem
+ },
+ {
+ matchName="scala.reflect.internal.Trees.duplicateAndKeepPositions"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.Trees.scala$reflect$internal$Trees$$duplicator"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.Trees.scala$reflect$internal$Trees$$duplicator"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.SymbolTable.duplicateAndKeepPositions"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.SymbolTable.scala$reflect$internal$Trees$$duplicator"
+ problemName=IncompatibleResultTypeProblem
+ }
]
}
View
8 src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -713,9 +713,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
var expectedTpe = expandee.tpe
if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType
- var typechecked = typecheck("macro def return type", expanded, expectedTpe)
- typechecked = typecheck("expected type", typechecked, pt)
- typechecked
+ // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc
+ val expanded0 = duplicateAndKeepPositions(expanded)
+ val expanded1 = typecheck("macro def return type", expanded0, expectedTpe)
+ val expanded2 = typecheck("expected type", expanded1, pt)
+ expanded2
} finally {
popMacroContext()
}
View
7 src/reflect/scala/reflect/internal/Trees.scala
@@ -1526,15 +1526,18 @@ trait Trees extends api.Trees { self: SymbolTable =>
}
}
- private lazy val duplicator = new Transformer {
+ private lazy val duplicator = new Duplicator(focusPositions = true)
+ private class Duplicator(focusPositions: Boolean) extends Transformer {
override val treeCopy = newStrictTreeCopier
override def transform(t: Tree) = {
val t1 = super.transform(t)
- if ((t1 ne t) && t1.pos.isRange) t1 setPos t.pos.focus
+ if ((t1 ne t) && t1.pos.isRange && focusPositions) t1 setPos t.pos.focus
t1
}
}
+ def duplicateAndKeepPositions(tree: Tree) = new Duplicator(focusPositions = false) transform tree
+
// ------ copiers -------------------------------------------
def copyDefDef(tree: Tree)(
View
1 test/files/run/macro-auto-duplicate.check
@@ -0,0 +1 @@
+42
View
17 test/files/run/macro-auto-duplicate/Macros_1.scala
@@ -0,0 +1,17 @@
+import scala.reflect.macros.Context
+import language.experimental.macros
+
+object Macros {
+ def impl(c: Context) = {
+ import c.universe._
+ val x = Ident(newTermName("x"))
+ def defAndUseX(rhs: Tree) = {
+ Block(List(ValDef(NoMods, newTermName("x"), TypeTree(), rhs)), x)
+ }
+ val xi4 = defAndUseX(Literal(Constant(4)))
+ val xs2 = defAndUseX(Literal(Constant("2")))
+ c.Expr[String](Apply(Select(xi4, newTermName("$plus")), List(xs2)))
+ }
+
+ def foo = macro impl
+}
View
3 test/files/run/macro-auto-duplicate/Test_2.scala
@@ -0,0 +1,3 @@
+object Test extends App {
+ println(Macros.foo)
+}
View
0 test/files/run/macro-duplicate.check
No changes.
View
1 test/files/run/macro-duplicate.flags
@@ -0,0 +1 @@
+-language:experimental.macros
View
29 test/files/run/macro-duplicate/Impls_Macros_1.scala
@@ -0,0 +1,29 @@
+import scala.reflect.macros.Context
+
+object Macros {
+ def impl(c: Context) = {
+ import c.universe._
+ val Expr(Block((cdef: ClassDef) :: Nil, _)) = reify { class C { def x = 2 } }
+ val cdef1 =
+ new Transformer {
+ override def transform(tree: Tree): Tree = tree match {
+ case Template(_, _, ctor :: defs) =>
+ val defs1 = defs collect {
+ case ddef @ DefDef(mods, name, tparams, vparamss, tpt, body) =>
+ val future = Select(Select(Select(Ident(newTermName("scala")), newTermName("concurrent")), newTermName("package")), newTermName("future"))
+ val Future = Select(Select(Ident(newTermName("scala")), newTermName("concurrent")), newTypeName("Future"))
+ val tpt1 = if (tpt.isEmpty) tpt else AppliedTypeTree(Future, List(tpt))
+ val body1 = Apply(future, List(body))
+ val name1 = newTermName("async" + name.toString.capitalize)
+ DefDef(mods, name1, tparams, vparamss, tpt1, body1)
+ }
+ Template(Nil, emptyValDef, ctor +: defs ::: defs1)
+ case _ =>
+ super.transform(tree)
+ }
+ } transform cdef
+ c.Expr[Unit](Block(cdef1 :: Nil, Literal(Constant(()))))
+ }
+
+ def foo = macro impl
+}
View
6 test/files/run/macro-duplicate/Test_2.scala
@@ -0,0 +1,6 @@
+import scala.concurrent._
+import ExecutionContext.Implicits.global
+
+object Test extends App {
+ Macros.foo
+}

0 comments on commit 75b44a6

Please sign in to comment.
Something went wrong with that request. Please try again.