diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index d141b7488f2a..004a3868c8af 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -46,16 +46,21 @@ class Compiler { new TailRec), List(new PatternMatcher, new ExplicitOuter, - // new LazyValTranformContext().transformer, // disabled, awaiting fixes new Splitter), List(new ElimByName, new InterceptedMethods, new Literalize, - new GettersSetters), + new Getters, + new ResolveSuper), List(new Erasure), - List(new CapturedVars, + List(new Mixin, + new Memoize, // TODO: Make LazyVals a part of this phase + 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/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 2a55d6732f83..4a492560f581 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -4,6 +4,7 @@ package core import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined import dotc.transform.ExplicitOuter._ +import typer.Mode import util.DotClass /** Erased types are: @@ -89,7 +90,7 @@ object TypeErasure { /** The current context with a phase no later than erasure */ private def erasureCtx(implicit ctx: Context) = - if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase) else ctx + if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase).addMode(Mode.FutureDefsOK) else ctx def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp)(erasureCtx) def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)(erasureCtx) @@ -107,6 +108,8 @@ object TypeErasure { case tp: TermRef => assert(tp.symbol.exists, tp) TermRef(erasedRef(tp.prefix), tp.symbol.asTerm) + case tp: ThisType => + tp case tp => erasure(tp) } @@ -141,7 +144,12 @@ object TypeErasure { if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) else if (sym.isAbstractType) TypeAlias(WildcardType) else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) - else eraseInfo(tp)(erasureCtx) + else eraseInfo(tp)(erasureCtx) match { + case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) => + defn.BoxedUnitClass.typeRef + case einfo => + einfo + } } def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !( @@ -265,7 +273,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild case tp: TermRef => this(tp.widen) case ThisType(_) => - tp + this(tp.widen) case SuperType(thistpe, supertpe) => SuperType(this(thistpe), this(supertpe)) case ExprType(rt) => @@ -319,7 +327,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild } def eraseInfo(tp: Type)(implicit ctx: Context) = tp match { - case ExprType(rt) => MethodType(Nil, Nil, erasure(rt)) + case ExprType(rt) => MethodType(Nil, Nil, eraseResult(rt)) case tp => erasure(tp) } diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 51e1ff16ffb7..8163c8bcc159 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -26,15 +26,18 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => /** Is tree legal as a member definition of an interface? */ - def isInterfaceMember(tree: Tree): Boolean = unsplice(tree) match { - case EmptyTree => true - case Import(_, _) => true - case TypeDef(_, _, _) => true - case DefDef(mods, _, _, _, _, __) => mods.flags is Deferred - case ValDef(mods, _, _, _) => mods is Deferred + def isPureInterfaceMember(tree: Tree): Boolean = unsplice(tree) match { + case EmptyTree | Import(_, _) | TypeDef(_, _, _) => true + case DefDef(_, _, _, _, _, rhs) => rhs.isEmpty + case ValDef(mods, _, _, rhs) => rhs.isEmpty case _ => false } + /** Is tree legal as a member definition of a no-init trait? + */ + def isNoInitMember(tree: Tree): Boolean = + isPureInterfaceMember(tree) || unsplice(tree).isInstanceOf[DefDef] + def isOpAssign(tree: Tree) = unsplice(tree) match { case Apply(fn, _ :: Nil) => unsplice(fn) match { 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..735b218e3d56 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -6,7 +6,7 @@ import transform.SymUtils._ import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._ -import Denotations._, Decorators._ +import Denotations._, Decorators._, DenotTransformers._ import config.Printers._ import typer.Mode import collection.mutable @@ -39,8 +39,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def This(cls: ClassSymbol)(implicit ctx: Context): This = untpd.This(cls.name).withType(cls.thisType) - def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean)(implicit ctx: Context): Super = - ta.assignType(untpd.Super(qual, mix), qual, inConstrCall) + def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context): Super = + ta.assignType(untpd.Super(qual, mix), qual, inConstrCall, mixinClass) def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = ta.assignType(untpd.Apply(fn, args), fn, args) @@ -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)) } @@ -527,7 +527,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { */ def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = { def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = { - if (from.isWeakOwner) loop(from.owner, from :: froms, to :: tos) + if (from.isWeakOwner && !from.owner.isClass) + loop(from.owner, from :: froms, to :: tos) else { //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree) @@ -536,6 +537,26 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { loop(from, Nil, to :: Nil) } + /** After phase `trans`, set the owner of every definition in this tree that was formerly + * owner by `from` to `to`. + */ + def changeOwnerAfter(from: Symbol, to: Symbol, trans: DenotTransformer)(implicit ctx: Context): ThisTree = { + assert(ctx.phase == trans.next) + val traverser = new TreeTraverser { + def traverse(tree: Tree) = tree match { + case tree: DefTree => + val sym = tree.symbol + if (sym.denot(ctx.withPhase(trans)).owner == from) + sym.copySymDenotation(owner = to).installAfter(trans) + if (sym.isWeakOwner) traverseChildren(tree) + case _ => + traverseChildren(tree) + } + } + traverser.traverse(tree) + tree + } + def select(name: Name)(implicit ctx: Context): Select = Select(tree, name) diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 743925e40753..abde6cb5317f 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -107,8 +107,9 @@ class ScalaSettings extends Settings.SettingGroup { val Xdce = BooleanSetting("-Ydead-code", "Perform dead code elimination.") val debug = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.") val debugNames = BooleanSetting("-YdebugNames", "Show name-space indicators when printing names") - val debugTrace = BooleanSetting("-YdebugTrace", "Trace core operations") - val debugFlags = BooleanSetting("-YdebugFlags", "Print all flags of definitions") + val debugTrace = BooleanSetting("-Ydebug-trace", "Trace core operations") + val debugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions") + val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)") //val doc = BooleanSetting ("-Ydoc", "Generate documentation") val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val inline = BooleanSetting("-Yinline", "Perform inlining when possible.") diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index de6b0cabfde7..6293d18d2d10 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -297,16 +297,21 @@ object Contexts { def thisCallArgContext: Context = { assert(owner.isClassConstructor) val constrCtx = outersIterator.dropWhile(_.outer.owner == owner).next - var classCtx = outersIterator.dropWhile(!_.isClassDefContext).next - classCtx.superOrThisCallContext(owner, constrCtx.scope).setTyperState(typerState) + superOrThisCallContext(owner, constrCtx.scope).setTyperState(typerState) } /** The super= or this-call context with given owner and locals. */ private def superOrThisCallContext(owner: Symbol, locals: Scope): FreshContext = { - assert(isClassDefContext) - outer.fresh.setOwner(owner).setScope(locals).setMode(ctx.mode | Mode.InSuperCall) + var classCtx = outersIterator.dropWhile(!_.isClassDefContext).next + classCtx.outer.fresh.setOwner(owner).setScope(locals).setMode(classCtx.mode | Mode.InSuperCall) } + /** The context of expression `expr` seen as a member of a statement sequence */ + def exprContext(stat: Tree[_ >: Untyped], exprOwner: Symbol) = + if (exprOwner == this.owner) this + else if (untpd.isSuperConstrCall(stat) && this.owner.isClass) superCallContext + else ctx.fresh.setOwner(exprOwner) + /** The current source file; will be derived from current * compilation unit. */ diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index b1c2baff6cd7..d78e09418115 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -191,7 +191,7 @@ class Definitions { lazy val ScalaStaticsClass = ScalaStaticsModule.moduleClass.asClass def staticsMethod(name: PreName) = ctx.requiredMethod(ScalaStaticsClass, name) - + lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef") lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil") lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms) @@ -201,7 +201,7 @@ class Definitions { // needed as a synthetic class because Scala 2.x refers to it in classfiles // but does not define it as an explicit class. newCompleteClassSymbol( - ScalaPackageClass, tpnme.Singleton, Trait | Interface | Final, + ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final, List(AnyClass.typeRef), EmptyScope) lazy val SeqClass: ClassSymbol = ctx.requiredClass("scala.collection.Seq") lazy val Seq_apply = ctx.requiredMethod(SeqClass, nme.apply) @@ -506,7 +506,7 @@ class Definitions { val cls = newClassSymbol( ScalaPackageClass, traitName, - Trait | Interface | Synthetic, + PureInterfaceCreationFlags | Synthetic, completer) myLambdaTraits += cls cls diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 82fd60fa0cef..ce11759ceb5c 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -202,7 +202,7 @@ object Denotations { 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 + /** The alternative of this denotation that has a type matching `targetType` when seen * as a member of type `site`, `NoDenotation` if none exists. */ def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation = diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index e5bf27eaeb00..db969767b8c4 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -287,7 +287,7 @@ object Flags { /** A trait that has only abstract methods as members * (and therefore can be represented by a Java interface */ - final val Interface = typeFlag(22, "interface") + final val PureInterface = typeFlag(22, "interface") /** Labeled with of abstract & override */ final val AbsOverride = termFlag(22, "abstract override") @@ -335,6 +335,9 @@ object Flags { final val JavaStaticTerm = JavaStatic.toTermFlags final val JavaStaticType = JavaStatic.toTypeFlags + /** Trait is not an interface, but does not have fields or intialization code */ + final val NoInits = typeFlag(32, "") + /** Variable is accessed from nested function. */ final val Captured = termFlag(32, "") @@ -353,9 +356,6 @@ object Flags { /** Symbol is a Java-style varargs method */ final val JavaVarargs = termFlag(37, "") - /** Symbol is a Java default method */ - final val DefaultMethod = termFlag(38, "") - // Flags following this one are not pickled /** Symbol always defines a fresh named type */ @@ -464,6 +464,9 @@ object Flags { /** Accessors always have these flags set */ final val AccessorCreationFlags = Method | Accessor + /** Pure interfaces always have these flags */ + final val PureInterfaceCreationFlags = Trait | NoInits | PureInterface + /** The flags of the self symbol */ final val SelfSymFlags = Private | Local | Deferred @@ -512,6 +515,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) @@ -536,8 +542,11 @@ object Flags { /** Is a default parameter in Scala 2*/ final val DefaultParameter = allOf(Param, DefaultParameterized) - /** A Java interface */ - final val JavaInterface = allOf(JavaDefined, Trait) + /** A trait that does not need to be initialized */ + final val NoInitsTrait = allOf(Trait, NoInits) + + /** A Java interface, potentially with default methods */ + final val JavaTrait = allOf(JavaDefined, Trait, NoInits) /** A Java companion object */ final val JavaModule = allOf(JavaDefined, Module) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index beb3142d3876..bc15f6a066bc 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -47,13 +47,15 @@ object NameOps { } } - object SuperAccessorName { - val pre = nme.SUPER_PREFIX + class PrefixNameExtractor(pre: TermName) { def apply(name: TermName): TermName = pre ++ name def unapply(name: TermName): Option[TermName] = if (name startsWith pre) Some(name.drop(pre.length).asTermName) else None } + object SuperAccessorName extends PrefixNameExtractor(nme.SUPER_PREFIX) + object InitializerName extends PrefixNameExtractor(nme.INITIALIZER_PREFIX) + implicit class NameDecorator[N <: Name](val name: N) extends AnyVal { import nme._ @@ -68,14 +70,13 @@ object NameOps { def isProtectedAccessorName = name startsWith PROTECTED_PREFIX def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER def isSetterName = name endsWith SETTER_SUFFIX - def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_PREFIX) def isSingletonName = name endsWith SINGLETON_SUFFIX def isModuleClassName = name endsWith MODULE_SUFFIX def isImportName = name startsWith IMPORT def isFieldName = name endsWith LOCAL_SUFFIX def isInheritedName = name.length > 0 && name.head == '(' && name.startsWith(nme.INHERITED) def isDefaultGetterName = name.isTermName && name.asTermName.defaultGetterIndex >= 0 - + def isScala2LocalSuffix = name.endsWith(" ") def isModuleVarName(name: Name): Boolean = name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX @@ -224,9 +225,6 @@ object NameOps { implicit class TermNameDecorator(val name: TermName) extends AnyVal { import nme._ - def traitSetterName: TermName = - nme.TRAIT_SETTER_PREFIX ++ setterName - def setterName: TermName = if (name.isFieldName) name.fieldToGetter.setterName else name ++ SETTER_SUFFIX @@ -240,13 +238,8 @@ object NameOps { else name ++ LOCAL_SUFFIX private def setterToGetter: TermName = { - val p = name.indexOfSlice(TRAIT_SETTER_PREFIX) - if (p >= 0) - (name drop (p + TRAIT_SETTER_PREFIX.length)).asTermName.getterName - else { - assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") - name.take(name.length - SETTER_SUFFIX.length).asTermName - } + assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") + name.take(name.length - SETTER_SUFFIX.length).asTermName } def fieldToGetter: TermName = { @@ -283,6 +276,9 @@ object NameOps { -1 } + def stripScala2LocalSuffix: TermName = + if (name.isScala2LocalSuffix) name.init.asTermName else name + /** The name of an accessor for protected symbols. */ def protectedAccessorName: TermName = PROTECTED_PREFIX ++ name.unexpandedName() diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 494a26f7e58d..09bdca19625a 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -20,7 +20,7 @@ import printing.Printer import util.common._ import util.DotClass import SymDenotations.NoDenotation -import collection.mutable.ListBuffer +import collection.mutable object Scopes { @@ -172,12 +172,27 @@ object Scopes { */ private var elemsCache: List[Symbol] = null - def cloneScope(implicit ctx: Context): MutableScope = newScopeWith(this.toList: _*) + /** Clone scope, taking care not to force the denotations of any symbols in the scope. + */ + def cloneScope(implicit ctx: Context): MutableScope = { + val entries = new mutable.ArrayBuffer[ScopeEntry] + var e = lastEntry + while ((e ne null) && e.owner == this) { + entries += e + e = e.prev + } + val scope = newScope + for (i <- entries.length - 1 to 0 by -1) { + val e = entries(i) + scope.newScopeEntry(e.name, e.sym) + } + scope + } - /** create and enter a scope entry */ - protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry = { + /** create and enter a scope entry with given name and symbol */ + protected def newScopeEntry(name: Name, sym: Symbol)(implicit ctx: Context): ScopeEntry = { ensureCapacity(if (hashTable ne null) hashTable.length else MinHash) - val e = new ScopeEntry(sym.name, sym, this) + val e = new ScopeEntry(name, sym, this) e.prev = lastEntry lastEntry = e if (hashTable ne null) enterInHash(e) @@ -186,6 +201,10 @@ object Scopes { e } + /** create and enter a scope entry */ + protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry = + newScopeEntry(sym.name, sym) + private def enterInHash(e: ScopeEntry)(implicit ctx: Context): Unit = { val idx = e.name.hashCode & (hashTable.length - 1) e.tail = hashTable(idx) @@ -325,7 +344,7 @@ object Scopes { } override def implicitDecls(implicit ctx: Context): List[TermRef] = { - var irefs = new ListBuffer[TermRef] + var irefs = new mutable.ListBuffer[TermRef] var e = lastEntry while (e ne null) { if (e.sym is Implicit) { diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 99290f0848d0..8393eb56f30a 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -119,9 +119,9 @@ object StdNames { val SINGLETON_SUFFIX: N = ".type" val SPECIALIZED_SUFFIX: N = "$sp" val SUPER_PREFIX: N = "super$" - val TRAIT_SETTER_PREFIX: N = "_setter_$" val WHILE_PREFIX: N = "while$" - val DEFAULT_EXCEPTION_NAME: N = "ex$" + val DEFAULT_EXCEPTION_NAME: N = "ex$" + val INITIALIZER_PREFIX: N = "initial$" // value types (and AnyRef) are all used as terms as well // as (at least) arguments to the @specialize annotation. diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ae8fceeb743b..0d92a8e5f60d 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -425,10 +425,14 @@ object SymDenotations { final def isSourceMethod(implicit ctx: Context) = this is (Method, butNot = Accessor) /** Is this a setter? */ - final def isGetter(implicit ctx: Context) = (this is Accessor) && !originalName.isSetterName + final def isGetter(implicit ctx: Context) = + (this is Accessor) && !originalName.isSetterName && !originalName.isScala2LocalSuffix /** Is this a setter? */ - final def isSetter(implicit ctx: Context) = (this is Accessor) && originalName.isSetterName + final def isSetter(implicit ctx: Context) = + (this is Accessor) && + originalName.isSetterName && + info.firstParamTypes.nonEmpty // to avoid being fooled by var x_= : Unit = ... /** is this the constructor of a class? */ final def isClassConstructor = name == nme.CONSTRUCTOR @@ -560,7 +564,7 @@ object SymDenotations { def membersNeedAsSeenFrom(pre: Type)(implicit ctx: Context) = !( this.isTerm || this.isStaticOwner - || ctx.erasedTypes && symbol != defn.ArrayClass + || ctx.erasedTypes || (pre eq NoPrefix) || (pre eq thisType) ) @@ -892,8 +896,9 @@ object SymDenotations { override def valRef(implicit ctx: Context): TermRef = TermRef.withSigAndDenot(owner.thisType, name.asTermName, Signature.NotAMethod, this) - override def termRefWithSig(implicit ctx: Context): TermRef = - TermRef.withSigAndDenot(owner.thisType, name.asTermName, signature, this) + override def termRefWithSig(implicit ctx: Context): TermRef = // TODO generalize + if (ctx.erasedTypes) TermRef.withFixedSym(owner.thisType, name.asTermName, symbol.asTerm) + else TermRef.withSigAndDenot(owner.thisType, name.asTermName, signature, this) def nonMemberTermRef(implicit ctx: Context): TermRef = TermRef.withFixedSym(owner.thisType, name.asTermName, symbol.asTerm) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d0ddfdd28b50..5d95fae7600a 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1173,8 +1173,19 @@ object Types { if (newd.exists) newd else d.staleSymbolError } case d => - if (d.validFor.runId == ctx.period.runId) d.current - else loadDenot + if (d.validFor.runId != ctx.period.runId) + loadDenot + // The following branch was used to avoid an assertErased error. + // It's idea was to void keeping non-sym denotations after erasure + // since they violate the assertErased contract. But the problem is + // that when seen again in an earlier phase the denotation is + // still seen as a SymDenotation, whereas it should be a SingleDenotation. + // That's why the branch is disabled. + // + // else if (ctx.erasedTypes && lastSymbol != null) + // denotOfSym(lastSymbol) + else + d.current } if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot") else if (d.exists) { @@ -1623,8 +1634,10 @@ object Types { final class CachedSuperType(thistpe: Type, supertpe: Type) extends SuperType(thistpe, supertpe) object SuperType { - def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = + def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = { + assert(thistpe != NoPrefix) unique(new CachedSuperType(thistpe, supertpe)) + } } /** A constant type with single `value`. */ @@ -1817,7 +1830,7 @@ object Types { } catch { case ex: AssertionError => - println(i"failure while taking result signture of $resultType") + println(i"failure while taking result signture of $this: $resultType") throw ex } @@ -1837,7 +1850,7 @@ object Types { extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType => override val resultType = resultTypeExp(this) - assert(resultType != NoType) + assert(resultType.exists) def isJava = false def isImplicit = false diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala index c35b9ca474fd..158f6b40973c 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala @@ -345,7 +345,7 @@ object ClassfileConstants { case JAVA_ACC_SYNTHETIC => Synthetic case JAVA_ACC_STATIC => JavaStatic case JAVA_ACC_ABSTRACT => if (isAnnotation) EmptyFlags else if (isClass) Abstract else Deferred - case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else JavaInterface + case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else PureInterfaceCreationFlags | JavaDefined case _ => EmptyFlags } diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 67f8255025f1..f2a5e41719e2 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -503,8 +503,9 @@ class ClassfileParser( parseExceptions(attrLen) case tpnme.CodeATTR => - if (sym.owner is Flags.Interface) { - sym.setFlag(Flags.DefaultMethod) + if (sym.owner is Flags.JavaTrait) { + sym.resetFlag(Flags.Deferred) + sym.owner.resetFlag(Flags.PureInterface) ctx.log(s"$sym in ${sym.owner} is a java8+ default method.") } in.skip(attrLen) diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala index d2a05bf3a4dc..9f8d4fc2d141 100644 --- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala @@ -220,7 +220,7 @@ object PickleBuffer { DEFERRED_PKL -> Deferred, FINAL_PKL -> Final, METHOD_PKL -> Method, - INTERFACE_PKL -> Interface, + INTERFACE_PKL -> PureInterface, MODULE_PKL -> Module, IMPLICIT_PKL -> Implicit, SEALED_PKL -> Sealed, diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 9fba7ec09816..ffbae59bb79a 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -288,7 +288,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else if (flags is Mutable) "variable" else if (sym.isClassConstructor && sym.isPrimaryConstructor) "primary constructor" else if (sym.isClassConstructor) "constructor" - else if (sym.isSourceMethod) "method" + else if (sym.is(Method)) "method" else if (sym.isTerm) "value" else "" } @@ -296,14 +296,14 @@ class PlainPrinter(_ctx: Context) extends Printer { /** String representation of symbol's definition key word */ protected def keyString(sym: Symbol): String = { val flags = sym.flagsUNSAFE - if (flags is JavaInterface) "interface" + if (flags is JavaTrait) "interface" else if ((flags is Trait) && !(flags is ImplClass)) "trait" else if (sym.isClass) "class" else if (sym.isType) "type" else if (flags is Mutable) "var" else if (flags is Package) "package" else if (flags is Module) "object" - else if (sym.isSourceMethod) "def" + else if (sym is Method) "def" else if (sym.isTerm && (!(flags is Param))) "val" else "" } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index f0d55882403b..50b73a35772e 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) @@ -205,7 +205,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } def dclTextOr(treeText: => Text) = - if (useSymbol) annotsText(tree.symbol) ~~ dclText(tree.symbol) + if (useSymbol) + annotsText(tree.symbol) ~~ dclText(tree.symbol) ~ + ( " " provided ctx.settings.debugOwners.value) else treeText def idText(tree: untpd.Tree): Text = { @@ -222,7 +224,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/transform/AttachOuter.scala b/src/dotty/tools/dotc/transform/AttachOuter.scala deleted file mode 100644 index 9d2b0574da61..000000000000 --- a/src/dotty/tools/dotc/transform/AttachOuter.scala +++ /dev/null @@ -1,65 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import core.StdNames.nme -import ast.Trees._ -import util.Attachment - -/** This phase decorates News and parent constructors of non-static inner classes - * with an attachment indicating the outer reference as a tree. This is necessary because - * outer prefixes are erased, and explicit outer runs only after erasure. - */ -class AttachOuter extends MiniPhaseTransform { - import ast.tpd._ - - val Outer = new Attachment.Key[Tree] - - override def phaseName: String = "attachOuter" - - private def outerPrefix(tpe: Type)(implicit ctx: Context): Type = tpe match { - case tpe: TypeRef => - tpe.symbol match { - case cls: ClassSymbol => - if (cls.owner.isStaticOwner || cls.is(Interface)) NoPrefix - else if (tpe.prefix eq NoPrefix) cls.owner.enclosingClass.thisType - else tpe.prefix - case _ => - outerPrefix(tpe.underlying) - } - case tpe: TypeProxy => - outerPrefix(tpe.underlying) - } - - override def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo): Tree = { - val pre = outerPrefix(tree.tpt.tpe) - pre match { - case pre: SingletonType => - tree.putAttachment(Outer, singleton(pre)) match { - case Some(outer) => assert(outer.tpe =:= pre) - case none => - } - case NoPrefix => - } - tree - } - - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { - def transformParent(tree: Tree): Tree = tree match { - case tree: TypeTree if outerPrefix(tree.tpe) != NoPrefix => - val constr = New(tree.tpe, Nil).withPos(tree.pos) - val Select(nu: New, _) = methPart(constr) - transformNew(nu) - constr - case _ => - tree - } - cpy.Template(tree)(parents = tree.parents mapconserve transformParent) - } -} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 7bde1ba4ffa4..9420ce2c089a 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -109,32 +109,6 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor } } - val superCalls = new mutable.ListBuffer[Tree] - - // If parent is a constructor call, pull out the call into a separate - // supercall constructor, which gets appended to `superCalls`, and keep - // only the type. - def normalizeParent(tree: Tree) = tree match { - case superApp @ Apply( - superSel @ Select( - superNew @ New(superType), - nme.CONSTRUCTOR), - superArgs) => - val toClass = !superType.symbol.is(Trait) - val mappedArgs = superArgs.map(intoConstr(_, inSuperCall = toClass)) - val receiver = - if (toClass) Super(This(cls), tpnme.EMPTY, inConstrCall = true) - else This(cls) - superCalls += - cpy.Apply(superApp)( - receiver.withPos(superNew.pos) - .select(superSel.symbol).withPos(superSel.pos), - mappedArgs) - superType - case tree: TypeTree => tree - } - val parentTypeTrees = tree.parents.map(normalizeParent) - // Collect all private parameter accessors and value definitions that need // to be retained. There are several reasons why a parameter accessor or // definition might need to be retained: @@ -172,16 +146,12 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor traverse(stat) } } - usage.collect(superCalls.toList ++ tree.body) + usage.collect(tree.body) def isRetained(acc: Symbol) = !mightBeDropped(acc) || usage.retained(acc) val constrStats, clsStats = new mutable.ListBuffer[Tree] - def assign(vble: Symbol, rhs: Tree): Tree = - if (cls is Trait) ref(vble.setter).appliedTo(rhs) - else Assign(ref(vble), rhs) - // Split class body into statements that go into constructor and // definitions that are kept as members of the class. def splitStats(stats: List[Tree]): Unit = stats match { @@ -191,7 +161,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val sym = stat.symbol if (isRetained(sym)) { if (!rhs.isEmpty && !isWildcardArg(rhs)) - constrStats += assign(sym, intoConstr(rhs)).withPos(stat.pos) + constrStats += Assign(ref(sym), intoConstr(rhs)).withPos(stat.pos) clsStats += cpy.ValDef(stat)(rhs = EmptyTree) } else if (!rhs.isEmpty) { @@ -215,7 +185,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor // The initializers for the retained accessors */ val copyParams = accessorFields.filter(isRetained).map(acc => - assign(acc, ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) + Assign(ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) // Drop accessors that are not retained from class scope val dropped = usage.dropped @@ -226,10 +196,14 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor decls = clsInfo.decls.filteredScope(!dropped.contains(_)))) } + val (superCalls, followConstrStats) = constrStats.toList match { + case (sc: Apply) :: rest if sc.symbol.isConstructor => (sc :: Nil, rest) + case stats => (Nil, stats) + } + cpy.Template(tree)( constr = cpy.DefDef(constr)( - rhs = Block(superCalls.toList ::: copyParams ::: constrStats.toList, unitLiteral)), - parents = parentTypeTrees, + rhs = Block(superCalls ::: copyParams ::: followConstrStats, unitLiteral)), body = clsStats.toList) } } \ No newline at end of file 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/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 9f381bb8e7d9..0a34b9e7ca16 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -94,7 +94,8 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => if (ctx.mode.isExpr) tree.tpe match { case ref: TermRef => - assert(ref.denot.isInstanceOf[SymDenotation], + assert(ref.denot.isInstanceOf[SymDenotation] || + ref.denot.isInstanceOf[UniqueRefDenotation], i"non-sym type $ref of class ${ref.getClass} with denot of class ${ref.denot.getClass} of $tree") case _ => } @@ -247,6 +248,10 @@ object Erasure extends TypeTestsCasts{ tree.withType(erased) } + override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = + if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt) + else super.typedLiteral(tree) + /** Type check select nodes, applying the following rewritings exhaustively * on selections `e.m`, where `OT` is the type of the owner of `m` and `ET` * is the erased type of the selection's original qualifier expression. @@ -277,7 +282,7 @@ object Erasure extends TypeTestsCasts{ case _ => sym.name } untpd.cpy.Select(tree)(qual, sym.name) - .withType(NamedType.withSymAndName(qual.tpe, sym, name)) + .withType(NamedType.withFixedSym(qual.tpe, sym)) } def selectArrayMember(qual: Tree, erasedPre: Type): Tree = @@ -369,12 +374,20 @@ object Erasure extends TypeTestsCasts{ } } + override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context): ValDef = + super.typedValDef(untpd.cpy.ValDef(vdef)( + tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym) + override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { + val restpe = sym.info.resultType val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, vparamss = ddef.vparamss.flatten :: Nil, - tpt = // keep UnitTypes intact in result position - untpd.TypedSplice(TypeTree(eraseResult(ddef.tpt.typeOpt)).withPos(ddef.tpt.pos))) + tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)), + rhs = ddef.rhs match { + case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) + case _ => ddef.rhs + }) super.typedDefDef(ddef1, sym) } diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index d056d7e351a2..436d9bcf7d9d 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -144,11 +144,11 @@ object ExplicitOuter { nme.OUTER.expandedName(cls) /** Class needs an outer pointer, provided there is a reference to an outer this in it. */ - def needsOuterIfReferenced(cls: ClassSymbol)(implicit ctx: Context): Boolean = !( - cls.isStatic || - cls.owner.enclosingClass.isStaticOwner || - cls.is(Interface) - ) + def needsOuterIfReferenced(cls: ClassSymbol)(implicit ctx: Context): Boolean = + !(cls.isStatic || + cls.owner.enclosingClass.isStaticOwner || + cls.is(PureInterface) + ) /** Class unconditionally needs an outer pointer. This is the case if * the class needs an outer pointer if referenced and one of the following holds: @@ -187,6 +187,10 @@ object ExplicitOuter { private def hasOuter(cls: ClassSymbol)(implicit ctx: Context): Boolean = needsOuterIfReferenced(cls) && outerAccessor(cls).exists + /** Class constructor takes an outer argument. Can be called only after phase ExplicitOuter. */ + private def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean = + !cls.is(Trait) && needsOuterIfReferenced(cls) && outerAccessor(cls).exists + /** Tree references a an outer class of `cls` which is not a static owner. */ def referencesOuter(cls: Symbol, tree: Tree)(implicit ctx: Context): Boolean = { @@ -248,7 +252,7 @@ object ExplicitOuter { /** If `cls` has an outer parameter add one to the method type `tp`. */ def addParam(cls: ClassSymbol, tp: Type): Type = - if (hasOuter(cls)) { + if (hasOuterParam(cls)) { val mt @ MethodType(pnames, ptypes) = tp mt.derivedMethodType( nme.OUTER :: pnames, cls.owner.enclosingClass.typeRef :: ptypes, mt.resultType) @@ -261,14 +265,14 @@ object ExplicitOuter { if (fun.symbol.isConstructor) { val cls = fun.symbol.owner.asClass def outerArg(receiver: Tree): Tree = receiver match { - case New(tpt) => - singleton(outerPrefix(tpt.tpe)) + case New(_) | Super(_, _) => + singleton(outerPrefix(receiver.tpe)) case This(_) => - ref(outerParamAccessor(cls)) // will be rewried to outer argument of secondary constructor in phase Constructors + ref(outerParamAccessor(cls)) // will be rewired to outer argument of secondary constructor in phase Constructors case TypeApply(Select(r, nme.asInstanceOf_), args) => outerArg(r) // cast was inserted, skip } - if (hasOuter(cls)) + if (hasOuterParam(cls)) methPart(fun) match { case Select(receiver, _) => outerArg(receiver).withPos(fun.pos) :: Nil } 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/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala new file mode 100644 index 000000000000..4ea9d2c6b6a4 --- /dev/null +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -0,0 +1,69 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Contexts.Context +import SymDenotations.SymDenotation +import Types._ +import Symbols._ +import SymUtils._ +import Constants._ +import TreeTransforms._ +import Flags._ +import Decorators._ + +/** Performs the following rewritings for fields of a class: + * + * val x: T = e + * --> def x: T = e + * var x: T = e + * --> def x: T = e + * + * val x: T + * --> def x: T + * + * var x: T + * --> def x: T + * + * Omitted from the rewritings are + * + * - private[this] fields in non-trait classes + * - fields generated for static modules (TODO: needed?) + * - parameters, static fields, and fields coming from Java + * + * Furthermore, assignements to mutable vars are replaced by setter calls + * + * p.x = e + * --> p.x_=(e) + * + * No fields are generated yet. This is done later in phase Memoize. + */ +class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + + override def phaseName = "getters" + override def treeTransformPhase = thisTransform.next + + override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { + def noGetterNeeded = + d.is(NoGetterNeeded) || + d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || + d.is(Module) && d.isStatic || + d.isSelfSym + if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) { + val maybeStable = if (d.isStable) Stable else EmptyFlags + d.copySymDenotation( + initFlags = d.flags | maybeStable | AccessorCreationFlags, + info = ExprType(d.info)) + } + else d + } + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic + + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree + + override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs) else tree +} diff --git a/src/dotty/tools/dotc/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala index 772a63e52a6c..b5933cc4835e 100644 --- a/src/dotty/tools/dotc/transform/GettersSetters.scala +++ b/src/dotty/tools/dotc/transform/GettersSetters.scala @@ -16,27 +16,39 @@ import NameOps._ import Flags._ import Decorators._ -/** Performs the following rewritings: +/** Performs the following rewritings on fields of classes, where `x_L` is the "local name" of `x`: * * val x: T = e * --> private val x_L: T = e - * def x: T = x_L (if in class) - * --> private notJavaPrivate var x_L: T = e * def x: T = x_L - * private notJavaPrivate def x_=(x: T): Unit = x_L = x (if in trait) + * * var x: T = e + * def x_=(y: T) = () * --> private var x_L: T = e * def x: T = x_L * def x_=(x: T): Unit = x_L = x (if in class or trait) + * * lazy val x: T = e - * --> lazy def x = e + * --> def x: T = e + * + * val x: T + * --> def x: T + * + * var x: T + * --> def x: T + * + * Omitted from the rewritings are + * + * - private[this] fields in non-trait classes + * - fields generated for static modules (TODO: needed?) + * - parameters, static fields, and fields coming from Java * * Furthermore, assignements to mutable vars are replaced by setter calls * * p.x = e * --> p.x_=(e) */ -class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform => + class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ override def phaseName = "gettersSetters" @@ -45,38 +57,34 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { def noGetterNeeded = - d.is(Method | Param | JavaDefined) || + d.is(NoGetterNeeded) || d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || d.is(Module) && d.isStatic || d.isSelfSym - if (d.isTerm && (d.owner.isClass || d.is(Lazy)) && d.info.isValueType && !noGetterNeeded) { + if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) { val maybeStable = if (d.isStable) Stable else EmptyFlags - if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}") + //if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}") d.copySymDenotation( initFlags = d.flags | maybeStable | AccessorCreationFlags, info = ExprType(d.info)) } else d } + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic + private val NoFieldNeeded = Lazy | Deferred | ParamAccessor override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { if (tree.symbol is Method) { val getter = tree.symbol.asTerm assert(getter is Accessor) - if (getter.is(Lazy | Deferred | ParamAccessor)) DefDef(getter, tree.rhs) + if (getter is NoFieldNeeded) + DefDef(getter, tree.rhs) else { val inTrait = getter.owner.is(Trait) - val maybePrivate = - if (inTrait) Private | NotJavaPrivate - else if (getter.owner.isClass) Private - else EmptyFlags - val maybeMutable = - if (inTrait || getter.is(Mutable)) Mutable - else EmptyFlags val field = ctx.newSymbol( owner = ctx.owner, name = getter.name.fieldName, - flags = maybePrivate | maybeMutable, + flags = Private | (getter.flags & Mutable), info = getter.info.resultType).enteredAfter(thisTransform) assert(tree.rhs.tpe.exists, tree.show) val fieldDef = @@ -88,16 +96,7 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf val rhs = ref(field) assert(rhs.hasType) val getterDef = DefDef(getter, rhs.ensureConforms(getter.info.widen)) - if (!getter.is(Mutable) && inTrait) { // add a setter anyway, will be needed for mixin - val setter = ctx.newSymbol( - owner = ctx.owner, - name = getter.name.traitSetterName, - flags = (getter.flags & AccessFlags) | Accessor | maybePrivate, - info = MethodType(field.info :: Nil, defn.UnitType)).enteredAfter(thisTransform) - val setterDef = DefDef(setter.asTerm, vrefss => Assign(ref(field), vrefss.head.head)) - Thicket(fieldDef, getterDef, setterDef) - } - else Thicket(fieldDef, getterDef) + Thicket(fieldDef, getterDef) } } else tree @@ -106,6 +105,7 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = if (tree.symbol.isSetter && !tree.symbol.is(Deferred | ParamAccessor)) { val Literal(Constant(())) = tree.rhs + assert(tree.symbol.field.exists, i"no field for ${tree.symbol.showLocated}") val initializer = Assign(ref(tree.symbol.field), ref(tree.vparamss.head.head.symbol)) assert(initializer.hasType) cpy.DefDef(tree)(rhs = initializer) 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/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 47ffaafb3e0d..3a8bcc92071c 100644 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -44,11 +44,10 @@ abstract class MacroTransform extends Phase { def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass def transformStats(trees: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { - val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat) case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) - case _ => transform(stat)(exprCtx) + case _ => transform(stat)(ctx.exprContext(stat, exprOwner)) } flatten(trees.mapconserve(transformStat(_))) } diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala new file mode 100644 index 000000000000..ef70b9ecf34a --- /dev/null +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -0,0 +1,86 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers._ +import Phases.Phase +import Contexts.Context +import SymDenotations.SymDenotation +import Types._ +import Symbols._ +import SymUtils._ +import Constants._ +import ast.Trees._ +import TreeTransforms._ +import NameOps._ +import Flags._ +import Decorators._ + +/** Provides the implementations of all getters and setters, introducing + * fields to hold the value accessed by them. + * TODO: Make LazyVals a part of this phase? + * + * def x(): T = e + * --> private val x: T = e + * def x(): T = x + * + * def x(): T = e + * --> private var x: T = e + * def x(): T = x + * + * def x_=(y: T): Unit = () + * --> def x_=(y: T): Unit = x = y + */ + class Memoize extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName = "memoize" + override def treeTransformPhase = thisTransform.next + + /** Should to run after mixin so that fields get generated in the + * class that contains the concrete getter rather than the trait + * that defines it. + */ + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) + + override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = { + val sym = tree.symbol + if (sym.isGetter && !sym.is(NoFieldNeeded)) { + // allocate field early so that initializer has the right owner for subsequeny phases in + // the group. + val maybeMutable = if (sym is Stable) EmptyFlags else Mutable + val field = ctx.newSymbol( + owner = ctx.owner, + name = sym.name.asTermName.fieldName, + flags = Private | maybeMutable, + info = sym.info.resultType, + coord = tree.pos).enteredAfter(thisTransform) + tree.rhs.changeOwnerAfter(sym, field, thisTransform) + } + this + } + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val sym = tree.symbol + def field = { + val field = sym.field.asTerm + assert(field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") + field + } + if (sym.is(Accessor, butNot = NoFieldNeeded)) + if (sym.isGetter) { + val fieldDef = transformFollowing(ValDef(field, tree.rhs)) + val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))) + Thicket(fieldDef, getterDef) + } + else if (sym.isSetter) { + val Literal(Constant(())) = tree.rhs + val initializer = Assign(ref(field), ref(tree.vparamss.head.head.symbol)) + cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)) + } + else tree // curiously, some accessors from Scala2 have ' ' suffixes. They count as + // neither getters nor setters + else tree + } + private val NoFieldNeeded = Lazy | Deferred | ParamAccessor +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala new file mode 100644 index 000000000000..3d68a2687c2b --- /dev/null +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -0,0 +1,176 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import StdNames._ +import NameOps._ +import Phases._ +import ast.Trees._ +import collection.mutable + +/** This phase performs the following transformations: + * + * 1. (done in `traitDefs`) Map every concrete trait getter + * + * def x(): T = expr + * + * to the pair of definitions: + * + * def x(): T + * protected def initial$x(): T = { stats; expr } + * + * where `stats` comprises all statements between either the start of the trait + * or the previous field definition which are not definitions (i.e. are executed for + * their side effects). + * + * 2. (done in `traitDefs`) Make every concrete trait setter + * + * def x_=(y: T) = () + * + * deferred by maping it to + * + * def x_=(y: T) + * + * 3. For a non-trait class C: + * + * For every trait M directly implemented by the class (see SymUtils.mixin), in + * reverse linearization order, add the following definitions to C: + * + * 3.1 (done in `traitInits`) For every concrete trait getter ` def x(): T` in M, + * in order of textual occurrence: + * + * def x(): T = super[M].initial$x() + * + * 3.2 (done in `superCallOpt`) The call: + * + * super[M]. + * + * 3.3 (done in `setters`) For every concrete setter ` def x_=(y: T)` in M: + * + * def x_=(y: T) = () + * + * Conceptually, this is the second half of the previous mixin phase. It needs to run + * after erasure because it copies references to possibly private inner classes and objects + * into enclosing classes where they are not visible. This can only be done if all references + * are symbolic. + */ +class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "mixin" + override def treeTransformPhase = thisTransform.next + + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) + + override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = + if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait)) + sym.copySymDenotation(initFlags = sym.flags | Deferred) + else + sym + + private def initializer(sym: Symbol)(implicit ctx: Context): TermSymbol = { + val initName = InitializerName(sym.name.asTermName) + sym.owner.info.decl(initName).symbol + .orElse( + ctx.newSymbol( + sym.owner, + initName, + Protected | Synthetic | Method, + sym.info, + coord = sym.symbol.coord).enteredAfter(thisTransform)) + .asTerm + } + + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = impl.symbol.owner.asClass + val ops = new MixinOps(cls, thisTransform) + import ops._ + + def traitDefs(stats: List[Tree]): List[Tree] = { + val initBuf = new mutable.ListBuffer[Tree] + stats flatMap { + case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty => + val vsym = stat.symbol + val isym = initializer(vsym) + val rhs = Block( + initBuf.toList.map(_.changeOwner(impl.symbol, isym)), + stat.rhs.changeOwner(vsym, isym)) + initBuf.clear() + List( + cpy.DefDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), + DefDef(isym, rhs)) + case stat: DefDef if stat.symbol.isSetter => + List(cpy.DefDef(stat)( + mods = stat.mods | Deferred, + rhs = EmptyTree)) + case stat: DefTree => + List(stat) + case stat => + initBuf += stat + Nil + } + } + + def transformSuper(tree: Tree): Tree = { + val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree + superRef(tree.symbol, tree.pos).appliedToArgs(args) + } + + val superCalls = ( + for (p <- impl.parents if p.symbol.isConstructor) + yield p.symbol.owner -> transformSuper(p) + ).toMap + + def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match { + case Some(call) => + if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil + case None => + if (baseCls.is(NoInitsTrait) || defn.PhantomClasses.contains(baseCls)) Nil + else { + //println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}") + superRef(baseCls.primaryConstructor).appliedToNone :: Nil +/* constr.tpe.widen match { + case tpe: PolyType => + val targs = cls.thisType.baseTypeWithArgs(baseCls).argTypes + constr = constr.appliedToTypes(targs) + case _ => + } + constr.ensureApplied :: Nil +*/ + } + } + + def wasDeferred(sym: Symbol) = + ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } + + def traitInits(mixin: ClassSymbol): List[Tree] = + for (getter <- mixin.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) + yield { + DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone) + } + + def setters(mixin: ClassSymbol): List[Tree] = + for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) + yield DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos)) + + cpy.Template(impl)( + parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)), + body = + if (cls is Trait) traitDefs(impl.body) + else { + val mixInits = mixins.flatMap { mixin => + traitInits(mixin) ::: superCallOpt(mixin) ::: setters(mixin) + } + superCallOpt(superCls) ::: mixInits ::: impl.body + }) + } +} diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala new file mode 100644 index 000000000000..de15b045ff61 --- /dev/null +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -0,0 +1,37 @@ +package dotty.tools.dotc +package transform + +import core._ +import Symbols._, Types._, Contexts._, SymDenotations._, DenotTransformers._, Flags._ +import util.Positions._ +import StdNames._, NameOps._ + +class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: Context) { + import ast.tpd._ + + val superCls: Symbol = cls.classInfo.parents.head.symbol + val mixins: List[ClassSymbol] = + if (cls is Trait) Nil + else cls.baseClasses.tail.takeWhile(_ ne superCls).reverse + + def implementation(member: TermSymbol): TermSymbol = + member.copy( + owner = cls, + name = member.name.stripScala2LocalSuffix, + flags = member.flags &~ Deferred &~ Module, + info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm + + def superRef(target: Symbol, pos: Position = cls.pos): Tree = { + val sup = if (target.isConstructor && !target.owner.is(Trait)) + Super(This(cls), tpnme.EMPTY, true) + else + Super(This(cls), target.owner.name.asTypeName, false, target.owner) + //println(i"super ref $target on $sup") + ast.untpd.Select(sup.withPos(pos), target.name) + .withType(NamedType.withFixedSym(sup.tpe, target)) + //sup.select(target) + } + + def forwarder(target: Symbol) = (targs: List[Type]) => (vrefss: List[List[Tree]]) => + superRef(target).appliedToTypes(targs).appliedToArgss(vrefss) +} diff --git a/src/dotty/tools/dotc/transform/PrivateToStatic.scala b/src/dotty/tools/dotc/transform/PrivateToStatic.scala new file mode 100644 index 000000000000..b9bfff459866 --- /dev/null +++ b/src/dotty/tools/dotc/transform/PrivateToStatic.scala @@ -0,0 +1,90 @@ +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 + +/** 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 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)).asInstanceOf[SymDenotation] + .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 + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = + tree.fun match { + case fun @ Select(qual, name) if shouldBeStatic(fun.symbol) => + //println(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/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala new file mode 100644 index 000000000000..23ff45a7c0b1 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -0,0 +1,98 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import StdNames._ +import NameOps._ +import ast.Trees._ +import util.Positions._ +import Names._ +import collection.mutable + +/** This phase adds super accessors and method overrides where + * linearization differs from Java's rule for default methods in interfaces. + * In particular: + * + * For every trait M directly implemented by the class (see SymUtils.mixin), in + * reverse linearization order, add the following definitions to C: + * + * 3.1 (done in `superAccessors`) For every superAccessor + * ` def super$f[Ts](ps1)...(psN): U` in M: + * + * def super$f[Ts](ps1)...(psN): U = super[S].f[Ts](ps1)...(psN) + * + * where `S` is the superclass of `M` in the linearization of `C`. + * + * 3.2 (done in `methodOverrides`) For every method + * ` def f[Ts](ps1)...(psN): U` in M` that needs to be disambiguated: + * + * def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) + * + * A method in M needs to be disambiguated if it is concrete, not overridden in C, + * and if it overrides another concrete method. + * + * This is the first part of what was the mixin phase. It is complemented by + * Mixin, which runs after erasure. + */ +class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "resolveSuper" + + override def treeTransformPhase = thisTransform.next + + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = impl.symbol.owner.asClass + val ops = new MixinOps(cls, thisTransform) + import ops._ + + /** Returns the symbol that is accessed by a super-accessor in a mixin composition. + * + * @param base The class in which everything is mixed together + * @param member The symbol statically referred to by the superaccessor in the trait + */ + def rebindSuper(base: Symbol, acc: Symbol): Symbol = { + var bcs = cls.info.baseClasses.dropWhile(acc.owner != _).tail + var sym: Symbol = NoSymbol + val SuperAccessorName(memberName) = acc.name: Name // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type + ctx.debuglog(i"starting rebindsuper from $cls of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") + while (bcs.nonEmpty && sym == NoSymbol) { + val other = bcs.head.info.nonPrivateDecl(memberName) + if (ctx.settings.debug.value) + ctx.log(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}") + sym = other.matchingDenotation(cls.thisType, cls.thisType.memberInfo(acc)).symbol + bcs = bcs.tail + } + assert(sym.exists) + sym + } + + def superAccessors(mixin: ClassSymbol): List[Tree] = + for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) + yield polyDefDef(implementation(superAcc.asTerm), forwarder(rebindSuper(cls, superAcc))) + + def methodOverrides(mixin: ClassSymbol): List[Tree] = { + def isOverridden(meth: Symbol) = meth.overridingSymbol(cls).is(Method, butNot = Deferred) + def needsDisambiguation(meth: Symbol): Boolean = + meth.is(Method, butNot = PrivateOrDeferred) && + !isOverridden(meth) && + !meth.allOverriddenSymbols.forall(_ is Deferred) + for (meth <- mixin.decls.toList if needsDisambiguation(meth)) + yield polyDefDef(implementation(meth.asTerm), forwarder(meth)) + } + + val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin)) + + cpy.Template(impl)(body = overrides ::: impl.body) + } + private val PrivateOrDeferred = Private | Deferred +} 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..537c8c0c6a0f 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -90,7 +90,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse { ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz") val acc = ctx.newSymbol( - clazz, supername, SuperAccessor | Private | Artifact, + clazz, supername, SuperAccessor | Private | Artifact | Method, ensureMethodic(sel.tpe.widenSingleton), coord = sym.coord).enteredAfter(thisTransformer) // Diagnostic for SI-7091 if (!accDefs.contains(clazz)) @@ -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..449affb9e3d3 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 @@ -59,9 +61,17 @@ class SymUtils(val self: Symbol) extends AnyVal { def setter(implicit ctx: Context): Symbol = if (self.isSetter) self - else accessorNamed(self.asTerm.name.setterName) orElse - accessorNamed(self.asTerm.name.traitSetterName) + else accessorNamed(self.asTerm.name.setterName) 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('$') + + def initializer(implicit ctx: Context): TermSymbol = + self.owner.info.decl(InitializerName(self.asTerm.name)).symbol.asTerm + + def isField(implicit ctx: Context): Boolean = + self.isTerm && !self.is(Method) } 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/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 588a13fc93a1..850563a484da 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -905,7 +905,7 @@ object TreeTransforms { case tree: UnApply => goUnApply(tree, info.nx.nxTransUnApply(cur)) case tree: Template => goTemplate(tree, info.nx.nxTransTemplate(cur)) case tree: PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur)) - case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) + case Thicket(trees) => tree case tree => goOther(tree, info.nx.nxTransOther(cur)) } @@ -1164,7 +1164,8 @@ object TreeTransforms { val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx) goPackageDef(cpy.PackageDef(tree)(pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) } - case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) + case Thicket(trees) => + cpy.Thicket(tree)(transformTrees(trees, info, cur)) case tree => implicit val originalInfo: TransformerInfo = info goOther(tree, info.nx.nxTransOther(cur)) @@ -1200,11 +1201,10 @@ object TreeTransforms { def transformStats(trees: List[Tree], exprOwner: Symbol, info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tree] = { val newInfo = mutateTransformers(info, prepForStats, info.nx.nxPrepStats, trees, current) - val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat, newInfo, current) case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) - case _ => transform(stat, newInfo, current)(exprCtx) + case _ => transform(stat, newInfo, current)(ctx.exprContext(stat, exprOwner)) } val newTrees = flatten(trees.mapconserve(transformStat)) goStats(newTrees, newInfo.nx.nxTransStats(current))(ctx, newInfo) 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/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index e96e04b1a449..8e8cf58f9b5c 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -35,7 +35,7 @@ object ErrorReporting { // See test pending/pos/boundspropagation.scala val treeSym = ctx.symOfContextTree(tree) if (treeSym.exists && treeSym.name == cycleSym.name && treeSym.owner == cycleSym.owner) { - val result = if (cycleSym.isSourceMethod) " result" else "" + val result = if (cycleSym is Method) " result" else "" d"overloaded or recursive $cycleSym needs$result type" } else errorMsg(msg, cx.outer) diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 3a1f0a98b461..e8bb1b9e7286 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -492,6 +492,13 @@ class Namer { typer: Typer => index(rest)(inClassContext(selfInfo)) denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo) + if (cls is Trait) { + if (body forall isNoInitMember) { + cls.setFlag(NoInits) + if (body forall isPureInterfaceMember) + cls.setFlag(PureInterface) + } + } } } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index bb488bdc5c82..765c6bea7f41 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -236,9 +236,10 @@ trait TypeAssigner { tree.withType(cls.thisType) } - def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean)(implicit ctx: Context) = { + def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context) = { val mix = tree.mix - val cls = qual.tpe.widen.typeSymbol + val qtype @ ThisType(_) = qual.tpe + val cls = qtype.cls def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix) match { case p :: Nil => @@ -249,7 +250,8 @@ trait TypeAssigner { errorType("ambiguous parent class qualifier", tree.pos) } val owntype = - if (!mix.isEmpty) findMixinSuper(cls.info) + if (mixinClass.exists) mixinClass.typeRef + else if (!mix.isEmpty) findMixinSuper(cls.info) else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent else { val ps = cls.info.parents diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 3c36a1f256ef..a5396d445fa5 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1042,8 +1042,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case Thicket(stats) :: rest => traverse(stats ++ rest) case stat :: rest => - val nestedCtx = if (exprOwner == ctx.owner) ctx else ctx.fresh.setOwner(exprOwner) - buf += typed(stat)(nestedCtx) + buf += typed(stat)(ctx.exprContext(stat, exprOwner)) traverse(rest) case nil => buf.toList @@ -1246,6 +1245,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typed(etaExpand(tree, wtp, arity), pt) else if (wtp.paramTypes.isEmpty) adaptInterpolated(tpd.Apply(tree, Nil), pt, EmptyTree) + else if (wtp.isImplicit) + err.typeMismatch(tree, pt) else errorTree(tree, d"""missing arguments for $methodStr diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 71a1600bb7da..5cce45e215df 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:resolveSuper,mixin,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/blockescapes.scala b/tests/pos/blockescapes.scala index 35d40974b1fe..68ce37ed3ee4 100644 --- a/tests/pos/blockescapes.scala +++ b/tests/pos/blockescapes.scala @@ -3,7 +3,7 @@ object blockescapes { { val x = 0; () } val x0 = { class Foo; new Foo } val x1 = {} - val x2 = { val z = 0 } + var x2 = { val z = 0 } val m1 = { val x = 2; x } trait T 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..db611eeb5ff2 --- /dev/null +++ b/tests/pos/traits.scala @@ -0,0 +1,25 @@ +trait B extends Object { + + val z: Int + +} + +trait T extends B { + + 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