diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/Reifiers.scala | 149 | ||||
-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.check | 1 | ||||
-rw-r--r-- | test/files/run/t5271_3.scala | 17 | ||||
-rw-r--r-- | test/files/run/t5271_4.check | 0 | ||||
-rw-r--r-- | test/files/run/t5271_4.scala | 14 | ||||
-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.check | 1 | ||||
-rw-r--r-- | test/files/run/t5273_2a.scala | 15 | ||||
-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.check | 1 | ||||
-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.check | 1 | ||||
-rw-r--r-- | test/files/run/t5276_1b.scala | 15 | ||||
-rw-r--r-- | test/files/run/t5276_2a.check | 1 | ||||
-rw-r--r-- | test/files/run/t5276_2a.scala | 18 | ||||
-rw-r--r-- | test/files/run/t5276_2b.check | 1 | ||||
-rw-r--r-- | test/files/run/t5276_2b.scala | 19 | ||||
-rw-r--r-- | test/pending/run/t5276.check | 1 |
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 |