From 164ecb70593728ce218e04f4e316a32ae346e4b7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 14 Mar 2014 18:44:03 +0100 Subject: [PATCH 1/7] Reorg of info transformer framework --- src/dotty/tools/dotc/core/Denotations.scala | 6 +- src/dotty/tools/dotc/core/Periods.scala | 2 +- src/dotty/tools/dotc/core/Transformers.scala | 63 +++++++++---------- .../tools/dotc/transform/SamplePhase.scala | 36 +++++++++++ 4 files changed, 70 insertions(+), 37 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/SamplePhase.scala diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index b185d15bf1f2..fce5b1a165b9 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -192,6 +192,9 @@ object Denotations { def requiredValue(name: PreName)(implicit ctx: Context): TermSymbol = info.member(name.toTermName).requiredSymbol(_.info.isParameterless).asTerm + def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol = + info.member(name.toTypeName).requiredSymbol(_.isClass).asClass + /** The denotation that has a type matching `targetType` when seen * as a member of type `site`, `NoDenotation` if none exists. */ @@ -515,8 +518,7 @@ object Denotations { } else { // not found, cur points to highest existing variant var startPid = cur.validFor.lastPhaseId + 1 - val transformers = ctx.transformersFor(cur) - val transformer = transformers.nextTransformer(startPid) + val transformer = ctx.infoTransformers.nextTransformer(startPid) next = transformer.transform(cur).syncWithParents if (next eq cur) startPid = cur.validFor.firstPhaseId diff --git a/src/dotty/tools/dotc/core/Periods.scala b/src/dotty/tools/dotc/core/Periods.scala index 04eef6aafc76..31fde2dac96e 100644 --- a/src/dotty/tools/dotc/core/Periods.scala +++ b/src/dotty/tools/dotc/core/Periods.scala @@ -34,7 +34,7 @@ abstract class Periods extends DotClass { self: Context => */ def stablePeriod = { var first = phaseId - val transformers = base.symTransformers + val transformers = base.infoTransformers val nxTrans = transformers.nextTransformer(first) while (first - 1 > NoPhaseId && (transformers.nextTransformer(first - 1) eq nxTrans)) { diff --git a/src/dotty/tools/dotc/core/Transformers.scala b/src/dotty/tools/dotc/core/Transformers.scala index 90df3a2743b0..9b8bb65a49c5 100644 --- a/src/dotty/tools/dotc/core/Transformers.scala +++ b/src/dotty/tools/dotc/core/Transformers.scala @@ -9,19 +9,10 @@ import Denotations._ import java.lang.AssertionError import dotty.tools.dotc.util.DotClass -trait Transformers - object Transformers { trait TransformerBase { self: ContextBase => - - def transformersFor(ref: SingleDenotation): TransformerGroup = ref match { - case _: SymDenotation => symTransformers - case _ => refTransformers - } - - val symTransformers = new TransformerGroup - val refTransformers = new TransformerGroup + val infoTransformers = new TransformerGroup } /** A transformer group contains a sequence of transformers, @@ -35,40 +26,44 @@ object Transformers { */ class TransformerGroup { - /** A transformer transforms denotations at a given phase */ - abstract class Transformer extends DotClass { - - /** The phase at the start of which the denotations are transformed */ - val phaseId: Int - - /** The last phase during which the transformed denotations are valid */ - def lastPhaseId = nextTransformer(phaseId).phaseId - 1 + private val nxTransformer = + Array.fill[Transformer](MaxPossiblePhaseId + 1)(NoTransformer) - /** The validity period of the transformer in the given context */ - def validFor(implicit ctx: Context): Period = - Period(ctx.runId, phaseId, lastPhaseId) + def nextTransformer(pid: PhaseId) = nxTransformer(pid) - /** The transformation method */ - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation - } + def install(pid: PhaseId, transFn: TransformerGroup => Transformer): Unit = + if ((pid > NoPhaseId) && (nxTransformer(pid).phaseId > pid)) { + val trans = transFn(this) + trans._phaseId = pid + nxTransformer(pid) = transFn(this) + install(pid - 1, transFn) + } /** A sentinel transformer object */ - object NoTransformer extends Transformer { - val phaseId = MaxPossiblePhaseId + 1 + object NoTransformer extends Transformer(this) { + _phaseId = MaxPossiblePhaseId + 1 override def lastPhaseId = phaseId - 1 // TODO JZ Probably off-by-N error here. MO: Don't think so: we want empty validity period. def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = unsupported("transform") } + } - private val nxTransformer = - Array.fill[Transformer](MaxPossiblePhaseId + 1)(NoTransformer) + /** A transformer transforms denotations at a given phase */ + abstract class Transformer(group: TransformerGroup) extends DotClass { - def nextTransformer(pid: PhaseId) = nxTransformer(pid) + private[Transformers] var _phaseId: PhaseId = _ - def install(pid: PhaseId, trans: Transformer): Unit = - if ((pid > NoPhaseId) && (nxTransformer(pid).phaseId > pid)) { - nxTransformer(pid) = trans - install(pid - 1, trans) - } + /** The phase at the start of which the denotations are transformed */ + def phaseId: PhaseId = _phaseId + + /** The last phase during which the transformed denotations are valid */ + def lastPhaseId = group.nextTransformer(phaseId).phaseId - 1 + + /** The validity period of the transformer in the given context */ + def validFor(implicit ctx: Context): Period = + Period(ctx.runId, phaseId, lastPhaseId) + + /** The transformation method */ + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation } } diff --git a/src/dotty/tools/dotc/transform/SamplePhase.scala b/src/dotty/tools/dotc/transform/SamplePhase.scala new file mode 100644 index 000000000000..5a6578bea1de --- /dev/null +++ b/src/dotty/tools/dotc/transform/SamplePhase.scala @@ -0,0 +1,36 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core.Denotations._ +import core.Contexts._ +import ast.Trees._ +import ast.tpd.{Apply, Tree, cpy} +import core.Transformers._ + +class SamplePhase extends TreeTransformer { + + def init(implicit ctx: Context) = { + ctx.base.infoTransformers.install(id, new UncurryInfoTransform(_)) + } + + def name = "sample" + + def transformations = Array(new UncurryTreeTransform(_, _)) + +} + +class UncurryInfoTransform(group: TransformerGroup) extends Transformer(group) { + + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ??? + +} + +class UncurryTreeTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + tree match { + case Apply(fn, args) => cpy.Apply(tree, fn, args ++ tree.args) + case _ => tree + } +} \ No newline at end of file From 9c7cf4ee6196e0ec9ac7c7701de417bd9cb31c43 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 15 Mar 2014 10:57:33 +0100 Subject: [PATCH 2/7] Renamings TreeTransformer -> TreeMap, TreeMapper -> TreeTypeMap This makes naming uniform between trees and types, and also avoids the clash with transform.TreeTransformer. The idea is that transformers are parts of phases, and have logic that is phase-specific. In particular, a context is passed around when transforming a tree. Maps are simpler, they only have a T -> T apply method. --- src/dotty/tools/dotc/ast/Trees.scala | 2 +- src/dotty/tools/dotc/ast/tpd.scala | 8 ++++---- src/dotty/tools/dotc/ast/untpd.scala | 2 +- src/dotty/tools/dotc/core/Symbols.scala | 4 ++-- src/dotty/tools/dotc/core/Types.scala | 2 +- .../tools/dotc/transform/PostTyperTransformers.scala | 2 +- src/dotty/tools/dotc/transform/TreeTransform.scala | 6 +----- test/test/DeSugarTest.scala | 2 +- test/test/parsePackage.scala | 2 +- 9 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index e0e64f06a9c2..5eb9a6409bc5 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -1077,7 +1077,7 @@ object Trees { } } - abstract class TreeTransformer(val cpy: TreeCopier = inst.cpy) { + abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case Ident(name) => diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 4de98d8f889d..9d4ec21dec5e 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -334,10 +334,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { shallowFold[Option[tpd.Tree]](None)((accum, tree) => if (pred(tree)) Some(tree) else accum) def subst(from: List[Symbol], to: List[Symbol])(implicit ctx: Context): ThisTree = - new TreeMapper(typeMap = new ctx.SubstSymMap(from, to)).apply(tree) + new TreeTypeMap(typeMap = new ctx.SubstSymMap(from, to)).apply(tree) def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = - new TreeMapper(ownerMap = (sym => if (sym == from) to else sym)).apply(tree) + new TreeTypeMap(ownerMap = (sym => if (sym == from) to else sym)).apply(tree) def appliedToTypes(targs: List[Type])(implicit ctx: Context): Tree = if (targs.isEmpty) tree else TypeApply(tree, targs map (TypeTree(_))) @@ -347,7 +347,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def tpes: List[Type] = xs map (_.tpe) } - class TreeMapper(val typeMap: TypeMap = IdentityTypeMap, val ownerMap: Symbol => Symbol = identity _)(implicit ctx: Context) extends TreeTransformer { + class TreeTypeMap(val typeMap: TypeMap = IdentityTypeMap, val ownerMap: Symbol => Symbol = identity _)(implicit ctx: Context) extends TreeMap { override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = super.transform { tree.withType(typeMap(tree.tpe)) match { case bind: tpd.Bind => @@ -378,7 +378,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** The current tree map composed with a substitution [from -> to] */ def withSubstitution(from: List[Symbol], to: List[Symbol]) = - new TreeMapper( + new TreeTypeMap( typeMap andThen ((tp: Type) => tp.substSym(from, to)), ownerMap andThen (from zip to).toMap) } diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index aea34e08a096..4e55fe868e60 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -285,7 +285,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { } } - abstract class UntypedTreeTransformer(cpy: UntypedTreeCopier = untpd.cpy) extends TreeTransformer(cpy) { + abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case ModuleDef(mods, name, impl) => cpy.ModuleDef(tree, mods, name, transformSub(impl)) diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index aa0cbb5a937c..3fc8a4f2cc9c 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -19,7 +19,7 @@ import Annotations._ import util.Positions._ import StdNames._ import NameOps._ -import ast.tpd.{TreeMapper, Tree} +import ast.tpd.{TreeTypeMap, Tree} import Denotations.{ Denotation, SingleDenotation, MultiDenotation } import collection.mutable import io.AbstractFile @@ -278,7 +278,7 @@ trait Symbols { this: Context => else { val copies: List[Symbol] = for (original <- originals) yield newNakedSymbol[original.ThisName](original.coord) - val treeMap = new TreeMapper(typeMap, ownerMap) + val treeMap = new TreeTypeMap(typeMap, ownerMap) .withSubstitution(originals, copies) (originals, copies).zipped foreach {(original, copy) => val odenot = original.denot diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 9714f7eae3dd..6dda937d2c0b 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2169,7 +2169,7 @@ object Types { annot.derivedAnnotation(mapOver(annot.tree)) def mapOver(tree: Tree): Tree = - new TreeMapper(this).apply(tree) + new TreeTypeMap(this).apply(tree) /** Can be overridden. By default, only the prefix is mapped. */ protected def mapClassInfo(tp: ClassInfo): ClassInfo = diff --git a/src/dotty/tools/dotc/transform/PostTyperTransformers.scala b/src/dotty/tools/dotc/transform/PostTyperTransformers.scala index 066a733f9473..14e2cf35d175 100644 --- a/src/dotty/tools/dotc/transform/PostTyperTransformers.scala +++ b/src/dotty/tools/dotc/transform/PostTyperTransformers.scala @@ -12,7 +12,7 @@ import NameOps._ object PostTyperTransformers { - import tpd.{TreeTransformer => _, _} + import tpd._ /** A trait that's assumed by the transformers that run right after typer. diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 6cee17589fcd..ecbe8daaf167 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -7,6 +7,7 @@ import dotty.tools.dotc.ast.Trees._ import scala.annotation.tailrec object TreeTransforms { + import tpd._ /** The base class of tree transforms. For each kind of tree K, there are * two methods which can be overridden: @@ -47,8 +48,6 @@ object TreeTransforms { */ class TreeTransform(group: TreeTransformer, idx: Int) { - import tpd._ - def prepareForIdent(tree: Ident) = this def prepareForSelect(tree: Select) = this def prepareForThis(tree: This) = this @@ -390,11 +389,8 @@ object TreeTransforms { /** A group of tree transforms that are applied in sequence during the same phase */ abstract class TreeTransformer extends Phase { - import tpd.{TreeTransformer => _, _} - protected def transformations: Array[(TreeTransformer, Int) => TreeTransform] - override def run(implicit ctx: Context): Unit = { val curTree = ctx.compilationUnit.tpdTree val newTree = transform(curTree) diff --git a/test/test/DeSugarTest.scala b/test/test/DeSugarTest.scala index be4dcbd4fa47..f38706d671e9 100644 --- a/test/test/DeSugarTest.scala +++ b/test/test/DeSugarTest.scala @@ -22,7 +22,7 @@ class DeSugarTest extends ParserTest { val Expr = Mode(0) - object DeSugar extends UntypedTreeTransformer { + object DeSugar extends UntypedTreeMap { var curMode: Mode = Expr def withMode[T](mode: Mode)(op: => T) = { val saved = curMode diff --git a/test/test/parsePackage.scala b/test/test/parsePackage.scala index da3113f00466..7b0d16b2a038 100644 --- a/test/test/parsePackage.scala +++ b/test/test/parsePackage.scala @@ -11,7 +11,7 @@ object parsePackage extends ParserTest { var nodes = 0 - val transformer = new UntypedTreeTransformer { + val transformer = new UntypedTreeMap { override def transform(tree: Tree)(implicit ctx: Context): Tree = { nodes += 1 tree match { From 76ea699ac1a76ee6048d7fe8239d0a1126581429 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 15 Mar 2014 11:05:42 +0100 Subject: [PATCH 3/7] Renaming core.Transformer(s) -> core.DenotTransformer(s) To bring in line with TreeTransformer terminology. --- src/dotty/tools/dotc/core/Contexts.scala | 2 +- ...nsformers.scala => DenotTransformers.scala} | 18 +++++++++--------- src/dotty/tools/dotc/core/Denotations.scala | 4 ++-- src/dotty/tools/dotc/core/Periods.scala | 2 +- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- src/dotty/tools/dotc/core/Symbols.scala | 1 - .../tools/dotc/transform/SamplePhase.scala | 6 +++--- 7 files changed, 17 insertions(+), 18 deletions(-) rename src/dotty/tools/dotc/core/{Transformers.scala => DenotTransformers.scala} (78%) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 9be2e2f43dd9..0d6e341a6ce6 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -361,7 +361,7 @@ object Contexts { * compiler run. */ class ContextBase extends ContextState - with Transformers.TransformerBase + with DenotTransformers.DenotTransformerBase with Denotations.DenotationsBase with Phases.PhasesBase { diff --git a/src/dotty/tools/dotc/core/Transformers.scala b/src/dotty/tools/dotc/core/DenotTransformers.scala similarity index 78% rename from src/dotty/tools/dotc/core/Transformers.scala rename to src/dotty/tools/dotc/core/DenotTransformers.scala index 9b8bb65a49c5..f41f1d171457 100644 --- a/src/dotty/tools/dotc/core/Transformers.scala +++ b/src/dotty/tools/dotc/core/DenotTransformers.scala @@ -9,10 +9,10 @@ import Denotations._ import java.lang.AssertionError import dotty.tools.dotc.util.DotClass -object Transformers { +object DenotTransformers { - trait TransformerBase { self: ContextBase => - val infoTransformers = new TransformerGroup + trait DenotTransformerBase { self: ContextBase => + val denotTransformers = new DenotTransformerGroup } /** A transformer group contains a sequence of transformers, @@ -24,14 +24,14 @@ object Transformers { * full symbol denotations, refTransformers translate only symbol references * of type Unique/JointRefDenotation. */ - class TransformerGroup { + class DenotTransformerGroup { private val nxTransformer = - Array.fill[Transformer](MaxPossiblePhaseId + 1)(NoTransformer) + Array.fill[DenotTransformer](MaxPossiblePhaseId + 1)(NoTransformer) def nextTransformer(pid: PhaseId) = nxTransformer(pid) - def install(pid: PhaseId, transFn: TransformerGroup => Transformer): Unit = + def install(pid: PhaseId, transFn: DenotTransformerGroup => DenotTransformer): Unit = if ((pid > NoPhaseId) && (nxTransformer(pid).phaseId > pid)) { val trans = transFn(this) trans._phaseId = pid @@ -40,7 +40,7 @@ object Transformers { } /** A sentinel transformer object */ - object NoTransformer extends Transformer(this) { + object NoTransformer extends DenotTransformer(this) { _phaseId = MaxPossiblePhaseId + 1 override def lastPhaseId = phaseId - 1 // TODO JZ Probably off-by-N error here. MO: Don't think so: we want empty validity period. def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = @@ -49,9 +49,9 @@ object Transformers { } /** A transformer transforms denotations at a given phase */ - abstract class Transformer(group: TransformerGroup) extends DotClass { + abstract class DenotTransformer(group: DenotTransformerGroup) extends DotClass { - private[Transformers] var _phaseId: PhaseId = _ + private[DenotTransformers] var _phaseId: PhaseId = _ /** The phase at the start of which the denotations are transformed */ def phaseId: PhaseId = _phaseId diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index fce5b1a165b9..bd1aedd7507d 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -11,7 +11,7 @@ import Symbols._ import Types._ import Periods._ import Flags._ -import Transformers._ +import DenotTransformers._ import Decorators._ import transform.Erasure import printing.Texts._ @@ -518,7 +518,7 @@ object Denotations { } else { // not found, cur points to highest existing variant var startPid = cur.validFor.lastPhaseId + 1 - val transformer = ctx.infoTransformers.nextTransformer(startPid) + val transformer = ctx.denotTransformers.nextTransformer(startPid) next = transformer.transform(cur).syncWithParents if (next eq cur) startPid = cur.validFor.firstPhaseId diff --git a/src/dotty/tools/dotc/core/Periods.scala b/src/dotty/tools/dotc/core/Periods.scala index 31fde2dac96e..59251653114e 100644 --- a/src/dotty/tools/dotc/core/Periods.scala +++ b/src/dotty/tools/dotc/core/Periods.scala @@ -34,7 +34,7 @@ abstract class Periods extends DotClass { self: Context => */ def stablePeriod = { var first = phaseId - val transformers = base.infoTransformers + val transformers = base.denotTransformers val nxTrans = transformers.nextTransformer(first) while (first - 1 > NoPhaseId && (transformers.nextTransformer(first - 1) eq nxTrans)) { diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 7fd5cdacc1a9..08566e3db72d 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc package core import Periods._, Contexts._, Symbols._, Denotations._, Names._, NameOps._, Annotations._ -import Types._, Flags._, Decorators._, Transformers._, StdNames._, Scopes._ +import Types._, Flags._, Decorators._, DenotTransformers._, StdNames._, Scopes._ import NameOps._ import Scopes.Scope import collection.mutable diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 3fc8a4f2cc9c..ff70679b847a 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -3,7 +3,6 @@ package dotc package core import Periods._ -import Transformers._ import Names._ import Scopes._ import Flags._ diff --git a/src/dotty/tools/dotc/transform/SamplePhase.scala b/src/dotty/tools/dotc/transform/SamplePhase.scala index 5a6578bea1de..137ab2eda67f 100644 --- a/src/dotty/tools/dotc/transform/SamplePhase.scala +++ b/src/dotty/tools/dotc/transform/SamplePhase.scala @@ -2,16 +2,16 @@ package dotty.tools.dotc package transform import TreeTransforms._ +import core.DenotTransformers._ import core.Denotations._ import core.Contexts._ import ast.Trees._ import ast.tpd.{Apply, Tree, cpy} -import core.Transformers._ class SamplePhase extends TreeTransformer { def init(implicit ctx: Context) = { - ctx.base.infoTransformers.install(id, new UncurryInfoTransform(_)) + ctx.base.denotTransformers.install(id, new UncurryDenotTransform(_)) } def name = "sample" @@ -20,7 +20,7 @@ class SamplePhase extends TreeTransformer { } -class UncurryInfoTransform(group: TransformerGroup) extends Transformer(group) { +class UncurryDenotTransform(group: DenotTransformerGroup) extends DenotTransformer(group) { def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ??? From 1dbf020c43639b0c37e9005f2692871d39676ac7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 15 Mar 2014 17:10:24 +0100 Subject: [PATCH 4/7] Refactored denotation transformers Many small and large changes. Added samplePhase to demonstrate functionality. To test functioning, run the compiler with args tests/pos/uncurry.scala -Ylog:sample,terminal --- src/dotty/tools/dotc/Compiler.scala | 4 +- src/dotty/tools/dotc/Run.scala | 2 +- src/dotty/tools/dotc/config/Printers.scala | 2 +- src/dotty/tools/dotc/core/Contexts.scala | 5 +- .../tools/dotc/core/DenotTransformers.scala | 44 ++------------- src/dotty/tools/dotc/core/Denotations.scala | 11 ++-- src/dotty/tools/dotc/core/Periods.scala | 14 ++--- src/dotty/tools/dotc/core/Phases.scala | 53 +++++++++++++------ .../tools/dotc/transform/SamplePhase.scala | 49 +++++++++++------ .../CreateCompanionObjectsTest.scala | 8 +-- tests/pos/uncurry.scala | 6 +++ 11 files changed, 106 insertions(+), 92 deletions(-) create mode 100644 tests/pos/uncurry.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 9844557f5052..84e11e7ed848 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -11,7 +11,9 @@ import dotty.tools.dotc.core.Phases.Phase class Compiler { - def phases: List[Phase] = List(new FrontEnd) + def phases: List[Phase] = List( + new FrontEnd, + new transform.SamplePhase) var runId = 1 def nextRunId = { runId += 1; runId } diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index 160390c4f47e..89ca45071b05 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -30,7 +30,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { def compileSources(sources: List[SourceFile]) = Stats.monitorHeartBeat { if (sources forall (_.exists)) { units = sources map (new CompilationUnit(_)) - for (phase <- ctx.allPhases) { + for (phase <- ctx.allPhases.init) { if (!ctx.reporter.hasErrors) phase.runOn(units) } diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala index e236e34e0608..853049b047b2 100644 --- a/src/dotty/tools/dotc/config/Printers.scala +++ b/src/dotty/tools/dotc/config/Printers.scala @@ -23,5 +23,5 @@ object Printers { val completions = noPrinter val gadts = noPrinter val incremental = noPrinter - + val config = noPrinter } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 0d6e341a6ce6..38784ec0c61e 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -325,6 +325,7 @@ object Contexts { def withProperty(prop: (String, Any)): this.type = withMoreProperties(moreProperties + prop) def withPhase(pid: PhaseId): this.type = withPeriod(Period(runId, pid)) + def withPhase(phase: Phase): this.type = withPhase(phase.id) def withSetting[T](setting: Setting[T], value: T): this.type = withSettings(setting.updateIn(sstate, value)) @@ -361,7 +362,6 @@ object Contexts { * compiler run. */ class ContextBase extends ContextState - with DenotTransformers.DenotTransformerBase with Denotations.DenotationsBase with Phases.PhasesBase { @@ -460,6 +460,9 @@ object Contexts { /** Phases by id */ private[core] var phases: Array[Phase] = _ + /** Next denotation transformer id */ + private[core] var nextTransformerId: Array[Int] = _ + // Printers state /** Number of recursive invocations of a show method on cuyrrent stack */ private[dotc] var toTextRecursions = 0 diff --git a/src/dotty/tools/dotc/core/DenotTransformers.scala b/src/dotty/tools/dotc/core/DenotTransformers.scala index f41f1d171457..be85b58772e0 100644 --- a/src/dotty/tools/dotc/core/DenotTransformers.scala +++ b/src/dotty/tools/dotc/core/DenotTransformers.scala @@ -6,62 +6,26 @@ import SymDenotations._ import Contexts._ import Types._ import Denotations._ +import Phases._ import java.lang.AssertionError import dotty.tools.dotc.util.DotClass object DenotTransformers { - trait DenotTransformerBase { self: ContextBase => - val denotTransformers = new DenotTransformerGroup - } - /** A transformer group contains a sequence of transformers, * ordered by the phase where they apply. Transformers are added * to a group via `install`. - * - * There are two transformerGroups in a context base: - * symTransformers and refTransformers. symTransformers translate - * full symbol denotations, refTransformers translate only symbol references - * of type Unique/JointRefDenotation. */ - class DenotTransformerGroup { - - private val nxTransformer = - Array.fill[DenotTransformer](MaxPossiblePhaseId + 1)(NoTransformer) - - def nextTransformer(pid: PhaseId) = nxTransformer(pid) - - def install(pid: PhaseId, transFn: DenotTransformerGroup => DenotTransformer): Unit = - if ((pid > NoPhaseId) && (nxTransformer(pid).phaseId > pid)) { - val trans = transFn(this) - trans._phaseId = pid - nxTransformer(pid) = transFn(this) - install(pid - 1, transFn) - } - - /** A sentinel transformer object */ - object NoTransformer extends DenotTransformer(this) { - _phaseId = MaxPossiblePhaseId + 1 - override def lastPhaseId = phaseId - 1 // TODO JZ Probably off-by-N error here. MO: Don't think so: we want empty validity period. - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = - unsupported("transform") - } - } /** A transformer transforms denotations at a given phase */ - abstract class DenotTransformer(group: DenotTransformerGroup) extends DotClass { - - private[DenotTransformers] var _phaseId: PhaseId = _ - - /** The phase at the start of which the denotations are transformed */ - def phaseId: PhaseId = _phaseId + trait DenotTransformer extends Phase { /** The last phase during which the transformed denotations are valid */ - def lastPhaseId = group.nextTransformer(phaseId).phaseId - 1 + def lastPhaseId(implicit ctx: Context) = ctx.nextTransformerId(id + 1) /** The validity period of the transformer in the given context */ def validFor(implicit ctx: Context): Period = - Period(ctx.runId, phaseId, lastPhaseId) + Period(ctx.runId, id, lastPhaseId) /** The transformation method */ def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index bd1aedd7507d..b35647eadedd 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -390,7 +390,6 @@ object Denotations { if ((symbol eq this.symbol) && (info eq this.info)) this else newLikeThis(symbol, info) - def orElse(that: => SingleDenotation) = if (this.exists) this else that def altsWith(p: Symbol => Boolean): List[SingleDenotation] = @@ -518,7 +517,8 @@ object Denotations { } else { // not found, cur points to highest existing variant var startPid = cur.validFor.lastPhaseId + 1 - val transformer = ctx.denotTransformers.nextTransformer(startPid) + val transformer = ctx.phases(startPid - 1).asInstanceOf[DenotTransformer] + //println(s"transforming with $transformer") next = transformer.transform(cur).syncWithParents if (next eq cur) startPid = cur.validFor.firstPhaseId @@ -527,20 +527,23 @@ object Denotations { case next: ClassDenotation => next.resetFlag(Frozen) case _ => } + next.nextInRun = cur.nextInRun cur.nextInRun = next cur = next } cur.validFor = Period( currentPeriod.runId, startPid, transformer.lastPhaseId) + //println(s"new denot: $cur, valid for ${cur.validFor}") } } else { - // currentPeriod < valid; in this case a version must exist + // currentPeriod < end of valid; in this case a version must exist // but to be defensive we check for infinite loop anyway var cnt = 0 while (!(cur.validFor contains currentPeriod)) { + //println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}") cur = cur.nextInRun cnt += 1 - assert(cnt <= MaxPossiblePhaseId, "seems to be a loop in Denotations") + assert(cnt <= MaxPossiblePhaseId, s"seems to be a loop in Denotations for $this, currentPeriod = $currentPeriod") } } cur diff --git a/src/dotty/tools/dotc/core/Periods.scala b/src/dotty/tools/dotc/core/Periods.scala index 59251653114e..1c9fbf0c6fb4 100644 --- a/src/dotty/tools/dotc/core/Periods.scala +++ b/src/dotty/tools/dotc/core/Periods.scala @@ -27,20 +27,16 @@ abstract class Periods extends DotClass { self: Context => /** The period containing the current period where denotations do not change. * We compute this by taking as first phase the first phase less or equal to - * the current phase that has the same "nextTransformer". As last phase - * we take the phaseId of the nextTransformer - 1. This has the advantage that - * it works even if no transformer is installed other than the sentinel - * NoTransformer, which is always installed automatically. + * the current phase that has the same "nextTransformerId". As last phase + * we take the next transformer id following the current phase. */ def stablePeriod = { var first = phaseId - val transformers = base.denotTransformers - val nxTrans = transformers.nextTransformer(first) - while (first - 1 > NoPhaseId && - (transformers.nextTransformer(first - 1) eq nxTrans)) { + val nxTrans = ctx.base.nextTransformerId(first) + while (first - 1 > NoPhaseId && (ctx.base.nextTransformerId(first - 1) == nxTrans)) { first -= 1 } - Period(runId, first, nxTrans.phaseId - 1) + Period(runId, first, nxTrans) } } diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 14cab98215b9..81ab7acbf0dd 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -4,6 +4,9 @@ package core import Periods._ import Contexts._ import util.DotClass +import DenotTransformers._ +import Denotations._ +import config.Printers._ trait Phases { self: Context => import Phases._ @@ -18,6 +21,8 @@ trait Phases { self: Context => def atPhase[T](phase: Phase)(op: Context => T): T = atPhase(phase.id)(op) + def atNextPhase[T](op: Context => T): T = atPhase(phase.next)(op) + def atPhaseNotLaterThan[T](limit: Phase)(op: Context => T): T = if (!limit.exists || phase <= limit) op(this) else atPhase(limit)(op) @@ -29,12 +34,13 @@ object Phases { trait PhasesBase { this: ContextBase => - def allPhases = phases.tail + def allPhases = phases.tail // drop NoPhase at beginning object NoPhase extends Phase { override def exists = false def name = "" def run(implicit ctx: Context): Unit = unsupported("run") + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = unsupported("transform") } object SomePhase extends Phase { @@ -42,14 +48,38 @@ object Phases { def run(implicit ctx: Context): Unit = unsupported("run") } + /** A sentinel transformer object */ + class TerminalPhase extends DenotTransformer { + def name = "terminal" + def run(implicit ctx: Context): Unit = unsupported("run") + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = + unsupported("transform") + override def lastPhaseId(implicit ctx: Context) = id + } + def phaseNamed(name: String) = phases.find(_.name == name).getOrElse(NoPhase) /** Use the following phases in the order they are given. * The list should never contain NoPhase. */ - def usePhases(phases: List[Phase]) = - this.phases = (NoPhase :: phases).toArray + def usePhases(phases: List[Phase]) = { + this.phases = (NoPhase :: phases ::: new TerminalPhase :: Nil).toArray + this.nextTransformerId = new Array[Int](this.phases.length) + var i = 0 + while (i < this.phases.length) { + this.phases(i)._id = i + i += 1 + } + var lastTransformerId = i + while (i > 0) { + i -= 1 + if (this.phases(i).isInstanceOf[DenotTransformer]) lastTransformerId = i + nextTransformerId(i) = lastTransformerId + } + config.println(s"Phases = ${this.phases.deep}") + config.println(s"nextTransformId = ${nextTransformerId.deep}") + } final val typerName = "typer" final val refchecksName = "refchecks" @@ -69,7 +99,7 @@ object Phases { def run(implicit ctx: Context): Unit def runOn(units: List[CompilationUnit])(implicit ctx: Context): Unit = - for (unit <- units) run(ctx.fresh.withCompilationUnit(unit)) + for (unit <- units) run(ctx.fresh.withPhase(this).withCompilationUnit(unit)) def description: String = name @@ -77,22 +107,13 @@ object Phases { def exists: Boolean = true - private[this] var idCache = -1 + private[Phases] var _id = -1 /** The sequence position of this phase in the given context where 0 * is reserved for NoPhase and the first real phase is at position 1. - * Returns -1 if the phase is not installed in the context. + * -1 if the phase is not installed in the context. */ - def id(implicit ctx: Context) = { - val id = idCache - val phases = ctx.phases - if (idCache >= 0 && idCache < phases.length && (phases(idCache) eq this)) - id - else { - idCache = phases indexOf this - idCache - } - } + def id = _id final def <= (that: Phase)(implicit ctx: Context) = exists && id <= that.id diff --git a/src/dotty/tools/dotc/transform/SamplePhase.scala b/src/dotty/tools/dotc/transform/SamplePhase.scala index 137ab2eda67f..ed4ac113f49e 100644 --- a/src/dotty/tools/dotc/transform/SamplePhase.scala +++ b/src/dotty/tools/dotc/transform/SamplePhase.scala @@ -4,33 +4,52 @@ package transform import TreeTransforms._ import core.DenotTransformers._ import core.Denotations._ +import core.SymDenotations._ import core.Contexts._ +import core.Types._ import ast.Trees._ import ast.tpd.{Apply, Tree, cpy} -class SamplePhase extends TreeTransformer { - - def init(implicit ctx: Context) = { - ctx.base.denotTransformers.install(id, new UncurryDenotTransform(_)) - } - +class SamplePhase extends TreeTransformer with DenotTransformer { def name = "sample" - def transformations = Array(new UncurryTreeTransform(_, _)) -} - -class UncurryDenotTransform(group: DenotTransformerGroup) extends DenotTransformer(group) { - - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ??? + def uncurry(tp: Type)(implicit ctx: Context): Type = tp match { + case tp @ MethodType(pnames1, ptypes1) => + tp.resultType match { + case rt @ MethodType(pnames2, ptypes2) => + tp.derivedMethodType(pnames1 ++ pnames2, ptypes1 ++ ptypes2, rt.resultType) + case _ => + tp + } + case tp: PolyType => + tp.derivedPolyType(tp.paramNames, tp.paramBounds, uncurry(tp.resultType)) + case _ => + tp + } + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = { + val info1 = uncurry(ref.info) + if (info1 eq ref.info) ref + else ref match { + case ref: SymDenotation => ref.copySymDenotation(info = info1) + case _ => ref.derivedSingleDenotation(ref.symbol, info1) + } + } } class UncurryTreeTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - tree match { - case Apply(fn, args) => cpy.Apply(tree, fn, args ++ tree.args) + ctx.traceIndented(s"transforming ${tree.show}", show = true) { + tree.fun match { + case Apply(fn, args) => + def showType(implicit ctx: Context) = + ctx.log(s"at ${ctx.phase} ${fn.symbol} has type ${fn.symbol.info.widen.show}") + showType + ctx.atNextPhase(showType(_)) + showType + cpy.Apply(tree, fn, args ++ tree.args) case _ => tree - } + }} } \ No newline at end of file diff --git a/test/test/transform/CreateCompanionObjectsTest.scala b/test/test/transform/CreateCompanionObjectsTest.scala index 95a84f4e02e9..e12e46496ea0 100644 --- a/test/test/transform/CreateCompanionObjectsTest.scala +++ b/test/test/transform/CreateCompanionObjectsTest.scala @@ -22,7 +22,7 @@ class CreateCompanionObjectsTest extends DottyTest { import tpd._ - @Test + //@Test def shouldCreateNonExistingObjectsInPackage = checkCompile("frontend", "class A{} ") { (tree, context) => implicit val ctx = context @@ -46,7 +46,7 @@ class CreateCompanionObjectsTest extends DottyTest { ) } - @Test + //@Test def shouldCreateNonExistingObjectsInBlock = checkCompile("frontend", "class D {def p = {class A{}; 1}} ") { (tree, context) => implicit val ctx = context @@ -68,7 +68,7 @@ class CreateCompanionObjectsTest extends DottyTest { ) } - @Test + //@Test def shouldCreateNonExistingObjectsInTemplate = checkCompile("frontend", "class D {class A{}; } ") { (tree, context) => implicit val ctx = context @@ -90,7 +90,7 @@ class CreateCompanionObjectsTest extends DottyTest { ) } - @Test + //@Test def shouldCreateOnlyIfAskedFor = checkCompile("frontend", "class DONT {class CREATE{}; } ") { (tree, context) => implicit val ctx = context diff --git a/tests/pos/uncurry.scala b/tests/pos/uncurry.scala new file mode 100644 index 000000000000..a11a0652d7cf --- /dev/null +++ b/tests/pos/uncurry.scala @@ -0,0 +1,6 @@ +object UnCurryTest { + + def f(x: Int)(y: Int) = x + y + + f(1)(2) +} From 46856320f4e21d94b3c5c29a921efac40e12421f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 15 Mar 2014 22:59:58 +0100 Subject: [PATCH 5/7] Re-enabled and renamed tests CreateCompanionObjectTests were re-enabled. They failed in a previous version but succeed again in the latest commit. Also uncurry.scala got renamed to curried.scala. --- test/test/transform/CreateCompanionObjectsTest.scala | 8 ++++---- tests/pos/{uncurry.scala => curried.scala} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename tests/pos/{uncurry.scala => curried.scala} (100%) diff --git a/test/test/transform/CreateCompanionObjectsTest.scala b/test/test/transform/CreateCompanionObjectsTest.scala index e12e46496ea0..95a84f4e02e9 100644 --- a/test/test/transform/CreateCompanionObjectsTest.scala +++ b/test/test/transform/CreateCompanionObjectsTest.scala @@ -22,7 +22,7 @@ class CreateCompanionObjectsTest extends DottyTest { import tpd._ - //@Test + @Test def shouldCreateNonExistingObjectsInPackage = checkCompile("frontend", "class A{} ") { (tree, context) => implicit val ctx = context @@ -46,7 +46,7 @@ class CreateCompanionObjectsTest extends DottyTest { ) } - //@Test + @Test def shouldCreateNonExistingObjectsInBlock = checkCompile("frontend", "class D {def p = {class A{}; 1}} ") { (tree, context) => implicit val ctx = context @@ -68,7 +68,7 @@ class CreateCompanionObjectsTest extends DottyTest { ) } - //@Test + @Test def shouldCreateNonExistingObjectsInTemplate = checkCompile("frontend", "class D {class A{}; } ") { (tree, context) => implicit val ctx = context @@ -90,7 +90,7 @@ class CreateCompanionObjectsTest extends DottyTest { ) } - //@Test + @Test def shouldCreateOnlyIfAskedFor = checkCompile("frontend", "class DONT {class CREATE{}; } ") { (tree, context) => implicit val ctx = context diff --git a/tests/pos/uncurry.scala b/tests/pos/curried.scala similarity index 100% rename from tests/pos/uncurry.scala rename to tests/pos/curried.scala From 5cbd2fbc8409b446f8751792b006693e1d091055 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 14 Mar 2014 12:56:11 +0100 Subject: [PATCH 6/7] LazyVals phase. Creates accessors for lazy vals: 1) lazy local vals are rewritten to dotty.runtime.Lazy*** holders 2) for a non-volatile field lazy val create a non-thread-safe accessor and flag: 2.a) if lazy val type indicates that val is not nullable, uses null value as a flag 2.b) else uses boolean flag for sake of performance, method size, and allowing more jvm optimizations 3) for a volatile field lazy val use double locking scheme, that guaranties no spurious deadlocks, using long bits as bitmaps and creating companion objects to store offsets needed for unsafe methods. Conflicts: test/dotc/tests.scala --- src/dotty/runtime/LazyHolders.scala | 40 ++ src/dotty/runtime/LazyVals.scala | 79 ++++ src/dotty/tools/dotc/Compiler.scala | 12 +- src/dotty/tools/dotc/core/Definitions.scala | 2 + src/dotty/tools/dotc/core/StdNames.scala | 1 + .../transform/CreateCompanionObjects.scala | 2 +- src/dotty/tools/dotc/transform/LazyVals.scala | 377 ++++++++++++++++++ .../tools/dotc/transform/TreeTransform.scala | 8 +- test/test/transform/LazyValsTest.scala | 350 ++++++++++++++++ 9 files changed, 864 insertions(+), 7 deletions(-) create mode 100644 src/dotty/runtime/LazyHolders.scala create mode 100644 src/dotty/runtime/LazyVals.scala create mode 100644 src/dotty/tools/dotc/transform/LazyVals.scala create mode 100644 test/test/transform/LazyValsTest.scala diff --git a/src/dotty/runtime/LazyHolders.scala b/src/dotty/runtime/LazyHolders.scala new file mode 100644 index 000000000000..86c5d34ecae4 --- /dev/null +++ b/src/dotty/runtime/LazyHolders.scala @@ -0,0 +1,40 @@ +package dotty.runtime + +/** + * Classes used as holders for local lazy vals + */ +class LazyInt(init: => Int) { + lazy val value = init +} + +class LazyLong(init: => Long) { + lazy val value = init +} + +class LazyBoolean(init: => Boolean) { + lazy val value = init +} + +class LazyDouble(init: => Double) { + lazy val value = init +} + +class LazyFloat(init: => Float) { + lazy val value = init +} + +class LazyByte(init: => Byte) { + lazy val value = init +} + +class LazyRef(init: => AnyRef) { + lazy val value = init +} + +class LazyShort(init: => Short) { + lazy val value = init +} + +class LazyChar(init: => Char) { + lazy val value = init +} diff --git a/src/dotty/runtime/LazyVals.scala b/src/dotty/runtime/LazyVals.scala new file mode 100644 index 000000000000..4130d4d60335 --- /dev/null +++ b/src/dotty/runtime/LazyVals.scala @@ -0,0 +1,79 @@ +package dotty.runtime + +import scala.annotation.tailrec + +/** + * Helper methods used in thread-safe lazy vals. + */ +object LazyVals { + private val unsafe = scala.concurrent.util.Unsafe.instance + + final val BITS_PER_LAZY_VAL = 2 + final val LAZY_VAL_MASK = 3 + + @inline def STATE(cur: Long, ord: Long) = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK + @inline def CAS(t: Object, offset: Long, e: Long, v: Long, ord: Int) = { + val mask = ~(LAZY_VAL_MASK << ord * BITS_PER_LAZY_VAL) + val n = (e & mask) | (v << (ord * BITS_PER_LAZY_VAL)) + compareAndSet(t, offset, e, n) + } + @inline def setFlag(t: Object, offset: Long, v: Int, ord: Int) = { + var retry = true + while (retry) { + val cur = get(t, offset) + if (STATE(cur, ord) == 1) retry = CAS(t, offset, cur, v, ord) + else { + // cur == 2, somebody is waiting on monitor + if (CAS(t, offset, cur, v, ord)) { + val monitor = getMonitor(t, ord) + monitor.synchronized { + monitor.notifyAll() + } + retry = false + } + } + } + } + @inline def wait4Notification(t: Object, offset: Long, cur: Long, ord: Int) = { + var retry = true + while (retry) { + val cur = get(t, offset) + val state = STATE(cur, ord) + if (state == 1) CAS(t, offset, cur, 2, ord) + else if (state == 2) { + val monitor = getMonitor(t, ord) + monitor.synchronized { + monitor.wait() + } + } + else retry = false + } + } + + @inline def compareAndSet(t: Object, off: Long, e: Long, v: Long) = unsafe.compareAndSwapLong(t, off, e, v) + @inline def get(t: Object, off: Long) = unsafe.getLongVolatile(t, off) + + val processors: Int = java.lang.Runtime.getRuntime.availableProcessors() + val base: Int = 8 * processors * processors + val monitors: Array[Object] = (0 to base).map { + x => new Object() + }.toArray + + @inline def getMonitor(obj: Object, fieldId: Int = 0) = { + var id = (java.lang.System.identityHashCode(obj) + fieldId) % base + if (id < 0) id += base + monitors(id) + } + + @inline def getOffset(obj: Object, name: String) = unsafe.objectFieldOffset(obj.getClass.getDeclaredField(name)) + + object Names { + final val state = "STATE" + final val cas = "CAS" + final val setFlag = "setFlag" + final val wait4Notification = "wait4Notification" + final val compareAndSet = "compareAndSet" + final val get = "get" + final val getOffset = "getOffset" + } +} diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 84e11e7ed848..132a25d0bb49 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -8,15 +8,19 @@ import Symbols._ import typer.{FrontEnd, Typer, Mode, ImportInfo} import reporting.ConsoleReporter import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.transform.{LazyValsCreateCompanionObjects, LazyValTranformContext} +import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer} +import dotty.tools.dotc.transform.PostTyperTransformers.PostTyperTransformer class Compiler { - def phases: List[Phase] = List( - new FrontEnd, - new transform.SamplePhase) + + def phases: List[Phase] = List(new FrontEnd, new transform.SamplePhase) var runId = 1 - def nextRunId = { runId += 1; runId } + def nextRunId = { + runId += 1; runId + } def rootContext(implicit ctx: Context): Context = { ctx.definitions.init(ctx) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index ef16a970dfa4..5f6698b33aaa 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -233,6 +233,7 @@ class Definitions { lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault") lazy val ThrowsAnnot = ctx.requiredClass("scala.throws") lazy val UncheckedAnnot = ctx.requiredClass("scala.unchecked") + lazy val VolatileAnnot = ctx.requiredClass("scala.volatile") // convenient one-parameter method types def methOfAny(tp: Type) = MethodType(List(AnyType), tp) @@ -266,6 +267,7 @@ class Definitions { def JavaRepeatedParamType = JavaRepeatedParamClass.typeRef def ThrowableType = ThrowableClass.typeRef def OptionType = OptionClass.typeRef + def VolatileAnnotType = VolatileAnnot.typeRef def ClassType(arg: Type)(implicit ctx: Context) = { val ctype = ClassClass.typeRef diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index e7c04e846503..0cbcfa5a74b9 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -221,6 +221,7 @@ object StdNames { val FAKE_LOCAL_THIS: N = "this$" val IMPLCLASS_CONSTRUCTOR: N = "$init$" val LAZY_LOCAL: N = "$lzy" + val LAZY_FIELD_OFFSET: N = "OFFSET$" val LAZY_SLOW_SUFFIX: N = "$lzycompute" val LOCAL_SUFFIX: N = " " val UNIVERSE_BUILD_PREFIX: N = "$u.build." diff --git a/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala b/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala index dcbcc3b548c5..8560b6f6d038 100644 --- a/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala +++ b/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala @@ -25,7 +25,7 @@ abstract class CreateCompanionObjects(group: TreeTransformer, idx: Int) extends /** Given class definition should return true if companion object creation should be enforced */ - def predicate(cls: TypeDef): Boolean + def predicate(cls: TypeDef)(implicit ctx:Context): Boolean override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = { @tailrec diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala new file mode 100644 index 000000000000..ffc2096f1783 --- /dev/null +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -0,0 +1,377 @@ +package dotty.tools.dotc.transform + +import scala.collection.mutable +import dotty.tools.dotc._ +import core._ +import Contexts._ +import Symbols._ +import Decorators._ +import NameOps._ +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, TreeTransform} +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.{untpd, tpd} +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core.Types.MethodType +import dotty.tools.dotc.core.Names.Name +import dotty.runtime.LazyVals +import scala.collection.mutable.ListBuffer +import dotty.tools.dotc.core.Denotations.SingleDenotation +import dotty.tools.dotc.core.SymDenotations.SymDenotation +import dotty.tools.dotc.core.DenotTransformers.DenotTransformer + + +class LazyValsCreateCompanionObjects extends CreateCompanionObjects { + import tpd._ + + + override def name: String = "lazyValsModules" + + /** Companion classes are required to hold offsets for volatile lazy vals */ + override def predicate(forClass: tpd.TypeDef)(implicit ctx: Context): Boolean = { + (!(forClass.symbol is Flags.Module)) && forClass.rhs.isInstanceOf[Template] && { + val body = forClass.rhs.asInstanceOf[Template].body + body.exists { + case x: ValDef => + (x.mods is Flags.Lazy) && x.mods.annotations.exists(_.tpe == defn.VolatileAnnotType) + case _ => false + } + } + } +} +class LazyValTranformContext { + + import tpd._ + + + def transformer = new LazyValsTransform + + /** 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) + val appendOffsetDefs = mutable.Map.empty[Name, OffsetInfo] + + val infoTransformerNewDefinitions = mutable.Map.empty[ClassSymbol, ListBuffer[Symbol]] + + def addSym(owner: ClassSymbol, sym: Symbol) { + infoTransformerNewDefinitions.get(owner) match { + case Some(x) => x += sym + case None => infoTransformerNewDefinitions.put(owner, ListBuffer(sym)) + } + } + + class LazyValsTransform extends TreeTransform with DenotTransformer { + + override def name: String = "LazyVals" + + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = { + ref match { + case ref: SymDenotation if ref.symbol.isClass => + val oldSym = ref.symbol.asClass + infoTransformerNewDefinitions.get(oldSym) match { + case Some(x) => + val den = ref.copySymDenotation() + den.resetFlag(Flags.Frozen) + x.foreach(stat => den.asClass.enter(stat)) + den + case None => + ref + } + case _ => ref + } + } + + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (!(tree.mods is Flags.Lazy)) tree + else { + val isField = tree.symbol.owner.isClass + val isVolatile = tree.mods.annotations.exists(_.tpe == defn.VolatileAnnotType) + + if (isField) { + if (isVolatile) transformFieldValDefVolatile(tree) + else transformFieldValDefNonVolatile(tree) + } + else transformLocalValDef(tree) + } + } + + /** Append offset fields to companion objects + */ + override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (!tree.symbol.isClass) tree + else { + appendOffsetDefs.get(tree.symbol.name) match { + case None => tree + case Some(data) => + val template = tree.rhs.asInstanceOf[Template] + ClassDef(tree.symbol.asClass, template.constr, data.defs.mapConserve(transformFollowingDeep) ::: template.body) + } + } + } + /** Replace a local lazy val inside a method, + * with a LazyHolder from + * dotty.runtime(eg dotty.runtime.LazyInt) + */ + def transformLocalValDef(x: ValDef)(implicit ctx: Context) = x match { + case x@ValDef(mods, name, tpt, rhs) => + val valueInitter = rhs + val holderName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName + val tpe = x.tpe.widen + + val holderType = + if (tpe =:= defn.IntType) "LazyInt" + else if (tpe =:= defn.LongType) "LazyLong" + else if (tpe =:= defn.BooleanType) "LazyBoolean" + else if (tpe =:= defn.FloatType) "LazyFloat" + else if (tpe =:= defn.DoubleType) "LazyDouble" + else if (tpe =:= defn.ByteType) "LazyByte" + else if (tpe =:= defn.CharType) "LazyChar" + else if (tpe =:= defn.ShortType) "LazyShort" + else "LazyRef" + + val holderImpl = ctx.requiredClass("dotty.runtime." + holderType) + + val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, Flags.Synthetic, holderImpl.typeRef, coord = x.symbol.coord) + val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List(valueInitter))) + val methodTree = DefDef(x.symbol.asTerm, Select(Ident(holderSymbol.termRef), "value".toTermName)) + ctx.debuglog(s"found a lazy val ${x.show},\n rewrote with ${holderTree.show}") + Thicket(holderTree, methodTree) + } + + /** Create non-threadsafe lazy accessor equivalent to such code + * def methodSymbol() = { + * if (flag) target + * else { + * target = rhs + * flag = true + * target + * } + * } + */ + + def mkNonThreadSafeDef(target: Symbol, flag: Symbol, rhs: Tree)(implicit ctx: Context) = { + val cond = Ident(flag.termRef) + val exp = Ident(target.termRef) + val setFlag = Assign(cond, Literal(Constants.Constant(true))) + val setTarget = Assign(exp, rhs) + val init = Block(List(setFlag, setTarget), exp) + If(cond, exp, init) + } + + /** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code + * def methodSymbol() = { + * if (target eq null) { + * target = rhs + * target + * } else target + * } + */ + def mkDefNonThreadSafeNonNullable(target: Symbol, rhs: Tree)(implicit ctx: Context) = { + val cond = Apply(Select(Ident(target.termRef), "eq".toTermName), List(Literal(Constant(null)))) + val exp = Ident(target.termRef) + val setTarget = Assign(exp, rhs) + val init = Block(List(setTarget), exp) + If(cond, init, exp) + } + + def initValue(tpe: Types.Type)(implicit ctx: Context) = + if (tpe =:= defn.IntType) Constant(0) + else if (tpe =:= defn.LongType) Constant(0L) + else if (tpe =:= defn.BooleanType) Constant(false) + else if (tpe =:= defn.CharType) Constant('\u0000') + else if (tpe =:= defn.FloatType) Constant(0f) + else if (tpe =:= defn.DoubleType) Constant(0d) + else if (tpe =:= defn.ByteType) Constant(0.toByte) + else if (tpe =:= defn.ShortType) Constant(0.toShort) + else Constant(null) + + + def transformFieldValDefNonVolatile(x: ValDef)(implicit ctx: Context) = x match { + case x@ValDef(mods, name, tpt, rhs) if (mods is Flags.Lazy) => + val claz = x.symbol.owner.asClass + val tpe = x.tpe.widen + assert(!(mods is Flags.Mutable)) + val containerName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName + val containerSymbol = ctx.newSymbol(claz, containerName, (mods &~ Flags.Lazy & Flags.Synthetic).flags, tpe, coord = x.symbol.coord) + addSym(claz, containerSymbol) + + val containerTree = ValDef(containerSymbol, Literal(initValue(tpe))) + if (x.tpe.isNotNull && initValue(tpe).tag == Constants.NullTag) { + val slowPath = DefDef(x.symbol.asTerm, mkDefNonThreadSafeNonNullable(containerSymbol, rhs)) + Thicket(List(containerTree, slowPath)) + } + else { + val flagName = ctx.freshName(name.toString + StdNames.nme.BITMAP_PREFIX).toTermName + val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, Flags.Synthetic, defn.BooleanType) + val flag = ValDef(flagSymbol, Literal(Constants.Constant(false))) + val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(containerSymbol, flagSymbol, rhs)) + Thicket(List(containerTree, flag, slowPath)) + } + + } + + /** Create non-threadsafe lazy accessor equivalent to such code + * + * def methodSymbol(): Int = { + * val result: Int = 0 + * val retry: Boolean = true + * var flag: Long = 0L + * while retry do { + * flag = dotty.runtime.LazyVals.get(this, $claz.$OFFSET) + * dotty.runtime.LazyVals.STATE(flag, 0) match { + * case 0 => + * if dotty.runtime.LazyVals.CAS(this, $claz.$OFFSET, flag, 1, $ord) { + * try {result = rhs} catch { + * case x: Throwable => + * dotty.runtime.LazyVals.setFlag(this, $claz.$OFFSET, 0, $ord) + * throw x + * } + * $target = result + * dotty.runtime.LazyVals.setFlag(this, $claz.$OFFSET, 3, $ord) + * retry = false + * } + * case 1 => + * dotty.runtime.LazyVals.wait4Notification(this, $claz.$OFFSET, flag, $ord) + * case 2 => + * dotty.runtime.LazyVals.wait4Notification(this, $claz.$OFFSET, flag, $ord) + * case 3 => + * retry = false + * result = $target + * } + * } + * result + * } + */ + def mkThreadSafeDef(methodSymbol: TermSymbol, claz: ClassSymbol, ord: Int, target: Symbol, rhs: Tree, tp: Types.Type, offset: Tree, getFlag: Tree, stateMask: Tree, casFlag: Tree, setFlagState: Tree, waitOnLock: Tree)(implicit ctx: Context) = { + val initState = Literal(Constants.Constant(0)) + val computeState = Literal(Constants.Constant(1)) + val notifyState = Literal(Constants.Constant(2)) + val computedState = Literal(Constants.Constant(3)) + val flagSymbol = ctx.newSymbol(methodSymbol, "flag".toTermName, Flags.Mutable & Flags.Synthetic, defn.LongType) + val flagDef = ValDef(flagSymbol, Literal(Constant(0L))) + + val thiz = This(claz)(ctx.fresh.withOwner(claz)) + + val resultSymbol = ctx.newSymbol(methodSymbol, "result".toTermName, Flags.Mutable & Flags.Synthetic, tp) + val resultDef = ValDef(resultSymbol, Literal(initValue(tp.widen))) + + val retrySymbol = ctx.newSymbol(methodSymbol, "retry".toTermName, Flags.Mutable & Flags.Synthetic, defn.BooleanType) + val retryDef = ValDef(retrySymbol, Literal(Constants.Constant(true))) + + val whileCond = Ident(retrySymbol.termRef) + + val compute = { + val handlerSymbol = ctx.newSymbol(methodSymbol, "$anonfun".toTermName, Flags.Synthetic, + MethodType(List("x$1".toTermName), List(defn.ThrowableType), defn.IntType)) + + val handler = Closure(handlerSymbol, { + args => + val exception = args.head.head + val complete = Apply(setFlagState, List(thiz, offset, initState, Literal(Constant(ord)))) + Block(List(complete), Throw(exception)) + }) + + val compute = Assign(Ident(resultSymbol.termRef), rhs) + val tr = Try(compute, handler, EmptyTree) + val assign = Assign(Ident(target.termRef), Ident(resultSymbol.termRef)) + val complete = Apply(setFlagState, List(thiz, offset, computedState, Literal(Constant(ord)))) + val noRetry = Assign(Ident(retrySymbol.termRef), Literal(Constants.Constant(false))) + val body = If(Apply(casFlag, List(thiz, offset, Ident(flagSymbol.termRef), computeState, Literal(Constant(ord)))), + Block(tr :: assign :: complete :: noRetry :: Nil, Literal(Constant(()))), + Literal(Constant(()))) + + CaseDef(initState, EmptyTree, body) + } + + val waitFirst = { + val wait = Apply(waitOnLock, List(thiz, offset, Ident(flagSymbol.termRef), Literal(Constant(ord)))) + CaseDef(computeState, EmptyTree, wait) + } + + val waitSecond = { + val wait = Apply(waitOnLock, List(thiz, offset, Ident(flagSymbol.termRef), Literal(Constant(ord)))) + CaseDef(notifyState, EmptyTree, wait) + } + + val computed = { + val noRetry = Assign(Ident(retrySymbol.termRef), Literal(Constants.Constant(false))) + val result = Assign(Ident(resultSymbol.termRef), Ident(target.termRef)) + val body = Block(noRetry :: result :: Nil, Literal(Constant(()))) + CaseDef(computedState, EmptyTree, body) + } + + val cases = Match(Apply(stateMask, List(Ident(flagSymbol.termRef), Literal(Constant(ord)))), + List(compute, waitFirst, waitSecond, computed)) //todo: annotate with @switch + + val whileBody = Block(List(Assign(Ident(flagSymbol.termRef), Apply(getFlag, List(thiz, offset)))), cases) + val cycle = untpd.WhileDo(whileCond, whileBody).withTypeUnchecked(defn.UnitType) + DefDef(methodSymbol, Block(resultDef :: retryDef :: flagDef :: cycle :: Nil, Ident(resultSymbol.termRef))) + } + + def transformFieldValDefVolatile(x: ValDef)(implicit ctx: Context) = x match { + case x@ValDef(mods, name, tpt, rhs) if (mods is Flags.Lazy) => + assert(!(mods is Flags.Mutable)) + + val tpe = x.tpe.widen + val claz = x.symbol.owner.asClass + val thiz = This(claz)(ctx.fresh.withOwner(claz)) + val companion = claz.companionModule + val helperModule = ctx.requiredModule("dotty.runtime.LazyVals") + val getOffset = Select(Ident(helperModule.termRef), LazyVals.Names.getOffset.toTermName) + var offsetSymbol: TermSymbol = null + var flag: Tree = EmptyTree + var ord = 0 + + // compute or create appropriate offsetSymol, bitmap and bits used by current ValDef + appendOffsetDefs.get(companion.name.moduleClassName) match { + case Some(info) => + val flagsPerLong = 64 / LazyVals.BITS_PER_LAZY_VAL + info.ord += 1 + ord = info.ord % flagsPerLong + val id = info.ord / flagsPerLong + if(ord != 0) { // there are unused bits in already existing flag + offsetSymbol = companion.moduleClass.info.decl((StdNames.nme.LAZY_FIELD_OFFSET + id.toString).toTermName) + .suchThat(sym => (sym is Flags.Synthetic) && sym.isTerm) + .symbol.asTerm + } else { // need to create a new flag + offsetSymbol = ctx.newSymbol(companion.moduleClass, (StdNames.nme.LAZY_FIELD_OFFSET + id.toString).toTermName, Flags.Synthetic, defn.LongType).entered + val flagName = (StdNames.nme.BITMAP_PREFIX + id.toString).toTermName + val flagSymbol = ctx.newSymbol(claz, flagName, Flags.Synthetic, defn.LongType) + addSym(claz, flagSymbol) + flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) + val offsetTree = ValDef(offsetSymbol, Apply(getOffset, List(thiz, Literal(Constant(flagName.toString))))) + info.defs = offsetTree :: info.defs + } + + case None => + offsetSymbol = ctx.newSymbol(companion.moduleClass, (StdNames.nme.LAZY_FIELD_OFFSET + "0").toTermName, Flags.Synthetic, defn.LongType).entered + val flagName = (StdNames.nme.BITMAP_PREFIX + "0").toTermName + val flagSymbol = ctx.newSymbol(claz, flagName, Flags.Synthetic, defn.LongType) + addSym(claz, flagSymbol) + flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) + val offsetTree = ValDef(offsetSymbol, Apply(getOffset, List(thiz, Literal(Constant(flagName.toString))))) + 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, (mods &~ Flags.Lazy & Flags.Synthetic).flags, tpe, coord = x.symbol.coord) + addSym(claz, containerSymbol) + val containerTree = ValDef(containerSymbol, Literal(initValue(tpe))) + + val offset = Select(Ident(companion.termRef), offsetSymbol.name) + val getFlag = Select(Ident(helperModule.termRef), LazyVals.Names.get.toTermName) + val setFlag = Select(Ident(helperModule.termRef), LazyVals.Names.setFlag.toTermName) + val wait = Select(Ident(helperModule.termRef), LazyVals.Names.wait4Notification.toTermName) + val state = Select(Ident(helperModule.termRef), LazyVals.Names.state.toTermName) + val cas = Select(Ident(helperModule.termRef), LazyVals.Names.compareAndSet.toTermName) + + val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, rhs, x.tpe, offset, getFlag, state, cas, setFlag, wait) + if(flag eq EmptyTree) + Thicket(List(containerTree, accessor)) + else Thicket(List(containerTree, flag, accessor)) + } + + } +} + + + diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index ecbe8daaf167..3e29af0e4cd9 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -113,13 +113,16 @@ object TreeTransforms { def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees /** Transform tree using all transforms of current group (including this one) */ - def transform(tree: Tree)(implicit ctx: Context): Tree = group.transform(tree) + def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = group.transform(tree, info, 0) /** Transform subtree using all transforms following the current one in this group */ def transformFollowingDeep(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = group.transform(tree, info, idx + 1) /** Transform single node using all transforms following the current one in this group */ def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = group.transformSingle(tree, idx + 1) + + /** perform context-dependant initialization */ + def init(implicit ctx:Context): Unit = {} } val NoTransform = new TreeTransform(null, -1) @@ -402,7 +405,7 @@ object TreeTransforms { var nxCopied = false var result = info.transformers var resultNX = info.nx - var i = mutationPlan(cur) + var i = mutationPlan(0) // if TreeTransform.transform() method didn't exist we could have used mutationPlan(cur) val l = result.length var allDone = i < l while (i < l) { @@ -459,6 +462,7 @@ object TreeTransforms { def transform(t: Tree)(implicit ctx: Context): Tree = { val initialTransformations = transformations.zipWithIndex.map(x => x._1(this, x._2)) + initialTransformations.foreach(_.init) transform(t, new TransformerInfo(initialTransformations, new NXTransformations(initialTransformations)), 0) } diff --git a/test/test/transform/LazyValsTest.scala b/test/test/transform/LazyValsTest.scala new file mode 100644 index 000000000000..c14ee3e98da8 --- /dev/null +++ b/test/test/transform/LazyValsTest.scala @@ -0,0 +1,350 @@ +package test.transform + +import org.junit.Test +import test.DottyTest +import org.junit.Assert + +class LazyValsTest extends DottyTest { + + @Test + def localInt = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s = 1; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy int rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyInt)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyInt)]),),List(Literal(Constant(1)))))" + )) + } + } + /* + @Test + def localLong = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s = 1L; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy long rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyLong)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyLong)]),),List(Literal(Constant(1)))))" + )) + } + } + + @Test + def localFloat = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s = 1.0f; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy float rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyFloat)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyFloat)]),),List(Literal(Constant(1.0)))))" + )) + } + } + + @Test + def localDouble = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s = 1.0; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy double rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyDouble)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyDouble)]),),List(Literal(Constant(1.0)))))" + )) + } + } + + @Test + def localBoolean = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s = true; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy boolean rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyBoolean)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyBoolean)]),),List(Literal(Constant(true)))))" + )) + } + } + + @Test + def localChar = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s = 'a'; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy char rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyChar)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyChar)]),),List(Literal(Constant(a)))))" + )) + } + } + + @Test + def localByte = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s:Byte = 1; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy byte rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyByte)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyByte)]),),List(Literal(Constant(1)))))" + )) + } + } + + @Test + def localShort = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s:Short = 1; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy short rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyShort)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyShort)]),),List(Literal(Constant(1)))))" + )) + } + } + + @Test + def localRef = { + checkCompile("LazyVals", "class LocalLV { def m = { lazy val s = \"string\"; s }}"){ (tree, ctx) => + Assert.assertTrue("local lazy ref rewritten to class creation", tree.toString.contains( + "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class runtime),LazyRef)],Apply(Select(New(TypeTree[TypeRef(ThisType(module class runtime),LazyRef)]),),List(Literal(Constant(string)))))" + )) + } + } + + @Test + def fieldRef = { + checkCompile("LazyVals", "class LV { lazy val s = \"string\" }"){ (tree, ctx) => + Assert.assertTrue("field lazy int rewritten to class creation", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class lang),String)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(string)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def fieldInt = { + checkCompile("LazyVals", "class LV { lazy val s = 1 }"){ (tree, ctx) => + Assert.assertTrue("field lazy int rewritten", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Int)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(1)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def fieldLong = { + checkCompile("LazyVals", "class LV { lazy val s = 1L }"){ (tree, ctx) => + Assert.assertTrue("field lazy long rewritten", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Long)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(1)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def fieldShort = { + checkCompile("LazyVals", "class LV { lazy val s:Short = 1 }"){ (tree, ctx) => + Assert.assertTrue("field lazy short rewritten", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Short)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(1)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def fieldByte = { + checkCompile("LazyVals", "class LV { lazy val s:Byte = 1 }"){ (tree, ctx) => + Assert.assertTrue("field lazy byte rewritten", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Byte)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(1)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def fieldBoolean = { + checkCompile("LazyVals", "class LV { lazy val s = true }"){ (tree, ctx) => + Assert.assertTrue("field lazy boolean rewritten", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Boolean)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(true)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def fieldDouble = { + checkCompile("LazyVals", "class LV { lazy val s = 1.0 }"){ (tree, ctx) => + Assert.assertTrue("field lazy double rewritten", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Double)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(1.0)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def fieldFloat = { + checkCompile("LazyVals", "class LV { lazy val s = 1.0f }"){ (tree, ctx) => + Assert.assertTrue("field lazy float rewritten", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Float)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(1.0)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def fieldChar = { + checkCompile("LazyVals", "class LV { lazy val s = 'a' }"){ (tree, ctx) => + Assert.assertTrue("field lazy char rewritten", tree.toString.contains( + "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Char)],If(Ident(sbitmap$1),Ident(s$lzy1),Block(List(Assign(Ident(sbitmap$1),Literal(Constant(true))), Assign(Ident(s$lzy1),Literal(Constant(a)))),Ident(s$lzy1))))" + )) + } + } + + @Test + def volatileFieldRef = { + checkCompile("LazyVals", "class LV { @volatile lazy val s = \"a\" }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class lang),String)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(null))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(a))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class lang),String)],Literal(Constant(null))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatileFieldInt = { + checkCompile("LazyVals", "class LV { @volatile lazy val s = 1 }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Int)],Literal(Constant(0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatileFieldLong = { + checkCompile("LazyVals", "class LV { @volatile lazy val s = 1L }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Long)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatileFieldFloat = { + checkCompile("LazyVals", "class LV { @volatile lazy val s = 1.0f }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Float)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0.0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1.0))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Float)],Literal(Constant(0.0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatileFieldDouble = { + checkCompile("LazyVals", "class LV { @volatile lazy val s = 1.0 }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Double)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0.0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1.0))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Double)],Literal(Constant(0.0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatileFieldBoolean = { + checkCompile("LazyVals", "class LV { @volatile lazy val s = true }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Boolean)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(false))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(true))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(false))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatileFieldByte = { + checkCompile("LazyVals", "class LV { @volatile lazy val s:Byte = 1 }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Byte)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Byte)],Literal(Constant(0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatileFieldShort = { + checkCompile("LazyVals", "class LV { @volatile lazy val s:Short = 1 }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Short)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Short)],Literal(Constant(0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatileFieldChar = { + checkCompile("LazyVals", "class LV { @volatile lazy val s = 'a' }") { + (tree, ctx) => + val accessor = "DefDef(Modifiers(,,List()),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Char)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(\u0000))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(a))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" + val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Char)],Literal(Constant(\u0000))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) + } + } + + @Test + def volatilesReuseBitmaps = { + checkCompile("LazyVals", "class LV { @volatile lazy val a = 'a'; @volatile lazy val b = 'b'; }") { + (tree, ctx) => + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val reuseFieldPattern = "Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(1))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(moduleField) && treeS.contains(reuseFieldPattern)) + } + } + + @Test + def volatilesCreateNewBitmaps = { + checkCompile("LazyVals", + """ + | class LV { + | @volatile lazy val a1 = '1'; + | @volatile lazy val a2 = '1'; + | @volatile lazy val a3 = '1'; + | @volatile lazy val a4 = '1'; + | @volatile lazy val a5 = '1'; + | @volatile lazy val a6 = '1'; + | @volatile lazy val a7 = '1'; + | @volatile lazy val a8 = '1'; + | @volatile lazy val a9 = '1'; + | @volatile lazy val a10 = '1'; + | @volatile lazy val a11 = '1'; + | @volatile lazy val a12 = '1'; + | @volatile lazy val a13 = '1'; + | @volatile lazy val a14 = '1'; + | @volatile lazy val a15 = '1'; + | @volatile lazy val a16 = '1'; + | @volatile lazy val a17 = '1'; + | @volatile lazy val a18 = '1'; + | @volatile lazy val a19 = '1'; + | @volatile lazy val a20 = '1'; + | @volatile lazy val a21 = '1'; + | @volatile lazy val a22 = '1'; + | @volatile lazy val a23 = '1'; + | @volatile lazy val a24 = '1'; + | @volatile lazy val a25 = '1'; + | @volatile lazy val a26 = '1'; + | @volatile lazy val a27 = '1'; + | @volatile lazy val a28 = '1'; + | @volatile lazy val a29 = '1'; + | @volatile lazy val a30 = '1'; + | @volatile lazy val a31 = '1'; + | @volatile lazy val a32 = '1'; + | @volatile lazy val a33 = '1'; + | @volatile lazy val a34 = '1'; + | } + """.stripMargin ){ + (tree, ctx) => + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$1,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$1))))), ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val reuseFieldPattern = "Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$1), Literal(Constant(3)), Literal(Constant(1))))" + val treeS = tree.toString + Assert.assertTrue("volatile field lazy ref rewritten to class creation", + treeS.contains(moduleField) && treeS.contains(reuseFieldPattern)) + } + } + */ +} From 09d02bd3670b947da147aec40835822d894b17b0 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 17 Mar 2014 22:25:47 +0100 Subject: [PATCH 7/7] Allow MiniPhase to be DenotTransformer All MiniPhases now as are full-fledged phases, and are given their own periods and can register DenotTransformers. MiniPhases belonging to same group(list) will be squashed to single phase. --- src/dotty/tools/dotc/Compiler.scala | 12 +- src/dotty/tools/dotc/core/Contexts.scala | 11 +- .../tools/dotc/core/DenotTransformers.scala | 2 +- src/dotty/tools/dotc/core/Denotations.scala | 13 +- src/dotty/tools/dotc/core/Periods.scala | 6 +- src/dotty/tools/dotc/core/Phases.scala | 76 +++++++-- .../transform/CreateCompanionObjects.scala | 4 +- .../tools/dotc/transform/TreeTransform.scala | 148 ++++++++++++------ ...Phase.scala => UncurryTreeTransform.scala} | 34 ++-- test/test/DottyTest.scala | 13 +- .../CreateCompanionObjectsTest.scala | 22 ++- test/test/transform/LazyValsTest.scala | 3 +- .../transform/PostTyperTransformerTest.scala | 30 ++-- test/test/transform/TreeTransformerTest.scala | 33 ++-- 14 files changed, 278 insertions(+), 129 deletions(-) rename src/dotty/tools/dotc/transform/{SamplePhase.scala => UncurryTreeTransform.scala} (85%) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 132a25d0bb49..c2f21f63908a 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -8,14 +8,20 @@ import Symbols._ import typer.{FrontEnd, Typer, Mode, ImportInfo} import reporting.ConsoleReporter import dotty.tools.dotc.core.Phases.Phase -import dotty.tools.dotc.transform.{LazyValsCreateCompanionObjects, LazyValTranformContext} +import dotty.tools.dotc.transform.{UncurryTreeTransform, LazyValsCreateCompanionObjects, LazyValTranformContext} import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer} import dotty.tools.dotc.transform.PostTyperTransformers.PostTyperTransformer +import dotty.tools.dotc.core.DenotTransformers.DenotTransformer +import dotty.tools.dotc.core.Denotations.SingleDenotation +import dotty.tools.dotc.transform.TreeTransforms.Separator class Compiler { - - def phases: List[Phase] = List(new FrontEnd, new transform.SamplePhase) + def phases: List[List[Phase]] = List( + List(new FrontEnd), List(new LazyValsCreateCompanionObjects), + //force separataion between lazyVals and LVCreateCO + List(new LazyValTranformContext().transformer, new UncurryTreeTransform) + ) var runId = 1 def nextRunId = { diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 38784ec0c61e..68daea44059f 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -25,7 +25,7 @@ import collection.immutable.BitSet import printing._ import config.{Settings, ScalaSettings, Platform, JavaPlatform} import language.implicitConversions - +import DenotTransformers.DenotTransformer object Contexts { /** A context is passed basically everywhere in dotc. @@ -387,7 +387,7 @@ object Contexts { def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root) // Set up some phases to get started */ - usePhases(SomePhase :: Nil) + usePhases(List(List(SomePhase))) /** The standard definitions */ val definitions = new Definitions @@ -460,8 +460,13 @@ object Contexts { /** Phases by id */ private[core] var phases: Array[Phase] = _ + /** Phases with consecutive Transforms groupped into a single phase */ + private [core] var squashedPhases: Array[Phase] = _ + /** Next denotation transformer id */ - private[core] var nextTransformerId: Array[Int] = _ + private[core] var nextDenotTransformerId: Array[Int] = _ + + private[core] var denotTransformers: Array[DenotTransformer] = _ // Printers state /** Number of recursive invocations of a show method on cuyrrent stack */ diff --git a/src/dotty/tools/dotc/core/DenotTransformers.scala b/src/dotty/tools/dotc/core/DenotTransformers.scala index be85b58772e0..e1ee355d8ec8 100644 --- a/src/dotty/tools/dotc/core/DenotTransformers.scala +++ b/src/dotty/tools/dotc/core/DenotTransformers.scala @@ -21,7 +21,7 @@ object DenotTransformers { trait DenotTransformer extends Phase { /** The last phase during which the transformed denotations are valid */ - def lastPhaseId(implicit ctx: Context) = ctx.nextTransformerId(id + 1) + def lastPhaseId(implicit ctx: Context) = ctx.nextDenotTransformerId(id + 1) /** The validity period of the transformer in the given context */ def validFor(implicit ctx: Context): Period = diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index b35647eadedd..e15c7063782f 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -501,6 +501,8 @@ object Denotations { def current(implicit ctx: Context): SingleDenotation = { val currentPeriod = ctx.period val valid = myValidFor + assert(valid.code > 0, s"negative period $valid: ${valid.code}") + if (valid.runId != currentPeriod.runId) bringForward.current else { var cur = this @@ -514,12 +516,15 @@ object Denotations { if (next.validFor.code > valid.code) { // in this case, next.validFor contains currentPeriod cur = next + cur } else { // not found, cur points to highest existing variant var startPid = cur.validFor.lastPhaseId + 1 - val transformer = ctx.phases(startPid - 1).asInstanceOf[DenotTransformer] + val nextTranformerId = ctx.nextDenotTransformerId(startPid) + val transformer = ctx.denotTransformers(nextTranformerId) //println(s"transforming with $transformer") - next = transformer.transform(cur).syncWithParents + if (currentPeriod.lastPhaseId > transformer.id) + next = transformer.transform(cur).syncWithParents if (next eq cur) startPid = cur.validFor.firstPhaseId else { @@ -534,6 +539,7 @@ object Denotations { cur.validFor = Period( currentPeriod.runId, startPid, transformer.lastPhaseId) //println(s"new denot: $cur, valid for ${cur.validFor}") + cur.current // multiple transformations could be required } } else { // currentPeriod < end of valid; in this case a version must exist @@ -545,8 +551,9 @@ object Denotations { cnt += 1 assert(cnt <= MaxPossiblePhaseId, s"seems to be a loop in Denotations for $this, currentPeriod = $currentPeriod") } + cur } - cur + } } diff --git a/src/dotty/tools/dotc/core/Periods.scala b/src/dotty/tools/dotc/core/Periods.scala index 1c9fbf0c6fb4..61f395c7d87a 100644 --- a/src/dotty/tools/dotc/core/Periods.scala +++ b/src/dotty/tools/dotc/core/Periods.scala @@ -32,8 +32,8 @@ abstract class Periods extends DotClass { self: Context => */ def stablePeriod = { var first = phaseId - val nxTrans = ctx.base.nextTransformerId(first) - while (first - 1 > NoPhaseId && (ctx.base.nextTransformerId(first - 1) == nxTrans)) { + val nxTrans = ctx.base.nextDenotTransformerId(first) + while (first - 1 > NoPhaseId && (ctx.base.nextDenotTransformerId(first - 1) == nxTrans)) { first -= 1 } Period(runId, first, nxTrans) @@ -49,6 +49,8 @@ object Periods { * runid 21 bits * last phase id: 5 bits * #phases before last: 5 bits + * + * // Dmitry: sign == 0 isn't actually always true, in some cases phaseId == -1 is used for shifts, that easily creates code < 0 */ class Period(val code: Int) extends AnyVal { diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 81ab7acbf0dd..c49d314bc376 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -7,8 +7,15 @@ import util.DotClass import DenotTransformers._ import Denotations._ import config.Printers._ +import scala.collection.mutable.{ListBuffer, ArrayBuffer} +import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, TreeTransform} +import dotty.tools.dotc.transform.PostTyperTransformers.PostTyperTransformer +import dotty.tools.dotc.transform.TreeTransforms +import TreeTransforms.Separator + +trait Phases { + self: Context => -trait Phases { self: Context => import Phases._ def phase: Phase = base.phases(period.phaseId) @@ -32,9 +39,13 @@ trait Phases { self: Context => object Phases { - trait PhasesBase { this: ContextBase => + trait PhasesBase { + this: ContextBase => + + // drop NoPhase at beginning + def allPhases = squashedPhases.tail + - def allPhases = phases.tail // drop NoPhase at beginning object NoPhase extends Phase { override def exists = false @@ -62,10 +73,12 @@ object Phases { /** Use the following phases in the order they are given. * The list should never contain NoPhase. + * if squashing is enabled, phases in same subgroup will be squashed to single phase. */ - def usePhases(phases: List[Phase]) = { - this.phases = (NoPhase :: phases ::: new TerminalPhase :: Nil).toArray - this.nextTransformerId = new Array[Int](this.phases.length) + def usePhases(phases: List[List[Phase]], squash: Boolean = true) = { + this.phases = (NoPhase :: phases.flatten ::: new TerminalPhase :: Nil).toArray + this.nextDenotTransformerId = new Array[Int](this.phases.length) + this.denotTransformers = new Array[DenotTransformer](this.phases.length) var i = 0 while (i < this.phases.length) { this.phases(i)._id = i @@ -74,11 +87,48 @@ object Phases { var lastTransformerId = i while (i > 0) { i -= 1 - if (this.phases(i).isInstanceOf[DenotTransformer]) lastTransformerId = i - nextTransformerId(i) = lastTransformerId + this.phases(i) match { + case transformer: DenotTransformer => + lastTransformerId = i + denotTransformers(i) = transformer + case _ => + } + nextDenotTransformerId(i) = lastTransformerId } + + if (squash) { + val squashedPhases = ListBuffer[Phase]() + var postTyperEmmited = false + var i = 0 + while (i < phases.length) { + if (phases(i).length > 1) { + assert(phases(i).forall(x => x.isInstanceOf[TreeTransform]), "Only tree transforms can be squashed") + + val transforms = phases(i).asInstanceOf[List[TreeTransform]] + val block = + if (!postTyperEmmited) { + postTyperEmmited = true + new PostTyperTransformer { + override def name: String = transformations.map(_.name).mkString("TreeTransform:{", ", ", "}") + override protected def transformations: Array[TreeTransform] = transforms.toArray + } + } else new TreeTransformer { + override def name: String = transformations.map(_.name).mkString("TreeTransform:{", ", ", "}") + override protected def transformations: Array[TreeTransform] = transforms.toArray + } + squashedPhases += block + block._id = phases(i).head.id + } else squashedPhases += phases(i).head + i += 1 + } + this.squashedPhases = (NoPhase::squashedPhases.toList :::new TerminalPhase :: Nil).toArray + } else { + this.squashedPhases = this.phases + } + config.println(s"Phases = ${this.phases.deep}") - config.println(s"nextTransformId = ${nextTransformerId.deep}") + config.println(s"squashedPhases = ${this.squashedPhases.deep}") + config.println(s"nextDenotTransformerId = ${nextDenotTransformerId.deep}") } final val typerName = "typer" @@ -110,12 +160,12 @@ object Phases { private[Phases] var _id = -1 /** The sequence position of this phase in the given context where 0 - * is reserved for NoPhase and the first real phase is at position 1. - * -1 if the phase is not installed in the context. + * is reserved for NoPhase and the first real phase is at position 1. + * -1 if the phase is not installed in the context. */ def id = _id - final def <= (that: Phase)(implicit ctx: Context) = + final def <=(that: Phase)(implicit ctx: Context) = exists && id <= that.id final def prev(implicit ctx: Context): Phase = @@ -131,7 +181,7 @@ object Phases { final def erasedTypes(implicit ctx: Context): Boolean = ctx.erasurePhase <= this final def flatClasses(implicit ctx: Context): Boolean = ctx.flattenPhase <= this - final def refChecked (implicit ctx: Context): Boolean = ctx.refchecksPhase <= this + final def refChecked(implicit ctx: Context): Boolean = ctx.refchecksPhase <= this override def toString = name } diff --git a/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala b/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala index 8560b6f6d038..f1131b3f279c 100644 --- a/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala +++ b/src/dotty/tools/dotc/transform/CreateCompanionObjects.scala @@ -19,13 +19,13 @@ import NameOps._ /** A transformer that provides a convenient way to create companion objects */ -abstract class CreateCompanionObjects(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { +abstract class CreateCompanionObjects extends TreeTransform { import tpd._ /** Given class definition should return true if companion object creation should be enforced */ - def predicate(cls: TypeDef)(implicit ctx:Context): Boolean + def predicate(cls: TypeDef)(implicit ctx: Context): Boolean override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = { @tailrec diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 3e29af0e4cd9..6455b6062924 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -46,7 +46,10 @@ object TreeTransforms { * (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable * for achieving this goal, but there can be no wasted cycles anywhere. */ - class TreeTransform(group: TreeTransformer, idx: Int) { + abstract class TreeTransform extends Phase { + + /** id of this treeTransform in group */ + var idx: Int = _ def prepareForIdent(tree: Ident) = this def prepareForSelect(tree: Select) = this @@ -100,7 +103,7 @@ object TreeTransforms { def transformTry(tree: Try)(implicit ctx: Context, info: TransformerInfo): Tree = tree def transformThrow(tree: Throw)(implicit ctx: Context, info: TransformerInfo): Tree = tree def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context, info: TransformerInfo): Tree = tree - def transformTypeTree(tree: TypeTree)(implicit ctx: Context): Tree = tree + def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo): Tree = tree def transformSelectFromTypeTree(tree: SelectFromTypeTree)(implicit ctx: Context, info: TransformerInfo): Tree = tree def transformBind(tree: Bind)(implicit ctx: Context, info: TransformerInfo): Tree = tree def transformAlternative(tree: Alternative)(implicit ctx: Context, info: TransformerInfo): Tree = tree @@ -113,23 +116,42 @@ object TreeTransforms { def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees /** Transform tree using all transforms of current group (including this one) */ - def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = group.transform(tree, info, 0) + def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, 0) /** Transform subtree using all transforms following the current one in this group */ - def transformFollowingDeep(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = group.transform(tree, info, idx + 1) + def transformFollowingDeep(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, idx + 1) /** Transform single node using all transforms following the current one in this group */ - def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = group.transformSingle(tree, idx + 1) + def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transformSingle(tree, idx + 1) /** perform context-dependant initialization */ - def init(implicit ctx:Context): Unit = {} + def init(implicit ctx:Context, info: TransformerInfo): Unit = {} + + protected def mkTreeTransformer = new TreeTransformer { + override def name: String = TreeTransform.this.name + override protected def transformations = Array(TreeTransform.this) + } + + override def run(implicit ctx: Context): Unit = { + mkTreeTransformer.run + } } - val NoTransform = new TreeTransform(null, -1) + val NoTransform = new TreeTransform { + override def name: String = "NoTransform" + idx = -1 + } + + class Separator extends TreeTransform { + override def name: String = "Separator" + idx = -1 + } type Mutator[T] = (TreeTransform, T) => TreeTransform - class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations) {} + class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group:TreeTransformer, val contexts:Array[Context]) { + assert(transformers.size == contexts.size) + } /** * This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX @@ -392,7 +414,7 @@ object TreeTransforms { /** A group of tree transforms that are applied in sequence during the same phase */ abstract class TreeTransformer extends Phase { - protected def transformations: Array[(TreeTransformer, Int) => TreeTransform] + protected def transformations: Array[TreeTransform] override def run(implicit ctx: Context): Unit = { val curTree = ctx.compilationUnit.tpdTree @@ -425,7 +447,7 @@ object TreeTransforms { } if (allDone) null else if (!transformersCopied) info - else new TransformerInfo(result, resultNX) + else new TransformerInfo(result, resultNX, info.group, info.contexts) } val prepForIdent: Mutator[Ident] = (trans, tree) => trans.prepareForIdent(tree) @@ -461,15 +483,23 @@ object TreeTransforms { val prepForStats: Mutator[List[Tree]]= (trans, trees) => trans.prepareForStats(trees) def transform(t: Tree)(implicit ctx: Context): Tree = { - val initialTransformations = transformations.zipWithIndex.map(x => x._1(this, x._2)) - initialTransformations.foreach(_.init) - transform(t, new TransformerInfo(initialTransformations, new NXTransformations(initialTransformations)), 0) + val initialTransformations = transformations + val contexts = initialTransformations.map(tr => ctx.fresh.withPhase(tr).ctx) + val info = new TransformerInfo(initialTransformations, new NXTransformations(initialTransformations), this, contexts) + initialTransformations.zipWithIndex.foreach{ + case (transform, id) => + transform.idx = id + transform.init(ctx, info) + } + transform(t, info, 0) } @tailrec final private[TreeTransforms] def goIdent(tree: Ident, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformIdent(tree) match { + val trans = info.transformers(cur) + + trans.transformIdent(tree)(info.contexts(cur), info) match { case t: Ident => goIdent(t, info.nx.nxTransIdent(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -479,7 +509,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goSelect(tree: Select, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformSelect(tree) match { + val trans = info.transformers(cur) + trans.transformSelect(tree)(info.contexts(cur), info) match { case t: Select => goSelect(t, info.nx.nxTransSelect(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -489,7 +520,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goThis(tree: This, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformThis(tree) match { + val trans = info.transformers(cur) + trans.transformThis(tree)(info.contexts(cur), info) match { case t: This => goThis(t, info.nx.nxTransThis(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -499,7 +531,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goSuper(tree: Super, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformSuper(tree) match { + val trans = info.transformers(cur) + trans.transformSuper(tree)(info.contexts(cur), info) match { case t: Super => goSuper(t, info.nx.nxTransSuper(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -509,7 +542,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goApply(tree: Apply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformApply(tree) match { + val trans = info.transformers(cur) + trans.transformApply(tree)(info.contexts(cur), info) match { case t: Apply => goApply(t, info.nx.nxTransApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -519,7 +553,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goTypeApply(tree: TypeApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformTypeApply(tree) match { + val trans = info.transformers(cur) + trans.transformTypeApply(tree)(info.contexts(cur), info) match { case t: TypeApply => goTypeApply(t, info.nx.nxTransTypeApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -529,7 +564,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goNew(tree: New, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformNew(tree) match { + val trans = info.transformers(cur) + trans.transformNew(tree)(info.contexts(cur), info) match { case t: New => goNew(t, info.nx.nxTransNew(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -539,7 +575,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goPair(tree: Pair, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformPair(tree) match { + val trans = info.transformers(cur) + trans.transformPair(tree)(info.contexts(cur), info) match { case t: Pair => goPair(t, info.nx.nxTransPair(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -549,7 +586,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goTyped(tree: Typed, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformTyped(tree) match { + val trans = info.transformers(cur) + trans.transformTyped(tree)(info.contexts(cur), info) match { case t: Typed => goTyped(t, info.nx.nxTransTyped(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -559,7 +597,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goAssign(tree: Assign, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformAssign(tree) match { + val trans = info.transformers(cur) + trans.transformAssign(tree)(info.contexts(cur), info) match { case t: Assign => goAssign(t, info.nx.nxTransAssign(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -569,7 +608,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goLiteral(tree: Literal, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformLiteral(tree) match { + val trans = info.transformers(cur) + trans.transformLiteral(tree)(info.contexts(cur), info) match { case t: Literal => goLiteral(t, info.nx.nxTransLiteral(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -579,7 +619,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goBlock(tree: Block, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformBlock(tree) match { + val trans = info.transformers(cur) + trans.transformBlock(tree)(info.contexts(cur), info) match { case t: Block => goBlock(t, info.nx.nxTransBlock(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -589,7 +630,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goIf(tree: If, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformIf(tree) match { + val trans = info.transformers(cur) + trans.transformIf(tree)(info.contexts(cur), info) match { case t: If => goIf(t, info.nx.nxTransIf(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -599,7 +641,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goClosure(tree: Closure, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformClosure(tree) match { + val trans = info.transformers(cur) + trans.transformClosure(tree)(info.contexts(cur), info) match { case t: Closure => goClosure(t, info.nx.nxTransClosure(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -609,7 +652,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goMatch(tree: Match, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformMatch(tree) match { + val trans = info.transformers(cur) + trans.transformMatch(tree)(info.contexts(cur), info) match { case t: Match => goMatch(t, info.nx.nxTransMatch(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -619,7 +663,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goCaseDef(tree: CaseDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformCaseDef(tree) match { + val trans = info.transformers(cur) + trans.transformCaseDef(tree)(info.contexts(cur), info) match { case t: CaseDef => goCaseDef(t, info.nx.nxTransCaseDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -629,7 +674,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goReturn(tree: Return, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformReturn(tree) match { + val trans = info.transformers(cur) + trans.transformReturn(tree)(info.contexts(cur), info) match { case t: Return => goReturn(t, info.nx.nxTransReturn(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -639,7 +685,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goTry(tree: Try, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformTry(tree) match { + val trans = info.transformers(cur) + trans.transformTry(tree)(info.contexts(cur), info) match { case t: Try => goTry(t, info.nx.nxTransTry(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -649,7 +696,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goThrow(tree: Throw, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformThrow(tree) match { + val trans = info.transformers(cur) + trans.transformThrow(tree)(info.contexts(cur), info) match { case t: Throw => goThrow(t, info.nx.nxTransThrow(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -659,7 +707,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goSeqLiteral(tree: SeqLiteral, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformSeqLiteral(tree) match { + val trans = info.transformers(cur) + trans.transformSeqLiteral(tree)(info.contexts(cur), info) match { case t: SeqLiteral => goSeqLiteral(t, info.nx.nxTransSeqLiteral(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -669,7 +718,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goTypeTree(tree: TypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformTypeTree(tree) match { + val trans = info.transformers(cur) + trans.transformTypeTree(tree)(info.contexts(cur), info) match { case t: TypeTree => goTypeTree(t, info.nx.nxTransTypeTree(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -679,7 +729,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goSelectFromTypeTree(tree: SelectFromTypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformSelectFromTypeTree(tree) match { + val trans = info.transformers(cur) + trans.transformSelectFromTypeTree(tree)(info.contexts(cur), info) match { case t: SelectFromTypeTree => goSelectFromTypeTree(t, info.nx.nxTransSelectFromTypeTree(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -689,7 +740,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goBind(tree: Bind, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformBind(tree) match { + val trans = info.transformers(cur) + trans.transformBind(tree)(info.contexts(cur), info) match { case t: Bind => goBind(t, info.nx.nxTransBind(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -699,7 +751,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goAlternative(tree: Alternative, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformAlternative(tree) match { + val trans = info.transformers(cur) + trans.transformAlternative(tree)(info.contexts(cur), info) match { case t: Alternative => goAlternative(t, info.nx.nxTransAlternative(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -709,7 +762,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goValDef(tree: ValDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformValDef(tree) match { + val trans = info.transformers(cur) + trans.transformValDef(tree)(info.contexts(cur), info) match { case t: ValDef => goValDef(t, info.nx.nxTransValDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -719,7 +773,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goDefDef(tree: DefDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformDefDef(tree) match { + val trans = info.transformers(cur) + trans.transformDefDef(tree)(info.contexts(cur), info) match { case t: DefDef => goDefDef(t, info.nx.nxTransDefDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -729,7 +784,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goUnApply(tree: UnApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformUnApply(tree) match { + val trans = info.transformers(cur) + trans.transformUnApply(tree)(info.contexts(cur), info) match { case t: UnApply => goUnApply(t, info.nx.nxTransUnApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -739,7 +795,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goTypeDef(tree: TypeDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformTypeDef(tree) match { + val trans = info.transformers(cur) + trans.transformTypeDef(tree)(info.contexts(cur), info) match { case t: TypeDef => goTypeDef(t, info.nx.nxTransTypeDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -749,7 +806,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goTemplate(tree: Template, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformTemplate(tree) match { + val trans = info.transformers(cur) + trans.transformTemplate(tree)(info.contexts(cur), info) match { case t: Template => goTemplate(t, info.nx.nxTransTemplate(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -759,7 +817,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goPackageDef(tree: PackageDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { - info.transformers(cur).transformPackageDef(tree) match { + val trans = info.transformers(cur) + trans.transformPackageDef(tree)(info.contexts(cur), info) match { case t: PackageDef => goPackageDef(t, info.nx.nxTransPackageDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -1069,7 +1128,8 @@ object TreeTransforms { @tailrec final private[TreeTransforms] def goStats(trees: List[Tree], cur: Int)(implicit ctx: Context, info: TransformerInfo): List[Tree] = { if (cur < info.transformers.length) { - val stats = info.transformers(cur).transformStats(trees) + val trans = info.transformers(cur) + val stats = trans.transformStats(trees) goStats(stats, info.nx.nxTransStats(cur + 1)) } else trees } diff --git a/src/dotty/tools/dotc/transform/SamplePhase.scala b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala similarity index 85% rename from src/dotty/tools/dotc/transform/SamplePhase.scala rename to src/dotty/tools/dotc/transform/UncurryTreeTransform.scala index ed4ac113f49e..fe50e41cda8e 100644 --- a/src/dotty/tools/dotc/transform/SamplePhase.scala +++ b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala @@ -10,9 +10,21 @@ import core.Types._ import ast.Trees._ import ast.tpd.{Apply, Tree, cpy} -class SamplePhase extends TreeTransformer with DenotTransformer { - def name = "sample" - def transformations = Array(new UncurryTreeTransform(_, _)) +class UncurryTreeTransform extends TreeTransform with DenotTransformer { + + override def name: String = "uncurry" + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + ctx.traceIndented(s"transforming ${tree.show}", show = true) { + tree.fun match { + case Apply(fn, args) => + def showType(implicit ctx: Context) = + ctx.log(s"at ${ctx.phase} ${fn.symbol} has type ${fn.symbol.info.widen.show}") + showType + ctx.atNextPhase(showType(_)) + showType + cpy.Apply(tree, fn, args ++ tree.args) + case _ => tree + }} def uncurry(tp: Type)(implicit ctx: Context): Type = tp match { case tp @ MethodType(pnames1, ptypes1) => @@ -36,20 +48,4 @@ class SamplePhase extends TreeTransformer with DenotTransformer { case _ => ref.derivedSingleDenotation(ref.symbol, info1) } } -} - -class UncurryTreeTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { - - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - ctx.traceIndented(s"transforming ${tree.show}", show = true) { - tree.fun match { - case Apply(fn, args) => - def showType(implicit ctx: Context) = - ctx.log(s"at ${ctx.phase} ${fn.symbol} has type ${fn.symbol.info.widen.show}") - showType - ctx.atNextPhase(showType(_)) - showType - cpy.Apply(tree, fn, args ++ tree.args) - case _ => tree - }} } \ No newline at end of file diff --git a/test/test/DottyTest.scala b/test/test/DottyTest.scala index ba67f14ce3de..fcc211175315 100644 --- a/test/test/DottyTest.scala +++ b/test/test/DottyTest.scala @@ -41,15 +41,16 @@ class DottyTest { private def compilerWithChecker(phase: String)(assertion:(tpd.Tree, Context) => Unit) = new Compiler { override def phases = { val allPhases = super.phases - val targetPhase = allPhases.find{p=> p.name == phase} - assert(targetPhase isDefined) - val phasesBefore = allPhases.takeWhile(x=> ! (x eq targetPhase.get)) - - val checker = new Phase{ + val targetPhase = allPhases.flatten.find(p => p.name == phase).get + val groupsBefore = allPhases.takeWhile(x => !x.contains(targetPhase)) + val lastGroup = allPhases.find(x => x.contains(targetPhase)).get.takeWhile(x => !(x eq targetPhase)) + val checker = new Phase { def name = "assertionChecker" override def run(implicit ctx: Context): Unit = assertion(ctx.compilationUnit.tpdTree, ctx) } - phasesBefore:::List(targetPhase.get, checker) + val lastGroupAppended = List(lastGroup ::: targetPhase :: Nil) + + groupsBefore ::: lastGroupAppended ::: List(List(checker)) } } diff --git a/test/test/transform/CreateCompanionObjectsTest.scala b/test/test/transform/CreateCompanionObjectsTest.scala index 95a84f4e02e9..bee9b414e004 100644 --- a/test/test/transform/CreateCompanionObjectsTest.scala +++ b/test/test/transform/CreateCompanionObjectsTest.scala @@ -28,8 +28,10 @@ class CreateCompanionObjectsTest extends DottyTest { implicit val ctx = context val transformer = new PostTyperTransformer { - override def transformations = Array(new CreateCompanionObjects(_, _) { - override def predicate(cts: TypeDef): Boolean = true + override def transformations = Array(new CreateCompanionObjects { + + override def name: String = "create all companion objects" + override def predicate(cts: TypeDef)(implicit ctx:Context): Boolean = true }) override def name: String = "test" @@ -51,8 +53,10 @@ class CreateCompanionObjectsTest extends DottyTest { (tree, context) => implicit val ctx = context val transformer = new PostTyperTransformer { - override def transformations = Array(new CreateCompanionObjects(_, _) { - override def predicate(cts: TypeDef): Boolean = true + override def transformations = Array(new CreateCompanionObjects { + + override def name: String = "create all companion modules" + override def predicate(cts: TypeDef)(implicit ctx:Context): Boolean = true }) override def name: String = "test" @@ -73,8 +77,9 @@ class CreateCompanionObjectsTest extends DottyTest { (tree, context) => implicit val ctx = context val transformer = new PostTyperTransformer { - override def transformations = Array(new CreateCompanionObjects(_, _) { - override def predicate(cts: TypeDef): Boolean = true + override def transformations = Array(new CreateCompanionObjects { + override def name: String = "create all companion modules" + override def predicate(cts: TypeDef)(implicit ctx:Context): Boolean = true }) override def name: String = "test" @@ -95,8 +100,9 @@ class CreateCompanionObjectsTest extends DottyTest { (tree, context) => implicit val ctx = context val transformer = new PostTyperTransformer { - override def transformations = Array(new CreateCompanionObjects(_, _) { - override def predicate(cts: TypeDef): Boolean = cts.name.toString.contains("CREATE") + override def transformations = Array(new CreateCompanionObjects { + override def name: String = "create all companion modules" + override def predicate(cts: TypeDef)(implicit ctx:Context): Boolean = cts.name.toString.contains("CREATE") }) override def name: String = "test" diff --git a/test/test/transform/LazyValsTest.scala b/test/test/transform/LazyValsTest.scala index c14ee3e98da8..edcc7ce0c35e 100644 --- a/test/test/transform/LazyValsTest.scala +++ b/test/test/transform/LazyValsTest.scala @@ -14,7 +14,7 @@ class LazyValsTest extends DottyTest { )) } } - /* + @Test def localLong = { checkCompile("LazyVals", "class LocalLV { def m = { lazy val s = 1L; s }}"){ (tree, ctx) => @@ -346,5 +346,4 @@ class LazyValsTest extends DottyTest { treeS.contains(moduleField) && treeS.contains(reuseFieldPattern)) } } - */ } diff --git a/test/test/transform/PostTyperTransformerTest.scala b/test/test/transform/PostTyperTransformerTest.scala index 50696728e286..040f871a28fe 100644 --- a/test/test/transform/PostTyperTransformerTest.scala +++ b/test/test/transform/PostTyperTransformerTest.scala @@ -22,9 +22,11 @@ class PostTyperTransformerTest extends DottyTest { def shouldStripImports = checkCompile("frontend", "class A{ import scala.collection.mutable._; val d = 1}") { (tree, context) => implicit val ctx = context - class EmptyTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) {} + class EmptyTransform extends TreeTransform { + override def name: String = "empty" + } val transformer = new PostTyperTransformer { - override def transformations = Array(new EmptyTransform(_, _)) + override def transformations = Array(new EmptyTransform) override def name: String = "test" } @@ -39,9 +41,11 @@ class PostTyperTransformerTest extends DottyTest { def shouldStripNamedArgs = checkCompile("frontend", "class A{ def p(x:Int, y:Int= 2) = 1; p(1, y = 2)}") { (tree, context) => implicit val ctx = context - class EmptyTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) {} + class EmptyTransform extends TreeTransform { + override def name: String = "empty" + } val transformer = new PostTyperTransformer { - override def transformations = Array(new EmptyTransform(_, _)) + override def transformations = Array(new EmptyTransform) override def name: String = "test" } @@ -56,9 +60,11 @@ class PostTyperTransformerTest extends DottyTest { def shouldReorderExistingObjectsInPackage = checkCompile("frontend", "object A{}; class A{} ") { (tree, context) => implicit val ctx = context - class EmptyTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) {} + class EmptyTransform extends TreeTransform { + override def name: String = "empty" + } val transformer = new PostTyperTransformer { - override def transformations = Array(new EmptyTransform(_, _)) + override def transformations = Array(new EmptyTransform) override def name: String = "test" } @@ -77,9 +83,11 @@ class PostTyperTransformerTest extends DottyTest { def shouldReorderExistingObjectsInBlock = checkCompile("frontend", "class D {def p = {object A{}; class A{}; 1}} ") { (tree, context) => implicit val ctx = context - class EmptyTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) {} + class EmptyTransform extends TreeTransform { + override def name: String = "empty" + } val transformer = new PostTyperTransformer { - override def transformations = Array(new EmptyTransform(_, _)) + override def transformations = Array(new EmptyTransform) override def name: String = "test" } @@ -98,9 +106,11 @@ class PostTyperTransformerTest extends DottyTest { def shouldReorderExistingObjectsInTemplate = checkCompile("frontend", "class D {object A{}; class A{}; } ") { (tree, context) => implicit val ctx = context - class EmptyTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) {} + class EmptyTransform extends TreeTransform { + override def name: String = "empty" + } val transformer = new PostTyperTransformer { - override def transformations = Array(new EmptyTransform(_, _)) + override def transformations = Array(new EmptyTransform) override def name: String = "test" } diff --git a/test/test/transform/TreeTransformerTest.scala b/test/test/transform/TreeTransformerTest.scala index 5d4a6fa06e9c..88ed9ce5346c 100644 --- a/test/test/transform/TreeTransformerTest.scala +++ b/test/test/transform/TreeTransformerTest.scala @@ -15,9 +15,11 @@ class TreeTransformerTest extends DottyTest { def shouldReturnSameTreeIfUnchanged = checkCompile("frontend", "class A{ val d = 1}") { (tree, context) => implicit val ctx = context - class EmptyTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) {} + class EmptyTransform extends TreeTransform { + override def name: String = "empty" + } val transformer = new TreeTransformer { - override def transformations = Array(new EmptyTransform(_, _)) + override def transformations = Array(new EmptyTransform) override def name: String = "test" } @@ -32,12 +34,13 @@ class TreeTransformerTest extends DottyTest { def canReplaceConstant = checkCompile("frontend", "class A{ val d = 1}") { (tree, context) => implicit val ctx = context - class ConstantTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { + class ConstantTransform extends TreeTransform { override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = tpd.Literal(Constant(2)) + override def name: String = "canReplaceConstant" } val transformer = new TreeTransformer { - override def transformations = Array(new ConstantTransform(_, _)) + override def transformations = Array(new ConstantTransform) override def name: String = "test" } @@ -52,9 +55,10 @@ class TreeTransformerTest extends DottyTest { def canOverwrite = checkCompile("frontend", "class A{ val d = 1}") { (tree, context) => implicit val ctx = context - class Transformation(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { + class Transformation extends TreeTransform { override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = tpd.Literal(Constant(-1)) + override def name: String = "canOverwrite" override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.ValDef = { Assert.assertTrue("transformation of children succeeded", @@ -64,7 +68,7 @@ class TreeTransformerTest extends DottyTest { } } val transformer = new TreeTransformer { - override def transformations = Array(new Transformation(_, _)) + override def transformations = Array(new Transformation) override def name: String = "test" } @@ -79,7 +83,8 @@ class TreeTransformerTest extends DottyTest { def transformationOrder = checkCompile("frontend", "class A{ val d = 1}") { (tree, context) => implicit val ctx = context - class Transformation1(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { + class Transformation1 extends TreeTransform { + override def name: String = "transformationOrder1" override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { Assert.assertTrue("correct constant", @@ -95,7 +100,8 @@ class TreeTransformerTest extends DottyTest { tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) } } - class Transformation2(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { + class Transformation2 extends TreeTransform { + override def name: String = "transformationOrder2" override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.ValDef = { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(2))" @@ -104,7 +110,7 @@ class TreeTransformerTest extends DottyTest { } } val transformer = new TreeTransformer { - override def transformations = Array(new Transformation1(_, _), new Transformation2(_, _)) + override def transformations = Array(new Transformation1, new Transformation2) override def name: String = "test" } @@ -120,7 +126,8 @@ class TreeTransformerTest extends DottyTest { (tree, context) => implicit val ctx = context var transformed1 = 0 - class Transformation1(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { + class Transformation1 extends TreeTransform { + override def name: String = "invocationCount1" override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { transformed1 += 1 Assert.assertTrue("correct constant", @@ -138,9 +145,9 @@ class TreeTransformerTest extends DottyTest { } } var transformed2 = 0 - class Transformation2(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { + class Transformation2 extends TreeTransform { var constantsSeen = 0 - + override def name: String = "invocationCount2" override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { transformed2 += 1 constantsSeen match { @@ -167,7 +174,7 @@ class TreeTransformerTest extends DottyTest { } } val transformer = new TreeTransformer { - override def transformations = Array(new Transformation1(_, _), new Transformation2(_, _)) + override def transformations = Array(new Transformation1, new Transformation2) override def name: String = "test" }