From b1ce9c260fba3bac1c57886b6d8039feb635740f Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 22 Apr 2015 13:42:27 +0200 Subject: [PATCH 01/47] Mixin: needs to call transformFollowing to make memoize run on traitInits. --- src/dotty/tools/dotc/transform/Mixin.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 7e307c7360f0..7950149d6bca 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -150,7 +150,8 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitInits(mixin: ClassSymbol): List[Tree] = for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { - DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone) + // transformFollowing call is needed to make memoize & lazy vals run + transformFollowing(DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone)) } def setters(mixin: ClassSymbol): List[Tree] = @@ -163,7 +164,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => if (cls is Trait) traitDefs(impl.body) else { val mixInits = mixins.flatMap { mixin => - traitInits(mixin) ::: superCallOpt(mixin) ::: setters(mixin) + flatten(traitInits(mixin)) ::: superCallOpt(mixin) ::: setters(mixin) } superCallOpt(superCls) ::: mixInits ::: impl.body }) From e1c482a98b5d07b227f6415aac9aaff0ab06e8ff Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 23 Apr 2015 11:22:56 +0200 Subject: [PATCH 02/47] Make Memoize not depend on prepareForDefDef. [@darkdimius] I see no good reason for this, as it creates tight coupling between `ifs` in two methods. [@odersky] The reason is probably in the deleted comment: // allocate field early so that initializer has the right owner for subsequent phases in // the group. We now transform the rhs in subsequent phases with the getter as owner, where before it was the field. It is not clear to me whether this matters or not. [Update] We figured out the problem: It was a missing changeOwnerAfter in Constructor. Added to next commit. --- src/dotty/tools/dotc/transform/Memoize.scala | 34 +++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala index 75a1950328d4..d96a52868623 100644 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -48,38 +48,26 @@ import Decorators._ case _ => } - override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = { - val sym = tree.symbol - if (sym.isGetter && !sym.is(NoFieldNeeded)) { - // allocate field early so that initializer has the right owner for subsequeny phases in - // the group. - val maybeMutable = if (sym is Stable) EmptyFlags else Mutable - val field = ctx.newSymbol( - owner = ctx.owner, - name = sym.name.asTermName.fieldName, - flags = Private | maybeMutable, - info = sym.info.resultType, - coord = tree.pos).enteredAfter(thisTransform) - tree.rhs.changeOwnerAfter(sym, field, thisTransform) - } - this - } - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { val sym = tree.symbol - def field = { - val field = sym.field.asTerm - assert(field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") - field - } + + def newField = ctx.newSymbol( + owner = ctx.owner, + name = sym.name.asTermName.fieldName, + flags = Private | (if (sym is Stable) EmptyFlags else Mutable), + info = sym.info.resultType, + coord = tree.pos).enteredAfter(thisTransform) + + lazy val field = sym.field.orElse(newField).asTerm if (sym.is(Accessor, butNot = NoFieldNeeded)) if (sym.isGetter) { + tree.rhs.changeOwnerAfter(sym, field, thisTransform) val fieldDef = transformFollowing(ValDef(field, tree.rhs)) val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))) Thicket(fieldDef, getterDef) } else if (sym.isSetter) { - if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } + if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } // this is intended as an assertion val initializer = Assign(ref(field), ref(tree.vparamss.head.head.symbol)) cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)) } From b50d209d52f066e5bcf415d9def6ea503b8aea5d Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 23 Apr 2015 17:57:35 +0200 Subject: [PATCH 03/47] Make Constructors change owners. --- src/dotty/tools/dotc/transform/Constructors.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index cd64497e9a26..6842e14d73ae 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -95,9 +95,9 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor if (noDirectRefsFrom(tree)) tree else super.transform(tree) } - def apply(tree: Tree, inSuperCall: Boolean = false)(implicit ctx: Context): Tree = { + def apply(tree: Tree, prevOwner: Symbol, inSuperCall: Boolean = false)(implicit ctx: Context): Tree = { this.excluded = if (inSuperCall) EmptyFlags else Mutable - transform(tree) + transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisTransform) } } @@ -153,19 +153,19 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val sym = stat.symbol if (isRetained(sym)) { if (!stat.rhs.isEmpty && !isWildcardArg(stat.rhs)) - constrStats += Assign(ref(sym), intoConstr(stat.rhs)).withPos(stat.pos) + constrStats += Assign(ref(sym), intoConstr(stat.rhs, sym)).withPos(stat.pos) clsStats += cpy.ValDef(stat)(rhs = EmptyTree) } else if (!stat.rhs.isEmpty) { sym.copySymDenotation( initFlags = sym.flags &~ Private, owner = constr.symbol).installAfter(thisTransform) - constrStats += intoConstr(stat) + constrStats += intoConstr(stat, sym) } case _: DefTree => clsStats += stat case _ => - constrStats += intoConstr(stat) + constrStats += intoConstr(stat, tree.symbol) } splitStats(stats1) case Nil => From de67c05e45f3d8234dedf4e41250366a47a6ba77 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Apr 2015 12:56:10 +0200 Subject: [PATCH 04/47] Allow FutureDefs in changeOwner Without it, the previous commit make LazyVals blow up wgen compiling dotc/transform. I could not fugure out why, but here is the stacktrace I saw. dotty.tools.dotc.core.Denotations$NotDefinedHere: demanding denotation of method $anonfun at phase seqLiterals(17) outside defined interval: defined periods are Period(22..22, run = 2) at dotty.tools.dotc.core.Denotations$SingleDenotation.current(Denotations.scala:598) at dotty.tools.dotc.core.Types$NamedType.computeDenot(Types.scala:1221) at dotty.tools.dotc.core.Types$NamedType.denotAt(Types.scala:1206) at dotty.tools.dotc.core.Types$NamedType.denot(Types.scala:1194) at dotty.tools.dotc.ast.Trees$DenotingTree.denot(Trees.scala:270) at dotty.tools.dotc.ast.Trees$Tree.symbol(Trees.scala:187) at dotty.tools.dotc.ast.TypedTreeInfo$$anonfun$localSyms$1.apply(TreeInfo.scala:437) at dotty.tools.dotc.ast.TypedTreeInfo$$anonfun$localSyms$1.apply(TreeInfo.scala:437) at scala.collection.TraversableLike$WithFilter$$anonfun$map$2.apply(TraversableLike.scala:728) at scala.collection.immutable.List.foreach(List.scala:381) at scala.collection.TraversableLike$WithFilter.map(TraversableLike.scala:727) at dotty.tools.dotc.ast.TypedTreeInfo$class.localSyms(TreeInfo.scala:437) at dotty.tools.dotc.ast.tpd$.localSyms(tpd.scala:19) at dotty.tools.dotc.ast.TreeTypeMap.transformDefs(TreeTypeMap.scala:116) at dotty.tools.dotc.ast.TreeTypeMap.transform(TreeTypeMap.scala:98) at dotty.tools.dotc.ast.Trees$Instance$TreeMap$$anonfun$transform$2.apply(Trees.scala:1181) at dotty.tools.dotc.ast.Trees$Instance$TreeMap$$anonfun$transform$2.apply(Trees.scala:1181) at scala.collection.immutable.List.loop$1(List.scala:172) at scala.collection.immutable.List.mapConserve(List.scala:188) at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1181) at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1100) at dotty.tools.dotc.ast.TreeTypeMap.transform(TreeTypeMap.scala:108) at dotty.tools.dotc.ast.Trees$Instance$TreeMap$$anonfun$transform$2.apply(Trees.scala:1181) at dotty.tools.dotc.ast.Trees$Instance$TreeMap$$anonfun$transform$2.apply(Trees.scala:1181) at scala.collection.immutable.List.loop$1(List.scala:172) at scala.collection.immutable.List.mapConserve(List.scala:188) at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1181) at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1100) at dotty.tools.dotc.ast.TreeTypeMap.transform(TreeTypeMap.scala:108) at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1100) at dotty.tools.dotc.ast.TreeTypeMap.transform(TreeTypeMap.scala:108) at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1094) at dotty.tools.dotc.ast.TreeTypeMap.transform(TreeTypeMap.scala:108) at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1100) at dotty.tools.dotc.ast.TreeTypeMap.transform(TreeTypeMap.scala:108) at dotty.tools.dotc.ast.TreeTypeMap.apply(TreeTypeMap.scala:129) at dotty.tools.dotc.ast.tpd$TreeOps$.loop$1(tpd.scala:565) at dotty.tools.dotc.ast.tpd$TreeOps$.changeOwner$extension(tpd.scala:568) at dotty.tools.dotc.transform.LazyVals.transformLocalValDef(LazyVals.scala:110) at dotty.tools.dotc.transform.LazyVals.transformValDef(LazyVals.scala:64) --- src/dotty/tools/dotc/ast/tpd.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 0a1611b61acd..3bcfc2ddaa70 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -563,7 +563,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { loop(from.owner, from :: froms, to :: tos) else { //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") - new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree) + new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.withMode(Mode.FutureDefsOK)).apply(tree) } } loop(from, Nil, to :: Nil) From ebb3917fe33041586160fd1ff4b904ac65aeed5c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Apr 2015 12:57:27 +0200 Subject: [PATCH 05/47] Reset some flags for bridges. Bridges are never accessors, deferred, or lazy. Scalac removes these flags, I guess this was dropped as an oversight. The change is needed to make the next commit work. --- src/dotty/tools/dotc/transform/Erasure.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 79db568b8a8d..51a06f9ff37b 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -502,6 +502,8 @@ object Erasure extends TypeTestsCasts{ traverse(newStats, oldStats) } + + private final val NoBridgeFlags = Flags.Accessor | Flags.Deferred | Flags.Lazy /** Create a bridge DefDef which overrides a parent method. * @@ -520,7 +522,7 @@ object Erasure extends TypeTestsCasts{ ??? } val bridge = ctx.newSymbol(currentClass, - parentSym.name, parentSym.flags | Flags.Bridge, parentSym.info, coord = newDefSym.owner.coord).asTerm + parentSym.name, parentSym.flags &~ NoBridgeFlags | Flags.Bridge, parentSym.info, coord = newDefSym.owner.coord).asTerm bridge.enteredAfter(ctx.phase.prev.asInstanceOf[DenotTransformer]) // this should be safe, as we're executing in context of next phase ctx.debuglog(s"generating bridge from ${newDefSym} to $bridge") From 979ee0ff41c2a957b22e886ea43ad4f070a9777d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Apr 2015 15:15:20 +0200 Subject: [PATCH 06/47] Fix changeOwnerAfter by adding transformDenotations method. With the previous commit, we get a bad owner for the "typedArgs" var in `dotc.typer.Applications`. What happens is the following (@DarkDimius figured it out): Here's the code in question: val result = { var typedArg = ... (code that captures typedArg) } There's an interplay between 3 mini-phases, which run in interleaved succession in the same group: Memoize CapturedVars Constructors The following events happen in the order they are written: 1. typedArg is noted to be captured, so prepareValDef in CapturedVars installs a new denotation, valid after CapturedVars, with a Ref type. 2. Memoize in transformDefDef creates a field for `result` and changes the owner of all definitions in the right-hand side to the field, using `changeOwnerAfter`. This gives `typedArg` a new denotation with the owner being the field `result$local` and a validity period from Memoize + 1 to CapturedVars + 1 (because CapturedVars has already installed a new denotation). 3. Constructors runs intoConstructor which changes the owner again. All code with the field as current owner becomes owned by the constructor. But unfortunately `typedArg` is owned by the getter `result`, because that's the denotation installed by the preceding phase, CapturedVars. So its owner stays the `getter` even though its definition is now part of the constructor. Boom, -Ycheck fails. The fix applied here adds a new method `transformAfter` which can transform all future denotations of a symbol. `changeOwnerAfter` uses this method to become insensitive to the order in which denotations are installed. Morale: State is hard. --- src/dotty/tools/dotc/ast/tpd.scala | 7 ++-- src/dotty/tools/dotc/core/Denotations.scala | 36 ++++++++++++++----- .../tools/dotc/core/SymDenotations.scala | 6 ++++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 3bcfc2ddaa70..56d916bdd858 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -578,8 +578,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def traverse(tree: Tree)(implicit ctx: Context) = tree match { case tree: DefTree => val sym = tree.symbol - if (sym.denot(ctx.withPhase(trans)).owner == from) - sym.copySymDenotation(owner = to).installAfter(trans) + if (sym.denot(ctx.withPhase(trans)).owner == from) { + val d = sym.copySymDenotation(owner = to) + d.installAfter(trans) + d.transformAfter(trans, d => if (d.owner eq from) d.copySymDenotation(owner = to) else d) + } if (sym.isWeakOwner) traverseChildren(tree) case _ => traverseChildren(tree) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index a30cff714f62..aa1442769029 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -620,14 +620,9 @@ object Denotations { // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}") // printPeriods(current) this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) - if (current.validFor.firstPhaseId == targetId) { - // replace current with this denotation - var prev = current - while (prev.nextInRun ne current) prev = prev.nextInRun - prev.nextInRun = this - this.nextInRun = current.nextInRun - current.validFor = Nowhere - } else { + if (current.validFor.firstPhaseId == targetId) + replaceDenotation(current) + else { // insert this denotation after current current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) this.nextInRun = current.nextInRun @@ -637,6 +632,31 @@ object Denotations { } } + /** Apply a transformation `f` to all denotations in this group that start at or after + * given phase. Denotations are replaced while keeping the same validity periods. + */ + protected def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = { + var current = symbol.current + while (current.validFor.firstPhaseId < phase.id && (current ne symbol.current)) + current = current.nextInRun + while (current.validFor.firstPhaseId >= phase.id) { + val current1: SingleDenotation = f(current.asSymDenotation) + if (current1 ne current) { + current1.validFor = current.validFor + current1.replaceDenotation(current) + } + current = current.nextInRun + } + } + + private def replaceDenotation(current: SingleDenotation): Unit = { + var prev = current + while (prev.nextInRun ne current) prev = prev.nextInRun + prev.nextInRun = this + this.nextInRun = current.nextInRun + current.validFor = Nowhere + } + def staleSymbolError(implicit ctx: Context) = { def ownerMsg = this match { case denot: SymDenotation => s"in ${denot.owner}" diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 83499ca7b53c..bcd46810e0eb 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1043,6 +1043,12 @@ object SymDenotations { /** Install this denotation as the result of the given denotation transformer. */ override def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = super.installAfter(phase) + + /** Apply a transformation `f` to all denotations in this group that start at or after + * given phase. Denotations are replaced while keeping the same validity periods. + */ + override def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = + super.transformAfter(phase, f) } /** The contents of a class definition during a period From bcc6bf711d2a0110db7c9dc091a8e81697ae6a3c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 Apr 2015 15:20:25 +0200 Subject: [PATCH 07/47] Remove dead code in Constructors The intoConstr method is never called with argument inSuperCall = true. So code dependent on it can be dropped. --- src/dotty/tools/dotc/transform/Constructors.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 6842e14d73ae..ddd64d500e14 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -80,11 +80,10 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor // (2) If the parameter accessor reference was to an alias getter, // drop the () when replacing by the parameter. object intoConstr extends TreeMap { - private var excluded: FlagSet = _ override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case Ident(_) | Select(This(_), _) => var sym = tree.symbol - if (sym is (ParamAccessor, butNot = excluded)) sym = sym.subst(accessors, paramSyms) + if (sym is (ParamAccessor, butNot = Mutable)) sym = sym.subst(accessors, paramSyms) if (sym.owner.isConstructor) ref(sym).withPos(tree.pos) else tree case Apply(fn, Nil) => val fn1 = transform(fn) @@ -95,8 +94,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor if (noDirectRefsFrom(tree)) tree else super.transform(tree) } - def apply(tree: Tree, prevOwner: Symbol, inSuperCall: Boolean = false)(implicit ctx: Context): Tree = { - this.excluded = if (inSuperCall) EmptyFlags else Mutable + def apply(tree: Tree, prevOwner: Symbol)(implicit ctx: Context): Tree = { transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisTransform) } } From a2813d2ef76cea23ff8a503af8a4a8c6c84f2fa3 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 22 Apr 2015 13:42:43 +0200 Subject: [PATCH 08/47] Mixin: do not remove Module Flag. --- src/dotty/tools/dotc/transform/MixinOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala index e6074323a436..1dce85eaa9e7 100644 --- a/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -17,7 +17,7 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: member.copy( owner = cls, name = member.name.stripScala2LocalSuffix, - flags = member.flags &~ Deferred &~ Module, + flags = member.flags &~ Deferred, info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm def superRef(target: Symbol, pos: Position = cls.pos): Tree = { From 5937b881a0e169e0b6f8d583924528f4a9773d91 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 22 Apr 2015 13:45:04 +0200 Subject: [PATCH 09/47] Make LazyVals implement non-static modules. Move LV after erasure. --- src/dotty/tools/dotc/Compiler.scala | 4 +- src/dotty/tools/dotc/transform/LazyVals.scala | 75 +++++++++---------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 1657adbbb739..b4be19799c5f 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -49,8 +49,7 @@ class Compiler { List(new PatternMatcher, new ExplicitOuter, new Splitter), - List(new LazyVals, - new SeqLiterals, + List(new SeqLiterals, new InterceptedMethods, new Literalize, new Getters, @@ -58,6 +57,7 @@ class Compiler { new ResolveSuper), List(new Erasure), List(new Mixin, + new LazyVals, new Memoize, new CapturedVars, new Constructors, diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index a28102d7b744..160333fdf1cc 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -21,26 +21,17 @@ import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer, DenotTransformer} -class LazyVals extends MiniPhaseTransform with SymTransformer { +class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { import tpd._ - - def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { - if (d is(Flags.Lazy, butNot = Flags.ModuleVal | Flags.Method)) { - // Method flag is set on lazy vals coming from Unpickler. They are already methods and shouldn't be transformed twice - d.copySymDenotation( - initFlags = d.flags | Flags.Method, - info = ExprType(d.info)) - } - else d - } - def transformer = new LazyVals val containerFlags = Flags.Synthetic | Flags.Mutable | Flags.Lazy val initFlags = Flags.Synthetic | Flags.Method + val containerFlagsMask = Flags.Lazy | Flags.Accessor + /** this map contains mutable state of transformation: OffsetDefs to be appended to companion object definitions, * and number of bits currently used */ class OffsetInfo(var defs: List[Tree], var ord:Int) @@ -52,16 +43,22 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { * before this phase starts processing same tree */ // override def ensureAfter: Set[String] = Set("mixin") - override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - if (!(tree.mods is Flags.Lazy) || (tree.mods is Flags.ModuleVal)) tree + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (!(tree.symbol is Flags.Lazy)) tree else { - val isField = tree.symbol.owner.isClass - - if (isField) { - if (tree.symbol.isVolatile) transformFieldValDefVolatile(tree) - else transformFieldValDefNonVolatile(tree) + if (tree.symbol is Flags.Module) { + val field = ctx.newSymbol(tree.symbol.owner, tree.symbol.name ++ StdNames.nme.MODULE_VAR_SUFFIX, containerFlags, tree.symbol.info.resultType, coord = tree.symbol.pos) + tpd.This(tree.symbol.enclosingClass.asClass).select(defn.Object_synchronized).appliedTo( + mkDefNonThreadSafeNonNullable(field, tree.rhs).ensureConforms(tree.tpe.widen.resultType.widen)) + } else { + val isField = tree.symbol.owner.isClass + + if (isField) { + if (tree.symbol.isVolatile) transformFieldValDefVolatile(tree) + else transformFieldValDefNonVolatile(tree) + } + else transformLocalValDef(tree) } - else transformLocalValDef(tree) } } @@ -82,12 +79,11 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { * with a LazyHolder from * dotty.runtime(eg dotty.runtime.LazyInt) */ - def transformLocalValDef(x: ValDef)(implicit ctx: Context) = x match { - case ValDef(name, tpt, _) => + def transformLocalValDef(x: DefDef)(implicit ctx: Context) = { val valueInitter = x.rhs - val holderName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName - val initName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL_INIT).toTermName - val tpe = x.tpe.widen + val holderName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName + val initName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL_INIT).toTermName + val tpe = x.tpe.widen.resultType.widen val holderType = if (tpe isRef defn.IntClass) "LazyInt" @@ -124,7 +120,12 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = { - val (holders, stats) = trees.partition { _.symbol.flags == containerFlags} + val (holders, stats) = + atGroupEnd { implicit ctx: Context => + trees.partition { + _.symbol.flags == containerFlags + } + } holders:::stats } @@ -162,13 +163,12 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { If(cond, init, exp) } - def transformFieldValDefNonVolatile(x: ValDef)(implicit ctx: Context) = x match { - case ValDef(name, tpt, _) if (x.mods is Flags.Lazy) => + def transformFieldValDefNonVolatile(x: DefDef)(implicit ctx: Context) = { val claz = x.symbol.owner.asClass - val tpe = x.tpe.widen + val tpe = x.tpe.widen.resultType.widen assert(!(x.mods is Flags.Mutable)) - val containerName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName - val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ Flags.Lazy | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this) + val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName + val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this) val containerTree = ValDef(containerSymbol, initValue(tpe)) if (x.tpe.isNotNull && tpe <:< defn.AnyRefType) { // can use 'null' value instead of flag @@ -176,7 +176,7 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { Thicket(List(containerTree, slowPath)) } else { - val flagName = ctx.freshName(name.toString + StdNames.nme.BITMAP_PREFIX).toTermName + val flagName = ctx.freshName(x.name ++ StdNames.nme.BITMAP_PREFIX).toTermName val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags, defn.BooleanType) val flag = ValDef(flagSymbol, Literal(Constants.Constant(false))) val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), x.rhs)) @@ -184,7 +184,7 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { } } - /** Create non-threadsafe lazy accessor equivalent to such code + /** Create a threadsafe lazy accessor equivalent to such code * * def methodSymbol(): Int = { * val result: Int = 0 @@ -280,11 +280,10 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { DefDef(methodSymbol, Block(resultDef :: retryDef :: flagDef :: cycle :: Nil, ref(resultSymbol))) } - def transformFieldValDefVolatile(x: ValDef)(implicit ctx: Context) = x match { - case ValDef(name, tpt, _) if (x.mods is Flags.Lazy) => + def transformFieldValDefVolatile(x: DefDef)(implicit ctx: Context) = { assert(!(x.mods is Flags.Mutable)) - val tpe = x.tpe.widen + val tpe = x.tpe.widen.resultType.widen val claz = x.symbol.owner.asClass val thiz = This(claz)(ctx.fresh.setOwner(claz)) val companion = claz.companionModule @@ -323,8 +322,8 @@ class LazyVals extends MiniPhaseTransform with SymTransformer { appendOffsetDefs += (companion.name.moduleClassName -> new OffsetInfo(List(offsetTree), ord)) } - val containerName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName - val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ Flags.Lazy | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this) + val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName + val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this) val containerTree = ValDef(containerSymbol, initValue(tpe)) val offset = Select(ref(companion), offsetSymbol.name) From fe8c717be04397efba4fff43acdc62d7b6dd14f2 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 22 Apr 2015 13:48:41 +0200 Subject: [PATCH 10/47] Add a test for a deferred object in interface. --- tests/pos/interfaceObject.scala | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/pos/interfaceObject.scala diff --git a/tests/pos/interfaceObject.scala b/tests/pos/interfaceObject.scala new file mode 100644 index 000000000000..e4d8b89a8f14 --- /dev/null +++ b/tests/pos/interfaceObject.scala @@ -0,0 +1,6 @@ +trait A{ + object O +} +class B extends A { + def foo = O +} From 9c150c572ee7bb308e87cf4f0ae51a6fef22c00b Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 22 Apr 2015 13:42:00 +0200 Subject: [PATCH 11/47] Getters now also makes getters for lazy vals. --- src/dotty/tools/dotc/transform/Getters.scala | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala index 918a92a041dc..e1c35febaf82 100644 --- a/src/dotty/tools/dotc/transform/Getters.scala +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -16,15 +16,21 @@ import Decorators._ /** Performs the following rewritings for fields of a class: * * val x: T = e - * --> def x: T = e + * --> def x: T = e * var x: T = e - * --> def x: T = e + * --> def x: T = e * * val x: T - * --> def x: T + * --> def x: T + * + * lazy val x: T = e + * --> lazy def x: T =e * * var x: T - * --> def x: T + * --> def x: T + * + * non-static val x$ = e + * --> def x$ = e * * Omitted from the rewritings are * @@ -47,10 +53,10 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { def noGetterNeeded = d.is(NoGetterNeeded) || - d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || + d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) && !d.is(Flags.Lazy) || d.is(Module) && d.isStatic || d.isSelfSym - if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) { + if (d.isTerm && (d.is(Lazy) || d.owner.isClass) && d.info.isValueType && !noGetterNeeded) { val maybeStable = if (d.isStable) Stable else EmptyFlags d.copySymDenotation( initFlags = d.flags | maybeStable | AccessorCreationFlags, @@ -58,7 +64,7 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => } else d } - private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic | Lazy + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree From a274d8dfc86d95e359c45e8f13db58983a2f041b Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 24 Apr 2015 11:12:34 +0200 Subject: [PATCH 12/47] Generate getters for modules in LV. --- src/dotty/tools/dotc/transform/LazyVals.scala | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index 160333fdf1cc..8fb47f416ba6 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -48,8 +48,11 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { else { if (tree.symbol is Flags.Module) { val field = ctx.newSymbol(tree.symbol.owner, tree.symbol.name ++ StdNames.nme.MODULE_VAR_SUFFIX, containerFlags, tree.symbol.info.resultType, coord = tree.symbol.pos) - tpd.This(tree.symbol.enclosingClass.asClass).select(defn.Object_synchronized).appliedTo( - mkDefNonThreadSafeNonNullable(field, tree.rhs).ensureConforms(tree.tpe.widen.resultType.widen)) + val getter = + tpd.DefDef(tree.symbol.asTerm, tpd.This(tree.symbol.enclosingClass.asClass).select(defn.Object_synchronized).appliedTo( + mkDefNonThreadSafeNonNullable(field, tree.rhs).ensureConforms(tree.tpe.widen.resultType.widen))) + val fieldVal = tpd.ValDef(field.asTerm, initValue(field.info.widen)) + Thicket(fieldVal, getter) } else { val isField = tree.symbol.owner.isClass @@ -105,8 +108,8 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { val flag = ref(holderSymbol).select("initialized".toTermName) val initer = valueInitter.changeOwner(x.symbol, initSymbol) val initBody = - ref(holderSymbol).select(defn.Object_synchronized).appliedToType(tpe).appliedTo( - mkNonThreadSafeDef(result, flag, initer).ensureConforms(tpe)) + ref(holderSymbol).select(defn.Object_synchronized).appliedTo( + mkNonThreadSafeDef(result, flag, initer)).ensureConforms(tpe) val initTree = DefDef(initSymbol, initBody) val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List())) val methodBody = { From cf4ee1dda912125f19a891062112e2c71b7fc907 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 24 Apr 2015 11:12:56 +0200 Subject: [PATCH 13/47] LV runs after memoize. Use setters. --- src/dotty/tools/dotc/transform/LazyVals.scala | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index 8fb47f416ba6..d2f4b6ee3268 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -50,7 +50,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { val field = ctx.newSymbol(tree.symbol.owner, tree.symbol.name ++ StdNames.nme.MODULE_VAR_SUFFIX, containerFlags, tree.symbol.info.resultType, coord = tree.symbol.pos) val getter = tpd.DefDef(tree.symbol.asTerm, tpd.This(tree.symbol.enclosingClass.asClass).select(defn.Object_synchronized).appliedTo( - mkDefNonThreadSafeNonNullable(field, tree.rhs).ensureConforms(tree.tpe.widen.resultType.widen))) + mkDefNonThreadSafeNonNullable(field, tree.rhs).ensureConforms(tree.tpe.widen.resultType.widen)).ensureConforms(tree.tpe.widen.resultType.widen)) val fieldVal = tpd.ValDef(field.asTerm, initValue(field.info.widen)) Thicket(fieldVal, getter) } else { @@ -114,7 +114,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List())) val methodBody = { tpd.If(flag, EmptyTree, ref(initSymbol)) - result.ensureConforms(tpe) + result.ensureApplied.ensureConforms(tpe) } val methodTree = DefDef(x.symbol.asTerm, methodBody) ctx.debuglog(s"found a lazy val ${x.show},\n rewrote with ${holderTree.show}") @@ -144,10 +144,10 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { */ def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = { - val setFlag = Assign(flag, Literal(Constants.Constant(true))) - val setTarget = Assign(target, rhs) - val init = Block(List(setFlag, setTarget), target) - If(flag, target, init) + val setFlag = flag.becomes(Literal(Constants.Constant(true))) + val setTarget = target.becomes(rhs) + val init = Block(List(setFlag, setTarget), target.ensureApplied) + If(flag.ensureApplied, target.ensureApplied, init) } /** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code @@ -161,7 +161,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { def mkDefNonThreadSafeNonNullable(target: Symbol, rhs: Tree)(implicit ctx: Context) = { val cond = ref(target).select(nme.eq).appliedTo(Literal(Constant(null))) val exp = ref(target) - val setTarget = Assign(exp, rhs) + val setTarget = exp.becomes(rhs) val init = Block(List(setTarget), exp) If(cond, init, exp) } @@ -247,10 +247,10 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { Block(List(complete), Throw(ref(caseSymbol)) )) - val compute = Assign(ref(resultSymbol), rhs) + val compute = ref(resultSymbol).becomes(rhs) val tr = Try(compute, List(handler), EmptyTree) - val assign = Assign(ref(target), ref(resultSymbol)) - val noRetry = Assign(ref(retrySymbol), Literal(Constants.Constant(false))) + val assign = ref(target).becomes(ref(resultSymbol)) + val noRetry = ref(retrySymbol).becomes(Literal(Constants.Constant(false))) val body = If(casFlag.appliedTo(thiz, offset, ref(flagSymbol), computeState, Literal(Constant(ord))), Block(tr :: assign :: complete :: noRetry :: Nil, Literal(Constant(()))), Literal(Constant(()))) @@ -269,8 +269,8 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { } val computed = { - val noRetry = Assign(ref(retrySymbol), Literal(Constants.Constant(false))) - val result = Assign(ref(resultSymbol), ref(target)) + val noRetry = ref(retrySymbol).becomes(Literal(Constants.Constant(false))) + val result = ref(resultSymbol).becomes(ref(target)) val body = Block(noRetry :: result :: Nil, Literal(Constant(()))) CaseDef(computedState, EmptyTree, body) } @@ -278,7 +278,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { val cases = Match(stateMask.appliedTo(ref(flagSymbol), Literal(Constant(ord))), List(compute, waitFirst, waitSecond, computed)) //todo: annotate with @switch - val whileBody = Block(List(Assign(ref(flagSymbol), getFlag.appliedTo(thiz, offset))), cases) + val whileBody = Block(List(ref(flagSymbol).becomes(getFlag.appliedTo(thiz, offset))), cases) val cycle = untpd.WhileDo(whileCond, whileBody).withTypeUnchecked(defn.UnitType) DefDef(methodSymbol, Block(resultDef :: retryDef :: flagDef :: cycle :: Nil, ref(resultSymbol))) } From a049c82093c02c471e0cd44e9de916fd1c1c0724 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 24 Apr 2015 11:25:48 +0200 Subject: [PATCH 14/47] Fix two infinite cycles in transformAfter. if symbol is defining starting from this phase current.validFor.firstPhaseId < phase.id is always true. If additionally f changes the initial symbol, (current ne symbol.current) will always be true. Else if a symbol has a single denotation that spawns all periods, which is changed, second cycle becomes infinite. --- src/dotty/tools/dotc/core/Denotations.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index aa1442769029..fc8bbc84fba3 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -637,9 +637,9 @@ object Denotations { */ protected def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = { var current = symbol.current - while (current.validFor.firstPhaseId < phase.id && (current ne symbol.current)) + while (current.validFor.firstPhaseId < phase.id && (current.nextInRun.validFor.code > current.validFor.code)) current = current.nextInRun - while (current.validFor.firstPhaseId >= phase.id) { + while ((current.validFor.firstPhaseId >= phase.id) && (current.nextInRun.validFor.code > current.validFor.code)) { val current1: SingleDenotation = f(current.asSymDenotation) if (current1 ne current) { current1.validFor = current.validFor @@ -648,7 +648,7 @@ object Denotations { current = current.nextInRun } } - + private def replaceDenotation(current: SingleDenotation): Unit = { var prev = current while (prev.nextInRun ne current) prev = prev.nextInRun From f9a15df76853071b8643c30509a92321040047de Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 27 Apr 2015 18:03:43 +0200 Subject: [PATCH 15/47] Mixing should make initialisers out of lazy vals. --- src/dotty/tools/dotc/transform/Mixin.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 7950149d6bca..e20468899c69 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -97,7 +97,9 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitDefs(stats: List[Tree]): List[Tree] = { val initBuf = new mutable.ListBuffer[Tree] stats flatMap { - case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty => + case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) => + // make initializer that has all effects of previous getter, + // replace getter rhs with empty tree. val vsym = stat.symbol val isym = initializer(vsym) val rhs = Block( From d0f9d6df8bfa185019004740ea8dd6472f3e7ed9 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 27 Apr 2015 18:04:46 +0200 Subject: [PATCH 16/47] Compiler: add comment on problems in late phases. --- src/dotty/tools/dotc/Compiler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index b4be19799c5f..102d99347c92 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -59,10 +59,10 @@ class Compiler { List(new Mixin, new LazyVals, new Memoize, - new CapturedVars, + new CapturedVars, // capturedVars has a transformUnit: no phases should introduce local mutable vars here new Constructors, new FunctionalInterfaces), - List(new LambdaLift, + List(new LambdaLift, // in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here new Flatten, new RestoreScopes), List(/*new PrivateToStatic,*/ new CollectEntryPoints, new LabelDefs, new ElimWildcardIdents, new TraitConstructors), From 75588e966906ddb89edb1ffde9ea7af8d6185dab Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 27 Apr 2015 18:33:07 +0200 Subject: [PATCH 17/47] tpd.WhileDo helper. --- src/dotty/tools/dotc/ast/tpd.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 56d916bdd858..03feafafa49b 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -243,6 +243,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.TypeDef(cls.name, impl), cls) } + // {