diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index d141b7488f2a..f4690df08987 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -55,7 +55,10 @@ class Compiler { List(new Erasure), List(new CapturedVars, new Constructors), - List(new LambdaLift) + List(new LambdaLift, + new Flatten, + new RestoreScopes), + List(new PrivateToStatic) ) var runId = 1 diff --git a/src/dotty/tools/dotc/Flatten.scala b/src/dotty/tools/dotc/Flatten.scala deleted file mode 100644 index d7ccc1ae4e11..000000000000 --- a/src/dotty/tools/dotc/Flatten.scala +++ /dev/null @@ -1,15 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.SymTransformer -import Phases.Phase -import Contexts.Context -import SymDenotations.SymDenotation -import TreeTransforms.MiniPhaseTransform - -class Flatten extends MiniPhaseTransform with SymTransformer { thisTransformer => - override def phaseName = "flatten" - - def transformSym(ref: SymDenotation)(implicit ctx: Context) = ??? -} diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 71026a4499f3..648f19a4fbb2 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -347,6 +347,11 @@ object Trees { s } + /** If this is a thicket, gerform `op` on each of its trees + * otherwise, perform `op` ion tree itself. + */ + def foreachInThicket(op: Tree[T] => Unit): Unit = op(this) + override def toText(printer: Printer) = printer.toText(this) override def hashCode(): Int = System.identityHashCode(this) @@ -809,6 +814,8 @@ object Trees { val newTrees = trees.map(_.withPos(pos)) new Thicket[T](newTrees).asInstanceOf[this.type] } + override def foreachInThicket(op: Tree[T] => Unit): Unit = + trees foreach (_.foreachInThicket(op)) } class EmptyValDef[T >: Untyped] extends ValDef[T]( diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index d0f64f5a7b24..74ba79176ea8 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -263,7 +263,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case _ => false } - try test + try test || tp.symbol.is(JavaStatic) catch { // See remark in SymDenotations#accessWithin case ex: NotDefinedHere => test(ctx.addMode(Mode.FutureDefsOK)) } diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index e5bf27eaeb00..804f6af1a3f0 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -512,6 +512,9 @@ object Flags { /** Labeled `private` or `final` */ final val PrivateOrFinal = Private | Final + /** A private method */ + final val PrivateMethod = allOf(Private, Method) + /** A type parameter with synthesized name */ final val ExpandedTypeParam = allOf(ExpandedName, TypeParam) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index f0d55882403b..e43aaa24fbec 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -36,7 +36,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def nameString(name: Name): String = name.decode.toString override protected def simpleNameString(sym: Symbol): String = - sym.originalName.decode.toString + sym.name.decode.toString override protected def fullNameOwner(sym: Symbol) = { val owner = super.fullNameOwner(sym) @@ -222,7 +222,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "`" ~ toText(id.name) ~ "`" case Ident(name) => tree.typeOpt match { - case tp: NamedType if name != nme.WILDCARD => toTextPrefix(tp.prefix) ~ selectionString(tp) + case tp: NamedType if name != nme.WILDCARD => + val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix + toTextPrefix(pre) ~ selectionString(tp) case _ => toText(name) } case tree @ Select(qual, name) => diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/transform/ElimLocals.scala similarity index 100% rename from src/dotty/tools/dotc/ElimLocals.scala rename to src/dotty/tools/dotc/transform/ElimLocals.scala diff --git a/src/dotty/tools/dotc/transform/Flatten.scala b/src/dotty/tools/dotc/transform/Flatten.scala new file mode 100644 index 000000000000..dceefc0bc90b --- /dev/null +++ b/src/dotty/tools/dotc/transform/Flatten.scala @@ -0,0 +1,49 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Phases.Phase +import Contexts.Context +import Flags._ +import SymUtils._ +import SymDenotations.SymDenotation +import collection.mutable +import TreeTransforms.MiniPhaseTransform +import dotty.tools.dotc.transform.TreeTransforms.TransformerInfo + +class Flatten extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + override def phaseName = "flatten" + + def transformSym(ref: SymDenotation)(implicit ctx: Context) = { + if (ref.isClass && !ref.is(Package) && !ref.owner.is(Package)) { + ref.copySymDenotation( + name = ref.flatName, + owner = ref.enclosingPackageClass) + } + else ref + } + + override def treeTransformPhase = thisTransform.next + + private val liftedDefs = new mutable.ListBuffer[Tree] + + private def liftIfNested(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = + if (ctx.owner is Package) tree + else { + transformFollowing(tree).foreachInThicket(liftedDefs += _) + EmptyTree + } + + override def transformStats(stats: List[Tree])(implicit ctx: Context, info: TransformerInfo) = + if (ctx.owner is Package) { + val liftedStats = stats ++ liftedDefs + liftedDefs.clear + liftedStats + } + else stats + + override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = + liftIfNested(tree) +} diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 196753e82d8e..f36ff6247a3a 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -278,20 +278,20 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } } - override def init(implicit ctx: Context, info: TransformerInfo) = { - free.clear() - proxyMap.clear() - called.clear() - calledFromInner.clear() - liftedOwner.clear() - liftedDefs.clear() - assert(ctx.phase == thisTransform) - (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree) - computeFreeVars() - computeLiftedOwners() - generateProxies()(ctx.withPhase(thisTransform.next)) - liftLocals()(ctx.withPhase(thisTransform.next)) - } + override def init(implicit ctx: Context, info: TransformerInfo) = + ctx.atPhase(thisTransform) { implicit ctx => + free.clear() + proxyMap.clear() + called.clear() + calledFromInner.clear() + liftedOwner.clear() + liftedDefs.clear() + (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree) + computeFreeVars() + computeLiftedOwners() + generateProxies()(ctx.withPhase(thisTransform.next)) + liftLocals()(ctx.withPhase(thisTransform.next)) + } private def currentEnclosure(implicit ctx: Context) = ctx.owner.enclosingMethod.skipConstructor @@ -355,8 +355,9 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } } - private def liftDef(tree: MemberDef)(implicit ctx: Context): Tree = { - liftedDefs(tree.symbol.owner) += rename(tree, tree.symbol.name) + private def liftDef(tree: MemberDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val buf = liftedDefs(tree.symbol.owner) + transformFollowing(rename(tree, tree.symbol.name)).foreachInThicket(buf += _) EmptyTree } diff --git a/src/dotty/tools/dotc/transform/PrivateToStatic.scala b/src/dotty/tools/dotc/transform/PrivateToStatic.scala new file mode 100644 index 000000000000..5562fc6b2c05 --- /dev/null +++ b/src/dotty/tools/dotc/transform/PrivateToStatic.scala @@ -0,0 +1,97 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Contexts.Context +import Symbols._ +import Scopes._ +import Flags._ +import StdNames._ +import SymDenotations._ +import Types._ +import collection.mutable +import TreeTransforms._ +import Decorators._ +import ast.Trees._ +import TreeTransforms.TransformerInfo + +/** Makes private methods static, provided they not deferred, accessors, or static, + * by rewriting a method `m` in class `C` as follows: + * + * private def m(ps) = e + * + * --> private static def($this: C, ps) = [this -> $this] e + */ +class PrivateToStatic extends MiniPhase with SymTransformer { thisTransform => + import ast.tpd._ + override def phaseName = "privateToStatic" + override def relaxedTyping = true + + private val Immovable = Deferred | Accessor | JavaStatic + + def shouldBeStatic(sd: SymDenotation)(implicit ctx: Context) = + sd.current(ctx.withPhase(thisTransform)).asSymDenotation + .is(PrivateMethod, butNot = Immovable) && + (sd.owner.is(Trait) || sd.is(NotJavaPrivate)) + + override def transformSym(sd: SymDenotation)(implicit ctx: Context): SymDenotation = + if (shouldBeStatic(sd)) { + val mt @ MethodType(pnames, ptypes) = sd.info + sd.copySymDenotation( + initFlags = sd.flags | JavaStatic, + info = MethodType(nme.SELF :: pnames, sd.owner.thisType :: ptypes, mt.resultType)) + } + else sd + + val treeTransform = new Transform(NoSymbol) + + class Transform(thisParam: Symbol) extends TreeTransform { + def phase = thisTransform + override def treeTransformPhase = thisTransform.next + + override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = + if (shouldBeStatic(tree.symbol)) { + val selfParam = ctx.newSymbol(tree.symbol, nme.SELF, Param, tree.symbol.owner.thisType, coord = tree.pos) + new Transform(selfParam) + } + else this + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = + if (shouldBeStatic(tree.symbol)) { + val thisParamDef = ValDef(thisParam.asTerm) + val vparams :: Nil = tree.vparamss + cpy.DefDef(tree)( + mods = tree.mods | JavaStatic, + vparamss = (thisParamDef :: vparams) :: Nil) + } + else tree + + override def transformThis(tree: This)(implicit ctx: Context, info: TransformerInfo) = + if (shouldBeStatic(ctx.owner.enclosingMethod)) ref(thisParam).withPos(tree.pos) + else tree + + /** Rwrites a call to a method `m` which is made static as folows: + * + * qual.m(args) --> m(qual, args) + */ + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = + tree.fun match { + case fun @ Select(qual, name) if shouldBeStatic(fun.symbol) => + ctx.debuglog(i"mapping $tree to ${cpy.Ident(fun)(name)} (${qual :: tree.args}%, %)") + cpy.Apply(tree)(ref(fun.symbol).withPos(fun.pos), qual :: tree.args) + case _ => + tree + } + + override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo) = + tree.meth match { + case meth @ Select(qual, name) if shouldBeStatic(meth.symbol) => + cpy.Closure(tree)( + env = qual :: tree.env, + meth = ref(meth.symbol).withPos(meth.pos)) + case _ => + tree + } + } +} diff --git a/src/dotty/tools/dotc/transform/RestoreScopes.scala b/src/dotty/tools/dotc/transform/RestoreScopes.scala new file mode 100644 index 000000000000..4a42523266ec --- /dev/null +++ b/src/dotty/tools/dotc/transform/RestoreScopes.scala @@ -0,0 +1,36 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.IdentityDenotTransformer +import Contexts.Context +import Symbols._ +import Scopes._ +import collection.mutable +import TreeTransforms.MiniPhaseTransform +import ast.Trees._ +import TreeTransforms.TransformerInfo + +/** The preceding lambda lift and flatten phases move symbols to different scopes + * and rename them. This miniphase cleans up afterwards and makes sure that all + * class scopes contain the symbols defined in them. + */ +class RestoreScopes extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + override def phaseName = "restoreScopes" + + override def treeTransformPhase = thisTransform.next + + override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = { + val TypeDef(_, _, Template(constr, _, _, body)) = tree + val restoredDecls = newScope + for (stat <- constr :: body) + if (stat.isInstanceOf[MemberDef] && stat.symbol.exists) + restoredDecls.enter(stat.symbol) + val cinfo = tree.symbol.asClass.classInfo + tree.symbol.copySymDenotation( + info = cinfo.derivedClassInfo( // Dotty deviation: Cannot expand cinfo inline without a type error + decls = restoredDecls: Scope)).installAfter(thisTransform) + tree + } +} diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 7cb4c5825b10..b11658efb7b6 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -295,7 +295,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val body1 = forwardParamAccessors(transformStats(impl.body, tree.symbol)) accDefs -= currentClass ownStats ++= body1 - cpy.Template(impl)(body = body1) + cpy.Template(impl)(body = ownStats.toList) } transformTemplate diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index 2875327c4712..ba45d3f04663 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -5,6 +5,7 @@ import core._ import Types._ import Contexts._ import Symbols._ +import SymDenotations._ import Decorators._ import Names._ import StdNames._ @@ -13,7 +14,8 @@ import Flags._ import language.implicitConversions object SymUtils { - implicit def decorateSymUtils(sym: Symbol): SymUtils = new SymUtils(sym) + implicit def decorateSymbol(sym: Symbol): SymUtils = new SymUtils(sym) + implicit def decorateSymDenot(d: SymDenotation): SymUtils = new SymUtils(d.symbol) } /** A decorator that provides methods on symbols @@ -64,4 +66,7 @@ class SymUtils(val self: Symbol) extends AnyVal { def field(implicit ctx: Context): Symbol = self.owner.info.decl(self.asTerm.name.fieldName).suchThat(!_.is(Method)).symbol + + /** `fullName` where `$' is the separator character */ + def flatName(implicit ctx: Context): Name = self.fullNameSeparated('$') } diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index d2f30e7baf78..f6e36a129b6a 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -180,7 +180,7 @@ class TreeChecker { override def typedStats(trees: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { for (tree <- trees) tree match { case tree: untpd.DefTree => checkOwner(tree) - case _: untpd.Thicket => assert(false, "unexpanded thicket in statement sequence") + case _: untpd.Thicket => assert(false, i"unexpanded thicket $tree in statement sequence $trees%\n%") case _ => } super.typedStats(trees, exprOwner) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 035f19028d7c..a237e7781822 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -461,7 +461,7 @@ trait Applications extends Compatibility { self: Typer => val result = { var typedArgs = typedArgBuf.toList - val app0 = cpy.Apply(app)(normalizedFun, typedArgs) + def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later val app1 = if (!success) app0.withType(ErrorType) else { diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 71a1600bb7da..a52f79abd0ba 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -15,7 +15,7 @@ class tests extends CompilerTest { implicit val defaultOptions = noCheckOptions ++ List( "-Yno-deep-subtypes", - "-Ycheck:patternMatcher,gettersSetters,lambdaLift" + "-Ycheck:patternMatcher,gettersSetters,restoreScopes" ) val twice = List("#runs", "2", "-YnoDoubleBindings") diff --git a/tests/pos/CoderTrait.scala b/tests/pos/CoderTrait.scala new file mode 100644 index 000000000000..1eba60097fae --- /dev/null +++ b/tests/pos/CoderTrait.scala @@ -0,0 +1,63 @@ +import collection.mutable.HashMap + +trait CoderTrait { + + val words: List[String] + + (2 -> "ABC", new ArrowAssoc('3') -> "DEF") + + private val mnemonics = Map( + '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' -> "JKL", + '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ") + + + ('1', "1") match { + case (digit, str) => true + case _ => false + } + + /** Invert the mnemonics map to give a map from chars 'A' ... 'Z' to '2' ... '9' */ + private val charCode0: Map[Char, Char] = mnemonics withFilter { + case (digit, str) => true + case _ => false + } flatMap { x$1 => + x$1 match { + case (digit, str) => str map (ltr => ltr -> digit) + } + } + + private val charCode: Map[Char, Char] = + for ((digit, str) <- mnemonics; ltr <- str) yield ltr -> digit + + /** Maps a word to the digit string it can represent */ + private def wordCode(word: String): String = word map charCode + + /** A map from digit strings to the words that represent them */ + private val wordsForNum: Map[String, List[String]] = + words groupBy wordCode withDefaultValue Nil + + /** All ways to encode a number as a list of words */ + def encode(number: String): Set[List[String]] = + if (number.isEmpty) Set(Nil) + else { + for { + splitPoint <- 1 to number.length + word <- wordsForNum(number take splitPoint) + rest <- encode(number drop splitPoint) + } yield word :: rest + }.toSet + + /** Maps a number to a list of all word phrases that can represent it */ + def translate(number: String): Set[String] = encode(number) map (_ mkString " ") + +} + +object Coder { + def main(args : Array[String]) : Unit = { + val coder = new CoderTrait { + val words = List("Scala", "sobls", "Python", "Ruby", "C", "A", "rocks", "sucks", "works", "Racka") + } +// println(coder.wordsForNum) + println(coder.translate("7225276257")) + } +} diff --git a/tests/pos/privates.scala b/tests/pos/privates.scala new file mode 100644 index 000000000000..edaa10cb6b1a --- /dev/null +++ b/tests/pos/privates.scala @@ -0,0 +1,9 @@ +trait Test { + + private val x = 2 + + private def foo() = x * x + + private def bar() = foo() + +} diff --git a/tests/pos/traits.scala b/tests/pos/traits.scala new file mode 100644 index 000000000000..15310d5a4e60 --- /dev/null +++ b/tests/pos/traits.scala @@ -0,0 +1,25 @@ +trait B { + + val z: Int + +} + +trait T { + + var x = 2 + + private var xp = 2 + + val y = 3 + + private val yp = 3 + + val z = 4 + + x = 4 + + xp = 4 + +} + +class C extends T