summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/Reifiers.scala149
-rw-r--r--test/files/run/t5271_1.check (renamed from test/pending/run/t5271_1.check)0
-rw-r--r--test/files/run/t5271_1.scala (renamed from test/pending/run/t5271_1.scala)0
-rw-r--r--test/files/run/t5271_2.check (renamed from test/pending/run/t5271_2.check)0
-rw-r--r--test/files/run/t5271_2.scala (renamed from test/pending/run/t5271_2.scala)0
-rw-r--r--test/files/run/t5271_3.check1
-rw-r--r--test/files/run/t5271_3.scala17
-rw-r--r--test/files/run/t5271_4.check0
-rw-r--r--test/files/run/t5271_4.scala14
-rw-r--r--test/files/run/t5273_1.check (renamed from test/pending/run/t5273_2.check)0
-rw-r--r--test/files/run/t5273_1.scala (renamed from test/pending/run/t5273_2.scala)0
-rw-r--r--test/files/run/t5273_2a.check1
-rw-r--r--test/files/run/t5273_2a.scala15
-rw-r--r--test/files/run/t5273_2b.check (renamed from test/pending/run/t5273_1.check)0
-rw-r--r--test/files/run/t5273_2b.scala (renamed from test/pending/run/t5273_1.scala)0
-rw-r--r--test/files/run/t5276_1a.check1
-rw-r--r--test/files/run/t5276_1a.scala (renamed from test/pending/run/t5276.scala)2
-rw-r--r--test/files/run/t5276_1b.check1
-rw-r--r--test/files/run/t5276_1b.scala15
-rw-r--r--test/files/run/t5276_2a.check1
-rw-r--r--test/files/run/t5276_2a.scala18
-rw-r--r--test/files/run/t5276_2b.check1
-rw-r--r--test/files/run/t5276_2b.scala19
-rw-r--r--test/pending/run/t5276.check1
24 files changed, 223 insertions, 33 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/Reifiers.scala b/src/compiler/scala/tools/nsc/ast/Reifiers.scala
index 91d5d2bf4a..21e075950f 100644
--- a/src/compiler/scala/tools/nsc/ast/Reifiers.scala
+++ b/src/compiler/scala/tools/nsc/ast/Reifiers.scala
@@ -8,6 +8,7 @@ package ast
import symtab._
import Flags._
+import scala.reflect.api.Modifier._
import scala.collection.{ mutable, immutable }
import scala.collection.mutable.ListBuffer
import scala.tools.nsc.util.FreshNameCreator
@@ -289,10 +290,102 @@ trait Reifiers { self: Global =>
var reifySymbols = false
var reifyTypes = false
+ /** Preprocess a tree before reification */
+ private def trimTree(tree: Tree): Tree = {
+ def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]) = {
+ var stats1 = stats filterNot (stat => stat.isDef && {
+ if (stat.symbol.isCaseAccessorMethod && reifyDebug) println("discarding case accessor method: " + stat)
+ stat.symbol.isCaseAccessorMethod
+ })
+ stats1 = stats1 filterNot (memberDef => memberDef.isDef && {
+ val isSynthetic = memberDef.symbol.isSynthetic
+ // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
+ // that's why I replace the check with an assumption that all synthetic members are, in fact, generated of case classes
+// val isCaseMember = deff.symbol.isCaseClass || deff.symbol.companionClass.isCaseClass
+ val isCaseMember = true
+ if (isSynthetic && isCaseMember && reifyDebug) println("discarding case class synthetic def: " + memberDef)
+ isSynthetic && isCaseMember
+ })
+ stats1 = stats1 map {
+ case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isCaseAccessor =>
+ if (reifyDebug) println("resetting visibility of case accessor field: " + valdef)
+ val Modifiers(flags, privateWithin, annotations) = mods
+ val flags1 = flags & ~Flags.LOCAL & ~Flags.PRIVATE
+ val mods1 = Modifiers(flags1, privateWithin, annotations)
+ ValDef(mods1, name, tpt, rhs).copyAttrs(valdef)
+ case stat =>
+ stat
+ }
+ stats1
+ }
+
+ def trimSyntheticCaseClassCompanions(stats: List[Tree]) =
+ stats diff (stats collect { case moddef: ModuleDef => moddef } filter (moddef => {
+ val isSynthetic = moddef.symbol.isSynthetic
+ // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
+ // that's why I replace the check with an assumption that all synthetic modules are, in fact, companions of case classes
+// val isCaseCompanion = moddef.symbol.companionClass.isCaseClass
+ val isCaseCompanion = true
+ // @xeno.by: we also have to do this ugly hack for the very same reason described above
+ // normally this sort of stuff is performed in reifyTree, which binds related symbols, however, local companions will be out of its reach
+ if (reifyDebug) println("boundSym: "+ moddef.symbol)
+ boundSyms += moddef.symbol
+ if (isSynthetic && isCaseCompanion && reifyDebug) println("discarding synthetic case class companion: " + moddef)
+ isSynthetic && isCaseCompanion
+ }))
+
+ tree match {
+ case tree if tree.isErroneous =>
+ tree
+ case ta @ TypeApply(hk, ts) =>
+ def isErased(tt: TypeTree) = tt.tpe != null && definedInLiftedCode(tt.tpe) && tt.original == null
+ val discard = ts collect { case tt: TypeTree => tt } exists isErased
+ if (reifyDebug && discard) println("discarding TypeApply: " + tree)
+ if (discard) hk else ta
+ case classDef @ ClassDef(mods, name, params, impl) =>
+ val Template(parents, self, body) = impl
+ val body1 = trimSyntheticCaseClassMembers(classDef, body)
+ var impl1 = Template(parents, self, body1).copyAttrs(impl)
+ ClassDef(mods, name, params, impl1).copyAttrs(classDef)
+ case moduledef @ ModuleDef(mods, name, impl) =>
+ val Template(parents, self, body) = impl
+ val body1 = trimSyntheticCaseClassMembers(moduledef, body)
+ var impl1 = Template(parents, self, body1).copyAttrs(impl)
+ ModuleDef(mods, name, impl1).copyAttrs(moduledef)
+ case template @ Template(parents, self, body) =>
+ val body1 = trimSyntheticCaseClassCompanions(body)
+ Template(parents, self, body1).copyAttrs(template)
+ case block @ Block(stats, expr) =>
+ val stats1 = trimSyntheticCaseClassCompanions(stats)
+ Block(stats1, expr).copyAttrs(block)
+ case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isLazy =>
+ if (reifyDebug) println("dropping $lzy in lazy val's name: " + tree)
+ val name1 = if (name endsWith nme.LAZY_LOCAL) name dropRight nme.LAZY_LOCAL.length else name
+ ValDef(mods, name1, tpt, rhs).copyAttrs(valdef)
+ case unapply @ UnApply(fun, args) =>
+ def extractExtractor(tree: Tree): Tree = {
+ val Apply(fun, args) = tree
+ args match {
+ case List(Ident(special)) if special == nme.SELECTOR_DUMMY =>
+ val Select(extractor, flavor) = fun
+ assert(flavor == nme.unapply || flavor == nme.unapplySeq)
+ extractor
+ case _ =>
+ extractExtractor(fun)
+ }
+ }
+
+ if (reifyDebug) println("unapplying unapply: " + tree)
+ val fun1 = extractExtractor(fun)
+ Apply(fun1, args).copyAttrs(unapply)
+ case _ =>
+ tree
+ }
+ }
+
/** Reify a tree */
- private def reifyTree(tree: Tree): Tree = {
- def reifyDefault(tree: Tree) =
- reifyProduct(tree)
+ private def reifyTree(tree0: Tree): Tree = {
+ val tree = trimTree(tree0)
var rtree = tree match {
case tree if tree.isErroneous =>
@@ -311,29 +404,24 @@ trait Reifiers { self: Global =>
} else reifyFree(tree)
case tt: TypeTree if (tt.tpe != null) =>
reifyTypeTree(tt)
- case ta @ TypeApply(hk, ts) =>
- def isErased(tt: TypeTree) = tt.tpe != null && definedInLiftedCode(tt.tpe) && tt.original == null
- val discard = ts collect { case tt: TypeTree => tt } exists isErased
- if (reifyDebug && discard) println("discarding TypeApply: " + tree)
- if (discard) reifyTree(hk) else reifyDefault(ta)
case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) =>
CannotReifyClassOfBoundType(tree, tpe)
case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym =>
CannotReifyClassOfBoundEnum(tree, constant.tpe)
case tree if tree.isDef =>
if (reifyDebug) println("boundSym: %s of type %s".format(tree.symbol, (tree.productIterator.toList collect { case tt: TypeTree => tt } headOption).getOrElse(TypeTree(tree.tpe))))
- // registerReifiableSymbol(tree.symbol)
boundSyms += tree.symbol
- if (tree.symbol.sourceModule != NoSymbol) {
- if (reifyDebug) println("boundSym (sourceModule): " + tree.symbol.sourceModule)
- boundSyms += tree.symbol.sourceModule
- }
-
- if (tree.symbol.moduleClass != NoSymbol) {
- if (reifyDebug) println("boundSym (moduleClass): " + tree.symbol.moduleClass)
- boundSyms += tree.symbol.moduleClass
- }
+ bindRelatedSymbol(tree.symbol.sourceModule, "sourceModule")
+ bindRelatedSymbol(tree.symbol.moduleClass, "moduleClass")
+ bindRelatedSymbol(tree.symbol.companionClass, "companionClass")
+ bindRelatedSymbol(tree.symbol.companionModule, "companionModule")
+ Some(tree.symbol) collect { case termSymbol: TermSymbol => bindRelatedSymbol(termSymbol.referenced, "referenced") }
+ def bindRelatedSymbol(related: Symbol, name: String): Unit =
+ if (related != null && related != NoSymbol) {
+ if (reifyDebug) println("boundSym (" + name + "): " + related)
+ boundSyms += related
+ }
val prefix = tree.productPrefix
val elements = (tree.productIterator map {
@@ -354,7 +442,7 @@ trait Reifiers { self: Global =>
}).toList
reifyProduct(prefix, elements)
case _ =>
- reifyDefault(tree)
+ reifyProduct(tree)
}
// usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation
@@ -396,10 +484,8 @@ trait Reifiers { self: Global =>
*
* This workaround worked surprisingly well and allowed me to fix several important reification bugs, until the abstraction has leaked.
* Suddenly I found out that in certain contexts original trees do not contain symbols, but are just parser trees.
- * To the moment I know two such situations:
- * 1) Unapplies: https://issues.scala-lang.org/browse/SI-5273?focusedCommentId=56057#comment-56057
- * 2) Annotations: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless
- * 3) <sigh, what will I discover next?>
+ * To the moment I know only one such situation: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless.
+ * This is laboriously worked around in the code below. I hope this will be the only workaround in this department.
*/
private def reifyTypeTree(tt: TypeTree): Tree = {
if (definedInLiftedCode(tt.tpe)) {
@@ -441,14 +527,15 @@ trait Reifiers { self: Global =>
}
} else {
var rtt = mirrorCall(nme.TypeTree, reifyType(tt.tpe))
- // @xeno.by: originals get typechecked during subsequent reflective compilation, which leads to subtle bugs
- // https://issues.scala-lang.org/browse/SI-5273?focusedCommentId=56057#comment-56057
- // until this is somehow sorted out, I disable reification of originals
- // if (tt.original != null) {
- // val setOriginal = Select(rtt, newTermName("setOriginal"))
- // val reifiedOriginal = reify(tt.original)
- // rtt = Apply(setOriginal, List(reifiedOriginal))
- // }
+ // @xeno.by: temporarily disabling reification of originals
+ // subsequent reflective compilation will try to typecheck them
+ // and this means that the reifier has to do additional efforts to ensure that this will succeed
+ // additional efforts + no clear benefit = will be implemented later
+// if (tt.original != null) {
+// val setOriginal = Select(rtt, newTermName("setOriginal"))
+// val reifiedOriginal = reify(tt.original)
+// rtt = Apply(setOriginal, List(reifiedOriginal))
+// }
rtt
}
}
diff --git a/test/pending/run/t5271_1.check b/test/files/run/t5271_1.check
index e69de29bb2..e69de29bb2 100644
--- a/test/pending/run/t5271_1.check
+++ b/test/files/run/t5271_1.check
diff --git a/test/pending/run/t5271_1.scala b/test/files/run/t5271_1.scala
index 5f10e64528..5f10e64528 100644
--- a/test/pending/run/t5271_1.scala
+++ b/test/files/run/t5271_1.scala
diff --git a/test/pending/run/t5271_2.check b/test/files/run/t5271_2.check
index b8626c4cff..b8626c4cff 100644
--- a/test/pending/run/t5271_2.check
+++ b/test/files/run/t5271_2.check
diff --git a/test/pending/run/t5271_2.scala b/test/files/run/t5271_2.scala
index 71967c04ed..71967c04ed 100644
--- a/test/pending/run/t5271_2.scala
+++ b/test/files/run/t5271_2.scala
diff --git a/test/files/run/t5271_3.check b/test/files/run/t5271_3.check
new file mode 100644
index 0000000000..f32a5804e2
--- /dev/null
+++ b/test/files/run/t5271_3.check
@@ -0,0 +1 @@
+true \ No newline at end of file
diff --git a/test/files/run/t5271_3.scala b/test/files/run/t5271_3.scala
new file mode 100644
index 0000000000..bfa116c691
--- /dev/null
+++ b/test/files/run/t5271_3.scala
@@ -0,0 +1,17 @@
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ val code = scala.reflect.Code.lift{
+ object C { def qwe = 4 }
+ case class C(foo: Int, bar: Int)
+ val c = C(2, 2)
+ println(c.foo * c.bar == C.qwe)
+ };
+
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(code.tree)
+ toolbox.runExpr(ttree)
+}
diff --git a/test/files/run/t5271_4.check b/test/files/run/t5271_4.check
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/files/run/t5271_4.check
diff --git a/test/files/run/t5271_4.scala b/test/files/run/t5271_4.scala
new file mode 100644
index 0000000000..e5e16033e8
--- /dev/null
+++ b/test/files/run/t5271_4.scala
@@ -0,0 +1,14 @@
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ val code = scala.reflect.Code.lift{
+ case object C
+ };
+
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(code.tree)
+ toolbox.runExpr(ttree)
+}
diff --git a/test/pending/run/t5273_2.check b/test/files/run/t5273_1.check
index 0cfbf08886..0cfbf08886 100644
--- a/test/pending/run/t5273_2.check
+++ b/test/files/run/t5273_1.check
diff --git a/test/pending/run/t5273_2.scala b/test/files/run/t5273_1.scala
index 1175881c9f..1175881c9f 100644
--- a/test/pending/run/t5273_2.scala
+++ b/test/files/run/t5273_1.scala
diff --git a/test/files/run/t5273_2a.check b/test/files/run/t5273_2a.check
new file mode 100644
index 0000000000..d8263ee986
--- /dev/null
+++ b/test/files/run/t5273_2a.check
@@ -0,0 +1 @@
+2 \ No newline at end of file
diff --git a/test/files/run/t5273_2a.scala b/test/files/run/t5273_2a.scala
new file mode 100644
index 0000000000..12ddbb280a
--- /dev/null
+++ b/test/files/run/t5273_2a.scala
@@ -0,0 +1,15 @@
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ val code = scala.reflect.Code.lift{
+ val foo :: bar :: _ = List(1, 2, 3)
+ println(foo * bar)
+ };
+
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(code.tree)
+ toolbox.runExpr(ttree)
+}
diff --git a/test/pending/run/t5273_1.check b/test/files/run/t5273_2b.check
index c551774ca5..c551774ca5 100644
--- a/test/pending/run/t5273_1.check
+++ b/test/files/run/t5273_2b.check
diff --git a/test/pending/run/t5273_1.scala b/test/files/run/t5273_2b.scala
index 8b75084463..8b75084463 100644
--- a/test/pending/run/t5273_1.scala
+++ b/test/files/run/t5273_2b.scala
diff --git a/test/files/run/t5276_1a.check b/test/files/run/t5276_1a.check
new file mode 100644
index 0000000000..d8263ee986
--- /dev/null
+++ b/test/files/run/t5276_1a.check
@@ -0,0 +1 @@
+2 \ No newline at end of file
diff --git a/test/pending/run/t5276.scala b/test/files/run/t5276_1a.scala
index 432fdb91e4..c8afbba19e 100644
--- a/test/pending/run/t5276.scala
+++ b/test/files/run/t5276_1a.scala
@@ -4,7 +4,7 @@ import reflect.runtime.Mirror.ToolBox
object Test extends App {
val code = scala.reflect.Code.lift{
- lazy x = 2
+ lazy val x = 2
println(x)
};
diff --git a/test/files/run/t5276_1b.check b/test/files/run/t5276_1b.check
new file mode 100644
index 0000000000..d8263ee986
--- /dev/null
+++ b/test/files/run/t5276_1b.check
@@ -0,0 +1 @@
+2 \ No newline at end of file
diff --git a/test/files/run/t5276_1b.scala b/test/files/run/t5276_1b.scala
new file mode 100644
index 0000000000..31582201fb
--- /dev/null
+++ b/test/files/run/t5276_1b.scala
@@ -0,0 +1,15 @@
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ val code = scala.reflect.Code.lift{
+ implicit lazy val x = 2
+ implicitly[Int]
+ };
+
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(code.tree)
+ toolbox.runExpr(ttree)
+}
diff --git a/test/files/run/t5276_2a.check b/test/files/run/t5276_2a.check
new file mode 100644
index 0000000000..d8263ee986
--- /dev/null
+++ b/test/files/run/t5276_2a.check
@@ -0,0 +1 @@
+2 \ No newline at end of file
diff --git a/test/files/run/t5276_2a.scala b/test/files/run/t5276_2a.scala
new file mode 100644
index 0000000000..179c14b739
--- /dev/null
+++ b/test/files/run/t5276_2a.scala
@@ -0,0 +1,18 @@
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ val code = scala.reflect.Code.lift{
+ class C {
+ lazy val x = 2
+ }
+
+ println(new C().x)
+ };
+
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(code.tree)
+ toolbox.runExpr(ttree)
+}
diff --git a/test/files/run/t5276_2b.check b/test/files/run/t5276_2b.check
new file mode 100644
index 0000000000..d8263ee986
--- /dev/null
+++ b/test/files/run/t5276_2b.check
@@ -0,0 +1 @@
+2 \ No newline at end of file
diff --git a/test/files/run/t5276_2b.scala b/test/files/run/t5276_2b.scala
new file mode 100644
index 0000000000..6fe2873fef
--- /dev/null
+++ b/test/files/run/t5276_2b.scala
@@ -0,0 +1,19 @@
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ val code = scala.reflect.Code.lift{
+ class C {
+ implicit lazy val x = 2
+ def y = implicitly[Int]
+ }
+
+ println(new C().y)
+ };
+
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(code.tree)
+ toolbox.runExpr(ttree)
+}
diff --git a/test/pending/run/t5276.check b/test/pending/run/t5276.check
deleted file mode 100644
index 0cfbf08886..0000000000
--- a/test/pending/run/t5276.check
+++ /dev/null
@@ -1 +0,0 @@
-2