summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHubert Plociniczak <hubert.plociniczak@gmail.com>2012-09-21 14:10:33 +0200
committerHubert Plociniczak <hubert.plociniczak@gmail.com>2012-10-09 21:48:56 +0200
commit981424b376e8e66253c2ec863ca1222e41d8b374 (patch)
tree32b5a2839a775531223d6ca8faf2890110542c0a
parentc61c18e042d0fa41a09fea29a8dcaa2e08a40a63 (diff)
downloadscala-981424b376e8e66253c2ec863ca1222e41d8b374.tar.gz
scala-981424b376e8e66253c2ec863ca1222e41d8b374.tar.bz2
scala-981424b376e8e66253c2ec863ca1222e41d8b374.zip
Closes SI-6358. Move accessor generation for lazy vals to typers.
Until now lazy accessors were handled somehow special because their symbol was created in typers but the corresponding tree was only added in Refchecks. This irregularity caused serious problems for value classes. Also it now looks just better when lazy value is treated in a similar way as other fields. I needed to adapt reifier so that it handles the new implementation correctly. Previously it had to recreate lazy val only by removing defdef and renaming. Now we basically need to recreate lazy val from scratch. There is one minor change to cps plugin but that is still fine because lazy vals were never really part of the transformation. Some range positions needed to be fixed manually. We could do it at the creation time but that would require a lot more "if (symbol.isLazy)" conditions for MethodSyntheis and Symbol/Tree creation and would just unnecessary complicate api. If someone has a better idea, please speak up. Range positions changes were necessary because previously accessors were created at refchecks and they weren't checked by validator (even though they were wrong). This commit removes lazy val implementation restriction introduced for 2.10.0.
-rw-r--r--src/compiler/scala/reflect/reify/phases/Reshape.scala61
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala63
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala46
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala15
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala6
-rw-r--r--src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala9
-rw-r--r--src/reflect/scala/reflect/internal/Trees.scala18
-rw-r--r--test/files/continuations-neg/lazy.check8
-rw-r--r--test/files/neg/valueclasses-impl-restrictions.check12
-rw-r--r--test/files/neg/valueclasses-impl-restrictions.scala1
-rw-r--r--test/files/pos/t6358.scala6
-rw-r--r--test/files/run/reify_lazyunit.check3
-rw-r--r--test/files/run/reify_lazyunit.scala13
16 files changed, 179 insertions, 95 deletions
diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala
index b5894e8eb6..ef099f9f1b 100644
--- a/src/compiler/scala/reflect/reify/phases/Reshape.scala
+++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala
@@ -46,13 +46,13 @@ trait Reshape {
if (discard) hk else ta
case classDef @ ClassDef(mods, name, params, impl) =>
val Template(parents, self, body) = impl
- var body1 = trimAccessors(classDef, body)
+ var body1 = trimAccessors(classDef, reshapeLazyVals(body))
body1 = trimSyntheticCaseClassMembers(classDef, body1)
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
- var body1 = trimAccessors(moduledef, body)
+ var body1 = trimAccessors(moduledef, reshapeLazyVals(body))
body1 = trimSyntheticCaseClassMembers(moduledef, body1)
var impl1 = Template(parents, self, body1).copyAttrs(impl)
ModuleDef(mods, name, impl1).copyAttrs(moduledef)
@@ -60,15 +60,11 @@ trait Reshape {
val discardedParents = parents collect { case tt: TypeTree => tt } filter isDiscarded
if (reifyDebug && discardedParents.length > 0) println("discarding parents in Template: " + discardedParents.mkString(", "))
val parents1 = parents diff discardedParents
- val body1 = trimSyntheticCaseClassCompanions(body)
+ val body1 = reshapeLazyVals(trimSyntheticCaseClassCompanions(body))
Template(parents1, self, body1).copyAttrs(template)
case block @ Block(stats, expr) =>
- val stats1 = trimSyntheticCaseClassCompanions(stats)
+ val stats1 = reshapeLazyVals(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
@@ -248,6 +244,20 @@ trait Reshape {
New(TypeTree(ann.atp) setOriginal extractOriginal(ann.original), List(args))
}
+ private def toPreTyperLazyVal(ddef: DefDef): ValDef = {
+ def extractRhs(rhs: Tree) = rhs match {
+ case Block(Assign(lhs, rhs)::Nil, _) if lhs.symbol.isLazy => rhs
+ case _ => rhs // unit or trait case
+ }
+ val DefDef(mods0, name0, _, _, tpt0, rhs0) = ddef
+ val name1 = nme.dropLocalSuffix(name0)
+ val Modifiers(flags0, privateWithin0, annotations0) = mods0
+ var flags1 = (flags0 & GetterFlags) & ~(STABLE | ACCESSOR | METHOD)
+ val mods1 = Modifiers(flags1, privateWithin0, annotations0) setPositions mods0.positions
+ val mods2 = toPreTyperModifiers(mods1, ddef.symbol)
+ ValDef(mods2, name1, tpt0, extractRhs(rhs0))
+ }
+
private def trimAccessors(deff: Tree, stats: List[Tree]): List[Tree] = {
val symdefs = (stats collect { case vodef: ValOrDefDef => vodef } map (vodeff => vodeff.symbol -> vodeff)).toMap
val accessors = scala.collection.mutable.Map[ValDef, List[DefDef]]()
@@ -270,7 +280,7 @@ trait Reshape {
});
var stats1 = stats flatMap {
- case vdef @ ValDef(mods, name, tpt, rhs) =>
+ case vdef @ ValDef(mods, name, tpt, rhs) if !mods.isLazy =>
val mods1 = if (accessors.contains(vdef)) {
val ddef = accessors(vdef)(0) // any accessor will do
val Modifiers(flags, privateWithin, annotations) = mods
@@ -287,7 +297,9 @@ trait Reshape {
val vdef1 = ValDef(mods2, name1, tpt, rhs)
if (reifyDebug) println("resetting visibility of field: %s => %s".format(vdef, vdef1))
Some(vdef1) // no copyAttrs here, because new ValDef and old symbols are now out of sync
- case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) if !ddef.mods.isLazy =>
+ // lazy val accessors are removed in reshapeLazyVals
+ // as they are needed to recreate lazy vals
if (accessors.values.exists(_.contains(ddef))) {
if (reifyDebug) println("discarding accessor method: " + ddef)
None
@@ -301,6 +313,35 @@ trait Reshape {
stats1
}
+ private def reshapeLazyVals(stats: List[Tree]): List[Tree] = {
+ val lazyvaldefs:Map[Symbol, DefDef] = stats.collect({ case ddef: DefDef if ddef.mods.isLazy => ddef }).
+ map((ddef: DefDef) => ddef.symbol -> ddef).toMap
+ // lazy valdef and defdef are in the same block.
+ // only that valdef needs to have its rhs rebuilt from defdef
+ stats flatMap (stat => stat match {
+ case vdef @ ValDef(mods0, name0, tpt0, rhs0) if vdef.symbol.isLazy =>
+ if (reifyDebug) println(s"reconstructing original lazy value for $vdef")
+ val ddefSym = vdef.symbol.lazyAccessor
+ val vdef1 = lazyvaldefs.get(ddefSym) match {
+ case Some(ddef) =>
+ toPreTyperLazyVal(ddef)
+ case None =>
+ if (reifyDebug) println("couldn't find corresponding lazy val accessor")
+ vdef
+ }
+ if (reifyDebug) println(s"reconstructed lazy val is $vdef1")
+ vdef1::Nil
+ case ddef @ DefDef(mods0, name0, _, _, tpt0, rhs0) if ddef.symbol.isLazy =>
+ def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty
+ if (hasUnitType(ddef.symbol)) {
+ // since lazy values of type Unit don't have val's
+ // we need to create them from scratch
+ toPreTyperLazyVal(ddef) :: Nil
+ } else Nil
+ case _ => stat::Nil
+ })
+ }
+
private def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]): List[Tree] =
stats filterNot (memberDef => memberDef.isDef && {
val isSynthetic = memberDef.symbol.isSynthetic
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
index c0cc560a17..481228fb3d 100644
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -94,6 +94,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
} else
sym.owner
}
+ debuglog(s"determined enclosing class/dummy/method for lazy val as $enclosingClassOrDummyOrMethod given symbol $sym")
val idx = lazyVals(enclosingClassOrDummyOrMethod)
lazyVals(enclosingClassOrDummyOrMethod) = idx + 1
val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym)
@@ -194,6 +195,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), lzyVal.pos, STABLE | PRIVATE)
defSym setInfo MethodType(List(), lzyVal.tpe.resultType)
defSym.owner = lzyVal.owner
+ debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal")
if (bitmaps.contains(lzyVal))
bitmaps(lzyVal).map(_.owner = defSym)
val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym)
@@ -248,6 +250,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
def mkBlock(stmt: Tree) = BLOCK(stmt, mkSetFlag(bitmapSym, mask, bitmapRef), UNIT)
+ debuglog(s"create complete lazy def in $methOrClass for $lazyVal")
val (block, res) = tree match {
case Block(List(assignment), res) if !lazyUnit(lazyVal) =>
(mkBlock(assignment), res)
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 91dcd90962..8e803a9a9e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -198,13 +198,14 @@ trait MethodSynthesis {
if (nme.isSetterName(name))
ValOrValWithSetterSuffixError(tree)
- val getter = Getter(tree).createAndEnterSymbol()
-
tree.symbol = (
- if (mods.isLazy) enterLazyVal(tree, getter)
- else {
+ if (mods.isLazy) {
+ val lazyValGetter = LazyValGetter(tree).createAndEnterSymbol()
+ enterLazyVal(tree, lazyValGetter)
+ } else {
if (mods.isPrivateLocal)
PrivateThisCaseClassParameterError(tree)
+ val getter = Getter(tree).createAndEnterSymbol()
// Create the setter if necessary.
if (mods.isMutable)
Setter(tree).createAndEnterSymbol()
@@ -219,7 +220,7 @@ trait MethodSynthesis {
}
def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match {
- case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) && !vd.symbol.isLazy =>
+ case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) =>
// If we don't save the annotations, they seem to wander off.
val annotations = stat.symbol.initialize.annotations
( allValDefDerived(vd)
@@ -247,6 +248,7 @@ trait MethodSynthesis {
def standardAccessors(vd: ValDef): List[DerivedFromValDef] = (
if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd))
+ else if (vd.mods.isLazy) List(LazyValGetter(vd))
else List(Getter(vd))
)
def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
@@ -259,10 +261,15 @@ trait MethodSynthesis {
else Nil
}
def allValDefDerived(vd: ValDef) = {
- val field = if (vd.mods.isDeferred) Nil else List(Field(vd))
+ val field = if (vd.mods.isDeferred || (vd.mods.isLazy && hasUnitType(vd.symbol))) Nil
+ else List(Field(vd))
field ::: standardAccessors(vd) ::: beanAccessors(vd)
}
+ // Take into account annotations so that we keep annotated unit lazy val
+ // to get better error message already from the cps plugin itself
+ def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty
+
/** This trait assembles what's needed for synthesizing derived methods.
* Important: Typically, instances of this trait are created TWICE for each derived
* symbol; once form Namers in an enter method, and once from Typers in addDerivedTrees.
@@ -388,16 +395,12 @@ trait MethodSynthesis {
def name: TermName = tree.name.toTermName
}
- case class Getter(tree: ValDef) extends DerivedGetter {
+ abstract class BaseGetter(tree: ValDef) extends DerivedGetter {
def name = tree.name
def category = GetterTargetClass
def flagsMask = GetterFlags
def flagsExtra = ACCESSOR | ( if (tree.mods.isMutable) 0 else STABLE )
- override def derivedSym = (
- if (mods.isDeferred) basisSym
- else basisSym.getter(enclClass)
- )
override def validate() {
assert(derivedSym != NoSymbol, tree)
if (derivedSym.isOverloaded)
@@ -405,6 +408,13 @@ trait MethodSynthesis {
super.validate()
}
+ }
+ case class Getter(tree: ValDef) extends BaseGetter(tree) {
+ override def derivedSym = (
+ if (mods.isDeferred) basisSym
+ else basisSym.getter(enclClass)
+ )
+
override def derivedTree: DefDef = {
// For existentials, don't specify a type for the getter, even one derived
// from the symbol! This leads to incompatible existentials for the field and
@@ -437,6 +447,36 @@ trait MethodSynthesis {
}
}
}
+ /** Implements lazy value accessors:
+ * - for lazy values of type Unit and all lazy fields inside traits,
+ * the rhs is the initializer itself
+ * - for all other lazy values z the accessor is a block of this form:
+ * { z = <rhs>; z } where z can be an identifier or a field.
+ */
+ case class LazyValGetter(tree: ValDef) extends BaseGetter(tree) {
+ // todo: in future this should be enabled but now other phases still depend on the flag for various reasons
+ //override def flagsMask = (super.flagsMask & ~LAZY)
+ override def derivedSym = basisSym.lazyAccessor
+ override def derivedTree: DefDef = {
+ val ValDef(_, _, tpt0, rhs0) = tree
+ val rhs1 = transformed.get(rhs0) match {
+ case Some(rhs) => rhs
+ case None => rhs0
+ }
+ val body = (
+ if (tree.symbol.owner.isTrait || hasUnitType(basisSym)) rhs1
+ else gen.mkAssignAndReturn(basisSym, rhs1)
+ )
+ derivedSym.setPos(tree.pos) // cannot set it at createAndEnterSymbol because basisSym can possible stil have NoPosition
+ val ddefRes = atPos(tree.pos)(DefDef(derivedSym, body.changeOwner(followModuleClass = true, basisSym -> derivedSym)))
+ // ValDef will have its position focused whereas DefDef will have original correct rangepos
+ // ideally positions would be correct at the creation time but lazy vals are really a special case
+ // here so for the sake of keeping api clean we fix positions manually in LazyValGetter
+ ddefRes.tpt.setPos(tpt0.pos)
+ tpt0.setPos(tpt0.pos.focus)
+ ddefRes
+ }
+ }
case class Setter(tree: ValDef) extends DerivedSetter {
def name = nme.getterToSetter(tree.name)
def category = SetterTargetClass
@@ -455,6 +495,7 @@ trait MethodSynthesis {
override def keepClean = !mods.isParamAccessor
override def derivedTree = (
if (mods.isDeferred) EmptyTree
+ else if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus)
else copyValDef(tree)(mods = mods | flagsExtra, name = this.name)
)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 9e1a9d6d17..f456856b3e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -113,10 +113,8 @@ trait Namers extends MethodSynthesis {
|| (context.unit.isJava)
)
def noFinishGetterSetter(vd: ValDef) = (
- vd.mods.isPrivateLocal
- || vd.symbol.isModuleVar
- || vd.symbol.isLazy
- )
+ (vd.mods.isPrivateLocal && !vd.mods.isLazy) // all lazy vals need accessors, even private[this]
+ || vd.symbol.isModuleVar)
def setPrivateWithin[T <: Symbol](tree: Tree, sym: T, mods: Modifiers): T =
if (sym.isPrivateLocal || !mods.hasAccessBoundary) sym
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
index 96e1ed9a1c..b6a56515ca 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
@@ -1376,10 +1376,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
t.symbol.owner = currentOwner
case d : DefTree if (d.symbol != NoSymbol) && ((d.symbol.owner == NoSymbol) || (d.symbol.owner == origOwner)) => // don't indiscriminately change existing owners! (see e.g., pos/t3440, pos/t3534, pos/unapplyContexts2)
patmatDebug("def: "+ (d, d.symbol.ownerChain, currentOwner.ownerChain))
- if(d.symbol.isLazy) { // for lazy val's accessor -- is there no tree??
- assert(d.symbol.lazyAccessor != NoSymbol && d.symbol.lazyAccessor.owner == d.symbol.owner, d.symbol.lazyAccessor)
- d.symbol.lazyAccessor.owner = currentOwner
- }
if(d.symbol.moduleClass ne NoSymbol)
d.symbol.moduleClass.owner = currentOwner
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index ccb9478aee..dacb68ea86 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1014,15 +1014,18 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
private def enterSyms(stats: List[Tree]) {
var index = -1
for (stat <- stats) {
- index = index + 1;
+ index = index + 1
+ def enterSym(sym: Symbol) = if (sym.isLocal) {
+ currentLevel.scope.enter(sym)
+ symIndex(sym) = index
+ }
+
stat match {
+ case DefDef(_, _, _, _, _, _) if stat.symbol.isLazy =>
+ enterSym(stat.symbol)
case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) =>
//assert(stat.symbol != NoSymbol, stat);//debug
- val sym = stat.symbol.lazyAccessorOrSelf
- if (sym.isLocal) {
- currentLevel.scope.enter(sym)
- symIndex(sym) = index;
- }
+ enterSym(stat.symbol.lazyAccessorOrSelf)
case _ =>
}
}
@@ -1287,34 +1290,6 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
})
}
- /** Implements lazy value accessors:
- * - for lazy values of type Unit and all lazy fields inside traits,
- * the rhs is the initializer itself
- * - for all other lazy values z the accessor is a block of this form:
- * { z = <rhs>; z } where z can be an identifier or a field.
- */
- private def makeLazyAccessor(tree: Tree, rhs: Tree): List[Tree] = {
- val vsym = tree.symbol
- assert(vsym.isTerm, vsym)
- val hasUnitType = vsym.tpe.typeSymbol == UnitClass
- val lazySym = vsym.lazyAccessor
- assert(lazySym != NoSymbol, vsym)
-
- // for traits, this is further transformed in mixins
- val body = (
- if (tree.symbol.owner.isTrait || hasUnitType) rhs
- else gen.mkAssignAndReturn(vsym, rhs)
- )
- val lazyDef = atPos(tree.pos)(DefDef(lazySym, body.changeOwner(vsym -> lazySym)))
- debuglog("Created lazy accessor: " + lazyDef)
-
- if (hasUnitType) List(typed(lazyDef))
- else List(
- typed(ValDef(vsym)),
- exitingRefchecks(typed(lazyDef))
- )
- }
-
def transformStat(tree: Tree, index: Int): List[Tree] = tree match {
case t if treeInfo.isSelfConstrCall(t) =>
assert(index == 0, index)
@@ -1327,8 +1302,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
case ModuleDef(_, _, _) => eliminateModuleDefs(tree)
case ValDef(_, _, _, _) =>
val tree1 @ ValDef(_, _, _, rhs) = transform(tree) // important to do before forward reference check
- if (tree.symbol.isLazy)
- makeLazyAccessor(tree, rhs)
+ if (tree1.symbol.isLazy) tree1 :: Nil
else {
val lazySym = tree.symbol.lazyAccessorOrSelf
if (lazySym.isLocal && index <= currentLevel.maxindex) {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 688b4b5160..53ff15fef2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1425,9 +1425,6 @@ trait Typers extends Modes with Adaptations with Tags {
//see https://issues.scala-lang.org/browse/SI-6463
case _: ClassDef =>
implRestriction(tree, "nested class")
- case x: ValDef if x.mods.isLazy =>
- //see https://issues.scala-lang.org/browse/SI-6358
- implRestriction(tree, "lazy val")
case _ =>
}
super.traverse(tree)
@@ -1907,7 +1904,7 @@ trait Typers extends Modes with Adaptations with Tags {
val rhs1 =
if (vdef.rhs.isEmpty) {
- if (sym.isVariable && sym.owner.isTerm && !isPastTyper)
+ if (sym.isVariable && sym.owner.isTerm && !sym.isLazy && !isPastTyper)
LocalVarUninitializedError(vdef)
vdef.rhs
} else {
@@ -2333,9 +2330,15 @@ trait Typers extends Modes with Adaptations with Tags {
case _ =>
}
}
- val stats1 = typedStats(block.stats, context.owner)
+ val stats1 = if (isPastTyper) block.stats else
+ block.stats.flatMap(stat => stat match {
+ case vd@ValDef(_, _, _, _) if vd.symbol.isLazy =>
+ namer.addDerivedTrees(Typer.this, vd)
+ case _ => stat::Nil
+ })
+ val stats2 = typedStats(stats1, context.owner)
val expr1 = typed(block.expr, mode & ~(FUNmode | QUALmode), pt)
- treeCopy.Block(block, stats1, expr1)
+ treeCopy.Block(block, stats2, expr1)
.setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst)
} finally {
// enable escaping privates checking from the outside and recycle
diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
index be3138c373..c147dc483d 100644
--- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
+++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala
@@ -496,7 +496,11 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes {
case ValDef(mods, name, tpt, rhs) =>
vprintln("[checker] checking valdef " + name + "/"+tpe+"/"+tpt+"/"+tree.symbol.tpe)
// ValDef symbols must *not* have annotations!
- if (hasAnswerTypeAnn(tree.symbol.info)) { // is it okay to modify sym here?
+ // lazy vals are currently not supported
+ // but if we erase here all annotations, compiler will complain only
+ // when generating bytecode.
+ // This way lazy vals will be reported as unsupported feature later rather than weird type error.
+ if (hasAnswerTypeAnn(tree.symbol.info) && !mods.isLazy) { // is it okay to modify sym here?
vprintln("removing annotation from sym " + tree.symbol + "/" + tree.symbol.tpe + "/" + tpt)
tpt modifyType removeAllCPSAnnotations
tree.symbol modifyInfo removeAllCPSAnnotations
diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala
index 7229ea41f4..ef13f8b1d8 100644
--- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala
+++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala
@@ -195,9 +195,12 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with
case _ =>
if (hasAnswerTypeAnn(tree.tpe)) {
- if (!cpsAllowed)
- unit.error(tree.pos, "cps code not allowed here / " + tree.getClass + " / " + tree)
-
+ if (!cpsAllowed) {
+ if (tree.symbol.isLazy)
+ unit.error(tree.pos, "implementation restriction: cps annotations not allowed on lazy value definitions")
+ else
+ unit.error(tree.pos, "cps code not allowed here / " + tree.getClass + " / " + tree)
+ }
log(tree)
}
diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala
index d74a78ebda..ea7336340f 100644
--- a/src/reflect/scala/reflect/internal/Trees.scala
+++ b/src/reflect/scala/reflect/internal/Trees.scala
@@ -179,9 +179,12 @@ trait Trees extends api.Trees { self: SymbolTable =>
new ForeachPartialTreeTraverser(pf).traverse(this)
}
- def changeOwner(pairs: (Symbol, Symbol)*): Tree = {
+ def changeOwner(pairs: (Symbol, Symbol)*): Tree =
+ changeOwner(false, pairs: _*)
+
+ def changeOwner(followModuleClass: Boolean, pairs: (Symbol, Symbol)*): Tree = {
pairs.foldLeft(this) { case (t, (oldOwner, newOwner)) =>
- new ChangeOwnerTraverser(oldOwner, newOwner) apply t
+ new ChangeOwnerTraverser(oldOwner, newOwner, followModuleClass) apply t
}
}
@@ -1310,7 +1313,11 @@ trait Trees extends api.Trees { self: SymbolTable =>
}
}
- class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser {
+ class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol, followModuleClass: Boolean = false) extends Traverser {
+ def changeSymboOwnerIfCorrect(sym: Symbol) = {
+ if (sym != NoSymbol && sym.owner == oldowner)
+ sym.owner = newowner
+ }
def changeOwner(tree: Tree) = tree match {
case Return(expr) =>
if (tree.symbol == oldowner) {
@@ -1323,9 +1330,8 @@ trait Trees extends api.Trees { self: SymbolTable =>
}
}
case _: DefTree | _: Function =>
- if (tree.symbol != NoSymbol && tree.symbol.owner == oldowner) {
- tree.symbol.owner = newowner
- }
+ changeSymboOwnerIfCorrect(tree.symbol)
+ if (followModuleClass) changeSymboOwnerIfCorrect(tree.symbol.moduleClass)
case _ =>
}
override def traverse(tree: Tree) {
diff --git a/test/files/continuations-neg/lazy.check b/test/files/continuations-neg/lazy.check
index b8c6887409..3c460546be 100644
--- a/test/files/continuations-neg/lazy.check
+++ b/test/files/continuations-neg/lazy.check
@@ -1,6 +1,4 @@
-lazy.scala:5: error: type mismatch;
- found : Unit @scala.util.continuations.cpsParam[Unit,Unit]
- required: Unit
- def foo() = {
- ^
+lazy.scala:6: error: implementation restriction: cps annotations not allowed on lazy value definitions
+ lazy val x = shift((k:Unit=>Unit)=>k())
+ ^
one error found
diff --git a/test/files/neg/valueclasses-impl-restrictions.check b/test/files/neg/valueclasses-impl-restrictions.check
index 17d07ba960..63924493aa 100644
--- a/test/files/neg/valueclasses-impl-restrictions.check
+++ b/test/files/neg/valueclasses-impl-restrictions.check
@@ -2,20 +2,16 @@ valueclasses-impl-restrictions.scala:3: error: implementation restriction: neste
This restriction is planned to be removed in subsequent releases.
object X
^
-valueclasses-impl-restrictions.scala:4: error: implementation restriction: lazy val is not allowed in value class
-This restriction is planned to be removed in subsequent releases.
- lazy val y = 1
- ^
-valueclasses-impl-restrictions.scala:10: error: implementation restriction: nested trait is not allowed in value class
+valueclasses-impl-restrictions.scala:9: error: implementation restriction: nested trait is not allowed in value class
This restriction is planned to be removed in subsequent releases.
trait I2 {
^
-valueclasses-impl-restrictions.scala:16: error: implementation restriction: nested class is not allowed in value class
+valueclasses-impl-restrictions.scala:15: error: implementation restriction: nested class is not allowed in value class
This restriction is planned to be removed in subsequent releases.
val i2 = new I2 { val q = x.s }
^
-valueclasses-impl-restrictions.scala:22: error: implementation restriction: nested class is not allowed in value class
+valueclasses-impl-restrictions.scala:21: error: implementation restriction: nested class is not allowed in value class
This restriction is planned to be removed in subsequent releases.
private[this] class I2(val q: String)
^
-5 errors found
+four errors found
diff --git a/test/files/neg/valueclasses-impl-restrictions.scala b/test/files/neg/valueclasses-impl-restrictions.scala
index 53396db958..137f3f854c 100644
--- a/test/files/neg/valueclasses-impl-restrictions.scala
+++ b/test/files/neg/valueclasses-impl-restrictions.scala
@@ -1,7 +1,6 @@
class M(val t: Int) extends AnyVal {
def lazyString = {
object X
- lazy val y = 1
() => X
}
}
diff --git a/test/files/pos/t6358.scala b/test/files/pos/t6358.scala
new file mode 100644
index 0000000000..25539c885e
--- /dev/null
+++ b/test/files/pos/t6358.scala
@@ -0,0 +1,6 @@
+class L(val t: Int) extends AnyVal {
+ def lazyString = {
+ lazy val x = t.toString
+ () => x
+ }
+}
diff --git a/test/files/run/reify_lazyunit.check b/test/files/run/reify_lazyunit.check
new file mode 100644
index 0000000000..1b46c909be
--- /dev/null
+++ b/test/files/run/reify_lazyunit.check
@@ -0,0 +1,3 @@
+12
+one
+two
diff --git a/test/files/run/reify_lazyunit.scala b/test/files/run/reify_lazyunit.scala
new file mode 100644
index 0000000000..78b00cde28
--- /dev/null
+++ b/test/files/run/reify_lazyunit.scala
@@ -0,0 +1,13 @@
+import scala.reflect.runtime.universe._
+import scala.tools.reflect.Eval
+
+object Test extends App {
+ reify {
+ lazy val x = { 0; println("12")}
+ x
+ println("one")
+ x
+ println("two")
+ }.eval
+}
+