diff options
Diffstat (limited to 'src/compiler')
7 files changed, 127 insertions, 71 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 12e2433e0d..21213cf9d9 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 a37dc77d44..9e9a22d4d1 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 2282f62152..de8685ab78 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 e3f5214581..e3f694f724 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1024,15 +1024,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 _ => } } @@ -1297,34 +1300,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)), - afterRefchecks(typed(lazyDef)) - ) - } - def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case t if treeInfo.isSelfConstrCall(t) => assert(index == 0, index) @@ -1337,8 +1312,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 f82786da35..2dbf715bda 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1417,9 +1417,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 Select(sup @ Super(qual, mix), selector) if selector != nme.CONSTRUCTOR && qual.symbol == clazz && mix != tpnme.EMPTY => //see https://issues.scala-lang.org/browse/SI-6483 implRestriction(sup, "qualified super reference") @@ -1905,7 +1902,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 { @@ -2331,9 +2328,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 |