From 31d299451204fe7feddb392f54d2fc2d6463fe2e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 31 Oct 2014 17:20:50 +0100 Subject: [PATCH 01/34] First version of mixin transform. Needs adaptations in getters/setters before it can be tested. --- src/dotty/tools/dotc/core/NameOps.scala | 6 +- src/dotty/tools/dotc/core/StdNames.scala | 3 +- src/dotty/tools/dotc/transform/Mixin.scala | 206 ++++++++++++++++++ src/dotty/tools/dotc/transform/SymUtils.scala | 15 ++ 4 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/Mixin.scala diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index beb3142d3876..752d9bcea133 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._ diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 99290f0848d0..80b9a7c37c89 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -121,7 +121,8 @@ object StdNames { 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/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala new file mode 100644 index 000000000000..2ad6153d6221 --- /dev/null +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -0,0 +1,206 @@ +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 NameOps._ +import collection.mutable + +/** This phase performs the following transformations: + * + * 1. (done in `traitDefs`) Map every concrete trait field + * + * val x: T = expr + * + * to the pair of definitions: + * + * val 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 field ` val x: T` in M, + * in order of textual occurrence: + * + * val x: T = super[M].initial$x + * + * 3.2 (done in `constrCall`) The call: + * + * super[M]. + * + * 3.3 (done in `setters`) For every concrete setter ` def x_=(y: T)` in M: + * + * def x_=(y: T) = () + * + * 3.4 (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.5 (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. + */ +class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "mixin" + + override def treeTransformPhase = thisTransform.next + + override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = + if (sym is Trait) { + lazy val newDecls = sym.decls.cloneScope + var hasInit = false + for (d <- sym.decls) + if (d.isTerm && !d.is(MethodOrDeferred)) { + hasInit = true + val initializer = ctx.newSymbol( + sym.symbol, + InitializerName(d.asTerm.name), + Protected | Synthetic | Method, + ExprType(d.info), + coord = d.coord) + newDecls.enter(initializer) + } + if (hasInit) { + val cinfo = sym.asClass.classInfo + sym.copySymDenotation(info = cinfo.derivedClassInfo(decls = newDecls)) + } + else sym + } + else if ((sym.isField || sym.isSetter) && sym.owner.is(Trait) && !sym.is(Deferred)) + sym.copySymDenotation(initFlags = sym.flags | Deferred) + else + sym + + private val MethodOrDeferred = Method | Deferred + private val PrivateOrDeferred = Private | Deferred + + private def traitDefs(cls: ClassSymbol, stats: List[Tree])(implicit ctx: Context): List[Tree] = { + val initBuf = new mutable.ListBuffer[Tree] + stats flatMap { + case stat: ValDef if !stat.rhs.isEmpty => + val vsym = stat.symbol + val isym = vsym.initializer + val rhs = Block(initBuf.toList, stat.rhs.changeOwner(vsym, isym)) + initBuf.clear() + List( + cpy.ValDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), + DefDef(stat.symbol.initializer, rhs)) + case stat: DefDef if stat.symbol.isSetter => + List(cpy.DefDef(stat)(rhs = EmptyTree)) + case stat: DefTree => + List(stat) + case stat => + initBuf += stat + Nil + } + } + + /** 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 + */ + private def rebindSuper(base: Symbol, acc: Symbol)(implicit ctx: Context): Symbol = { + var bcs = base.info.baseClasses.dropWhile(acc.owner != _).tail + var sym: Symbol = NoSymbol + val SuperAccessorName(memberName) = acc.name + ctx.debuglog("starting rebindsuper " + base + " " + memberName + ":" + acc.info + + " " + acc.owner + " " + base.info.baseClasses + "/" + bcs) + while (!bcs.isEmpty && sym == NoSymbol) { + val other = bcs.head.info.nonPrivateDecl(memberName) + if (ctx.settings.debug.value) + ctx.debuglog("rebindsuper " + bcs.head + " " + other + " " + other.info + + " " + other.symbol.is(Deferred)) + sym = other.matchingDenotation(base.thisType, acc.info).symbol + bcs = bcs.tail + } + sym + } + + private def superAccessors(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = + for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) + yield polyDefDef(implementation(cls, superAcc), forwarder(cls, rebindSuper(cls, superAcc))) + + private def traitInits(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = + for (field <- mixin.decls.filter(fld => fld.isField && !wasDeferred(fld)).toList) + yield DefDef(implementation(cls, field), ref(field.initializer).withPos(cls.pos)) + + private def setters(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = + for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) + yield DefDef(implementation(cls, setter), unitLiteral.withPos(cls.pos)) + + private def implementation(cls: ClassSymbol, member: Symbol)(implicit ctx: Context): TermSymbol = + member.copy(owner = cls, flags = member.flags &~ Deferred) + .enteredAfter(thisTransform).asTerm + + private def constrCall(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context) = + mixinSuperRef(cls, mixin.primaryConstructor) + + private def methodOverrides(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): 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(cls, meth), forwarder(cls, meth)) + } + + private def wasDeferred(sym: Symbol)(implicit ctx: Context) = + ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } + + private def mixinSuperRef(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = + Super(ref(cls), target.owner.name.asTypeName, inConstrCall = false).select(target) + + private def forwarder(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = + (targs: List[Type]) => (vrefss: List[List[Tree]]) => + mixinSuperRef(cls, target).appliedToTypes(targs).appliedToArgss(vrefss).withPos(cls.pos) + + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = tree.symbol.owner.asClass + val stats = tree.body + cpy.Template(tree)(body = + if (cls is Trait) traitDefs(cls, stats) + else + cls.mixins.flatMap { mixin => + assert(mixin is Trait) + traitInits(cls, mixin) ::: + constrCall(cls, mixin) :: + setters(cls, mixin) + superAccessors(cls, mixin) ::: + methodOverrides(cls, mixin) + } ::: stats) + } +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index ba45d3f04663..ffbc2978592a 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -69,4 +69,19 @@ class SymUtils(val self: Symbol) extends AnyVal { /** `fullName` where `$' is the separator character */ def flatName(implicit ctx: Context): Name = self.fullNameSeparated('$') + + /** The traits mixed into this class in linearization order. + * These are all inherited traits that are not also inherited by the superclass + */ + def mixins(implicit ctx: Context): List[ClassSymbol] = { + val cls = self.asClass + val superCls = cls.classInfo.parents.head.symbol + cls.baseClasses.tail.takeWhile(_ ne superCls) + } + + 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) } From 7c5a3ff5c5d5bc8844d981f55c3e2152976a42ac Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 1 Nov 2014 10:27:21 +0100 Subject: [PATCH 02/34] Adapt GettersSetters to new Mixin scheme. Trait getters are no longer needed. --- src/dotty/tools/dotc/core/NameOps.scala | 13 +---- src/dotty/tools/dotc/core/StdNames.scala | 1 - .../tools/dotc/transform/GettersSetters.scala | 55 +++++++++---------- src/dotty/tools/dotc/transform/Mixin.scala | 8 ++- src/dotty/tools/dotc/transform/SymUtils.scala | 3 +- 5 files changed, 36 insertions(+), 44 deletions(-) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 752d9bcea133..56f292b8772d 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -70,7 +70,6 @@ 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 @@ -226,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 @@ -242,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 = { diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 80b9a7c37c89..8393eb56f30a 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -119,7 +119,6 @@ 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 INITIALIZER_PREFIX: N = "initial$" diff --git a/src/dotty/tools/dotc/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala index 772a63e52a6c..b0f6c15c8496 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 diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 2ad6153d6221..515ac9c10f38 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -112,13 +112,17 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => case stat: ValDef if !stat.rhs.isEmpty => val vsym = stat.symbol val isym = vsym.initializer - val rhs = Block(initBuf.toList, stat.rhs.changeOwner(vsym, isym)) + val rhs = Block( + initBuf.toList, + stat.rhs.changeOwner(vsym, isym).ensureConforms(isym.info.widen)) initBuf.clear() List( cpy.ValDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), DefDef(stat.symbol.initializer, rhs)) case stat: DefDef if stat.symbol.isSetter => - List(cpy.DefDef(stat)(rhs = EmptyTree)) + List(cpy.DefDef(stat)( + mods = stat.mods | Deferred, + rhs = EmptyTree)) case stat: DefTree => List(stat) case stat => diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index ffbc2978592a..06882f1c614c 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -61,8 +61,7 @@ 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 From 0119ffd3e285e43b63fb9c43c1c8b009174a1987 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 1 Nov 2014 18:32:09 +0100 Subject: [PATCH 03/34] Avoid getting confused because of Scala2 local suffixes When inheriting from Scala2 traits we sometimes encounter names with a space at the end, denoting a local variable. Drop the space because our translation scheme has no room for it. --- src/dotty/tools/dotc/core/NameOps.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 56f292b8772d..299ead580464 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -276,6 +276,9 @@ object NameOps { -1 } + def stripScala2LocalSuffix: TermName = + if (name.endsWith(" ")) name.init.asTermName else name + /** The name of an accessor for protected symbols. */ def protectedAccessorName: TermName = PROTECTED_PREFIX ++ name.unexpandedName() From 43075bb832c3b4fe080c24a20ecf8c4737d5cdd2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 1 Nov 2014 18:33:56 +0100 Subject: [PATCH 04/34] Improved version of mixin. Now also handles all supercalls. Seems to do the right thing on pos/traits.scala. But does not pass most tests because the sym transformer forces too many things. --- src/dotty/tools/dotc/Compiler.scala | 5 +- src/dotty/tools/dotc/ast/tpd.scala | 4 +- .../tools/dotc/transform/Constructors.scala | 36 ++------ .../tools/dotc/transform/GettersSetters.scala | 1 + src/dotty/tools/dotc/transform/Mixin.scala | 84 +++++++++++++------ src/dotty/tools/dotc/transform/SymUtils.scala | 9 -- src/dotty/tools/dotc/typer/TypeAssigner.scala | 5 +- test/dotc/tests.scala | 2 +- tests/pos/traits.scala | 4 +- 9 files changed, 79 insertions(+), 71 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index f4690df08987..62759ba09f71 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -50,8 +50,9 @@ class Compiler { new Splitter), List(new ElimByName, new InterceptedMethods, - new Literalize, - new GettersSetters), + new Literalize), + List(new Mixin), + List(new GettersSetters), List(new Erasure), List(new CapturedVars, new Constructors), diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 74ba79176ea8..8c300c2317eb 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -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) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 7bde1ba4ffa4..c966e2678b66 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,7 +146,7 @@ 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) @@ -226,10 +200,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/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala index b0f6c15c8496..b5933cc4835e 100644 --- a/src/dotty/tools/dotc/transform/GettersSetters.scala +++ b/src/dotty/tools/dotc/transform/GettersSetters.scala @@ -105,6 +105,7 @@ import Decorators._ 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/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 515ac9c10f38..8116c723a562 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -11,7 +11,10 @@ import SymDenotations._ import Types._ import Decorators._ import DenotTransformers._ +import StdNames._ import NameOps._ +import ast.Trees._ +import util.Positions._ import collection.mutable /** This phase performs the following transformations: @@ -155,22 +158,21 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => private def superAccessors(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) - yield polyDefDef(implementation(cls, superAcc), forwarder(cls, rebindSuper(cls, superAcc))) + yield polyDefDef(implementation(cls, superAcc.asTerm), forwarder(cls, rebindSuper(cls, superAcc))) private def traitInits(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = for (field <- mixin.decls.filter(fld => fld.isField && !wasDeferred(fld)).toList) - yield DefDef(implementation(cls, field), ref(field.initializer).withPos(cls.pos)) + yield ValDef(implementation(cls, field.asTerm), superRef(cls, field.initializer, cls.pos)) private def setters(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) - yield DefDef(implementation(cls, setter), unitLiteral.withPos(cls.pos)) + yield DefDef(implementation(cls, setter.asTerm), unitLiteral.withPos(cls.pos)) - private def implementation(cls: ClassSymbol, member: Symbol)(implicit ctx: Context): TermSymbol = - member.copy(owner = cls, flags = member.flags &~ Deferred) - .enteredAfter(thisTransform).asTerm - - private def constrCall(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context) = - mixinSuperRef(cls, mixin.primaryConstructor) + private def implementation(cls: ClassSymbol, member: TermSymbol)(implicit ctx: Context): TermSymbol = + member.copy( + owner = cls, + name = member.name.stripScala2LocalSuffix, + flags = member.flags &~ Deferred).enteredAfter(thisTransform).asTerm private def methodOverrides(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = { def isOverridden(meth: Symbol) = meth.overridingSymbol(cls).is(Method, butNot = Deferred) @@ -179,32 +181,66 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => !isOverridden(meth) && !meth.allOverriddenSymbols.forall(_ is Deferred) for (meth <- mixin.decls.toList if needsDisambiguation(meth)) - yield polyDefDef(implementation(cls, meth), forwarder(cls, meth)) + yield polyDefDef(implementation(cls, meth.asTerm), forwarder(cls, meth)) } private def wasDeferred(sym: Symbol)(implicit ctx: Context) = ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } - private def mixinSuperRef(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = - Super(ref(cls), target.owner.name.asTypeName, inConstrCall = false).select(target) + private def superRef(cls: ClassSymbol, target: Symbol, pos: Position)(implicit ctx: Context) = { + val inTrait = target.owner is Trait + Super( + qual = This(cls), + mix = if (inTrait) target.owner.name.asTypeName else tpnme.EMPTY, + inConstrCall = target.isConstructor && !target.owner.is(Trait), + mixinClass = if (inTrait) target.owner else NoSymbol + ).select(target) + } private def forwarder(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = (targs: List[Type]) => (vrefss: List[List[Tree]]) => - mixinSuperRef(cls, target).appliedToTypes(targs).appliedToArgss(vrefss).withPos(cls.pos) + superRef(cls, target, cls.pos).appliedToTypes(targs).appliedToArgss(vrefss) override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { val cls = tree.symbol.owner.asClass val stats = tree.body - cpy.Template(tree)(body = - if (cls is Trait) traitDefs(cls, stats) - else - cls.mixins.flatMap { mixin => - assert(mixin is Trait) - traitInits(cls, mixin) ::: - constrCall(cls, mixin) :: - setters(cls, mixin) - superAccessors(cls, mixin) ::: - methodOverrides(cls, mixin) - } ::: stats) + val superCls = cls.classInfo.parents.head.symbol + val mixins = cls.baseClasses.tail.takeWhile(_ ne superCls) + + // If parent is a constructor call, pull out the call into a separate + // supercall constructor, which gets added to `superCalls`, and keep + // only the type. + val superCalls = new mutable.HashMap[Symbol, Tree] + def normalizeParent(tree: Tree) = tree match { + case superApp @ Apply(superSel @ Select(New(superType), nme.CONSTRUCTOR), superArgs) => + val constr = superSel.symbol + superCalls(constr.owner) = superRef(cls, constr, superSel.pos).appliedToArgs(superArgs) + superType + case tree: TypeTree => tree + } + val parentTypeTrees = tree.parents.map(normalizeParent) + + def supCalls(baseCls: Symbol): List[Tree] = superCalls.remove(baseCls) match { + case Some(call) => call :: Nil + case None => + if (baseCls is Interface) Nil + else superRef(cls, baseCls.primaryConstructor, cls.pos).appliedToNone :: Nil + } + + cpy.Template(tree)( + parents = parentTypeTrees, + body = + if (cls is Trait) traitDefs(cls, stats) + else { + val mixInits = mixins.flatMap { mixin => + assert(mixin is Trait) + traitInits(cls, mixin) ::: + supCalls(mixin) ::: + setters(cls, mixin) ::: + superAccessors(cls, mixin) ::: + methodOverrides(cls, mixin) + } + supCalls(superCls) ::: mixInits ::: stats + }) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index 06882f1c614c..449affb9e3d3 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -69,15 +69,6 @@ class SymUtils(val self: Symbol) extends AnyVal { /** `fullName` where `$' is the separator character */ def flatName(implicit ctx: Context): Name = self.fullNameSeparated('$') - /** The traits mixed into this class in linearization order. - * These are all inherited traits that are not also inherited by the superclass - */ - def mixins(implicit ctx: Context): List[ClassSymbol] = { - val cls = self.asClass - val superCls = cls.classInfo.parents.head.symbol - cls.baseClasses.tail.takeWhile(_ ne superCls) - } - def initializer(implicit ctx: Context): TermSymbol = self.owner.info.decl(InitializerName(self.asTerm.name)).symbol.asTerm diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index bb488bdc5c82..ed10c764454a 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -236,7 +236,7 @@ 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 @@ -249,7 +249,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/test/dotc/tests.scala b/test/dotc/tests.scala index a52f79abd0ba..2b0d7eb0837c 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,restoreScopes" + "-Ycheck:patternMatcher,mixin,gettersSetters,restoreScopes" ) val twice = List("#runs", "2", "-YnoDoubleBindings") diff --git a/tests/pos/traits.scala b/tests/pos/traits.scala index 15310d5a4e60..db611eeb5ff2 100644 --- a/tests/pos/traits.scala +++ b/tests/pos/traits.scala @@ -1,10 +1,10 @@ -trait B { +trait B extends Object { val z: Int } -trait T { +trait T extends B { var x = 2 From 97080a8b5dda38d50ff288dd60c92a06a04dd75a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 10:27:39 +0100 Subject: [PATCH 05/34] New option -Ydebug-owners Will print owners of symbols when displaying trees. Requires -Yprint-syms to be set also. --- src/dotty/tools/dotc/config/ScalaSettings.scala | 5 +++-- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) 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/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index e43aaa24fbec..50b73a35772e 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -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 = { From b8b06825fe575d7a6750874299cb356da99f5bc6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 10:30:13 +0100 Subject: [PATCH 06/34] Make cloneScope less forcefull. Motivation: Avoid needless forcing of symbols in scope. This is a problem when cloneScope is called in TreeTransforms. --- src/dotty/tools/dotc/core/Scopes.scala | 31 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) 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) { From b98ed348f16958c300cd3c98aac665f7a6d76bb2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 10:31:24 +0100 Subject: [PATCH 07/34] Fix problem in changeOwner The problem manifests itself when changing the owner of a class field. Being a ValDef, this is classified as a weak owner. But it's parent (the class) should not be owner-changed. --- src/dotty/tools/dotc/ast/tpd.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 8c300c2317eb..6aa715c8ed4e 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -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) From a2f558146e2d18a6ce1702bb7d48b69f36e38c80 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 11:14:38 +0100 Subject: [PATCH 08/34] Adaptation of Constructors to new mixin scheme. No more trait_setters are called. --- src/dotty/tools/dotc/transform/Constructors.scala | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index c966e2678b66..9420ce2c089a 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -152,10 +152,6 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor 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 { @@ -165,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) { @@ -189,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 From d28754110576070ea6f94e626005d149a3470d20 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 11:16:20 +0100 Subject: [PATCH 09/34] Adaptation of explicitOuter to trait initializers Trait initializers do not have outer params; need to avoid passing outer args to them. This problem did not manifest itself before because no trait constructor calls were generated before erasure. --- src/dotty/tools/dotc/transform/ExplicitOuter.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index d056d7e351a2..3bef4e26353f 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -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) @@ -264,11 +268,11 @@ object ExplicitOuter { case New(tpt) => singleton(outerPrefix(tpt.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 } From 7194afe4b9a0bb04700b023a1c29047eb6349f88 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 09:30:36 +0100 Subject: [PATCH 10/34] Take supercalls into account for statement context After Mixin, super calls can appear in statemenr sequences. They need to be typechecked and transformed using a special context. --- src/dotty/tools/dotc/core/Contexts.scala | 13 +++++++++---- src/dotty/tools/dotc/transform/MacroTransform.scala | 3 +-- src/dotty/tools/dotc/transform/TreeTransform.scala | 3 +-- src/dotty/tools/dotc/typer/Typer.scala | 3 +-- 4 files changed, 12 insertions(+), 10 deletions(-) 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/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/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 588a13fc93a1..2037bede8553 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -1200,11 +1200,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/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 3c36a1f256ef..59fda174df1e 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 From e445000dc747b55351f3bc60bb5f849bb6aac096 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 20:14:05 +0100 Subject: [PATCH 11/34] Fixed redundancy in membersNeedAsSeenFrom Array types are no longer used after erasure, so the test was redundant. --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ae8fceeb743b..99b1c9f7788b 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -560,7 +560,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) ) From 6098546fad7e6e8709cc18a126b0e655f439d5bc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 20:15:05 +0100 Subject: [PATCH 12/34] Fixed typing of supertypes. Previous version could fail where the thisType widened to an explicit self type. --- src/dotty/tools/dotc/typer/TypeAssigner.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index ed10c764454a..765c6bea7f41 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -238,7 +238,8 @@ trait TypeAssigner { 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 => From 3299df8baf74564627d474d47a26605b5e02a6d9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 20:16:48 +0100 Subject: [PATCH 13/34] Fix of computeDenot. Since we demand that after erasure all TermRefs are SymDenotations we need to assure this when computing the denotations of term refs in new phases. --- src/dotty/tools/dotc/core/Types.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d0ddfdd28b50..6dce5581de8f 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1173,8 +1173,12 @@ 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 + else if (ctx.erasedTypes && lastSymbol != null) + denotOfSym(lastSymbol) // avoid keeping non-sym denotations after erasure; they violate the assertErased contract + else + d.current } if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot") else if (d.exists) { @@ -1623,8 +1627,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 +1823,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 } From 1fad64325e2f457dee76629a8cf8c9427e931412 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 20:19:06 +0100 Subject: [PATCH 14/34] Select operations in erasure should fix symbols in types. Otherwise we get binding races, e.g. for pos/t2133.scala after mixin because we create same-named symbols in subclasses, and type refs now can refer to the subclass symbol where previously they refereed to the superclass symbol. To avoid a data race the new term ref will have a fixed symbol. --- src/dotty/tools/dotc/transform/Erasure.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 9f381bb8e7d9..fd90481572ff 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -277,7 +277,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 = From 0d74e591de620d52084736251f9f437a6dccf23a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Nov 2014 11:02:23 +0100 Subject: [PATCH 15/34] Superaccessors are methods Need to have Method flag set --- src/dotty/tools/dotc/transform/SuperAccessors.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index b11658efb7b6..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)) From 211d46e29a4d923a4f63ec8f71bcf3784f42e842 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Nov 2014 10:41:08 +0100 Subject: [PATCH 16/34] Cleanup of code for ExplicitOuter --- src/dotty/tools/dotc/transform/ExplicitOuter.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 3bef4e26353f..28d742b5ee27 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -265,8 +265,8 @@ 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 rewired to outer argument of secondary constructor in phase Constructors case TypeApply(Select(r, nme.asInstanceOf_), args) => From 0f7934d42892f36e28154779588a1d2a80d80616 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Nov 2014 22:12:01 +0100 Subject: [PATCH 17/34] Methods always shwoDcl as `def` This is reasonable because getters are only generated late in the compilation pipeline. No need to hide a getter as a val in error messages. --- src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 9fba7ec09816..eace75433cfd 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -303,7 +303,7 @@ class PlainPrinter(_ctx: Context) extends Printer { 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 "" } From f618e471b3f4f035221234cfeb980e52cc283701 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:39:35 +0100 Subject: [PATCH 18/34] Fixes to Unit handling in erasure 1. Erase unit results in getters to BoxedUnit. 2. Erase => Unit to ()Unit; was ()BoxedUnit 3. Make sure ValDefs have same type in tpt as in symbol, analogous to DefDefs. --- src/dotty/tools/dotc/TypeErasure.scala | 12 +++++++++--- src/dotty/tools/dotc/transform/Erasure.scala | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 2a55d6732f83..448dd3f57a6a 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) @@ -141,7 +142,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) = !( @@ -319,7 +325,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/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index fd90481572ff..b51a2360e28e 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -247,6 +247,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. @@ -369,12 +373,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) } From c67d6cf46cee95d8fab4375fa146877a11321d8d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:40:19 +0100 Subject: [PATCH 19/34] More systematic handling of Scala2LocalSuffix. Separate test instead of inline. --- src/dotty/tools/dotc/core/NameOps.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 299ead580464..bc15f6a066bc 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -76,7 +76,7 @@ object NameOps { 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 @@ -277,7 +277,7 @@ object NameOps { } def stripScala2LocalSuffix: TermName = - if (name.endsWith(" ")) name.init.asTermName else name + if (name.isScala2LocalSuffix) name.init.asTermName else name /** The name of an accessor for protected symbols. */ def protectedAccessorName: TermName = From e3a56c3be785159491af0ea3e0ed527845bee623 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:41:07 +0100 Subject: [PATCH 20/34] Accessor methods print as "method", not "val/var". --- src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- src/dotty/tools/dotc/typer/ErrorReporting.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index eace75433cfd..78ee32b98c3c 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 "" } 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) From 4b5e6305f95f97d06325889d8bb9d21995c4deff Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:41:53 +0100 Subject: [PATCH 21/34] Small polishings in docs and code. --- src/dotty/tools/dotc/core/Denotations.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 6dce5581de8f..3953562b2655 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1843,7 +1843,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 From 75c06fcb0116a9cb68aa06573431401a05713856 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:42:42 +0100 Subject: [PATCH 22/34] More robost handling of isSetter/isGetter Now survives the case where a field is written x_=, dissumulating a setter. --- src/dotty/tools/dotc/core/SymDenotations.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 99b1c9f7788b..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 @@ -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) From 0cd63ac2a8ae378d85922cbef4c3b4654fb75ea7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:43:11 +0100 Subject: [PATCH 23/34] Adding test for Unit vars. --- tests/pos/blockescapes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From fea26e104d202f3ac755f309d982af4d7ddd7bba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:45:30 +0100 Subject: [PATCH 24/34] New Mixin scheme. Split into two phases, ResolveSuper before Erasure and Mixin after. Likewise GettersSetters is split into Getters and Memoize. All tests pass, except two tests fail when compiled twice. Will investigate next why. --- src/dotty/tools/dotc/Compiler.scala | 8 +- src/dotty/tools/dotc/transform/Getters.scala | 67 +++++ src/dotty/tools/dotc/transform/Memoize.scala | 68 +++++ src/dotty/tools/dotc/transform/Mixin.scala | 257 +++++++----------- src/dotty/tools/dotc/transform/MixinOps.scala | 37 +++ .../tools/dotc/transform/ResolveSuper.scala | 95 +++++++ test/dotc/tests.scala | 4 +- 7 files changed, 365 insertions(+), 171 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/Getters.scala create mode 100644 src/dotty/tools/dotc/transform/Memoize.scala create mode 100644 src/dotty/tools/dotc/transform/MixinOps.scala create mode 100644 src/dotty/tools/dotc/transform/ResolveSuper.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 62759ba09f71..9da00f38a01d 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -50,10 +50,12 @@ class Compiler { new Splitter), List(new ElimByName, new InterceptedMethods, - new Literalize), - List(new Mixin), - List(new GettersSetters), + new Literalize, + new Getters), + List(new ResolveSuper), List(new Erasure), + List(new Mixin), + List(new Memoize), List(new CapturedVars, new Constructors), List(new LambdaLift, diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala new file mode 100644 index 000000000000..55b70a790733 --- /dev/null +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -0,0 +1,67 @@ +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) + */ +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/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala new file mode 100644 index 000000000000..db9349bdb76f --- /dev/null +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -0,0 +1,68 @@ +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 + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val sym = tree.symbol + if (sym.is(Accessor, butNot = NoFieldNeeded)) + if (sym.isGetter) { + val maybeMutable = if (sym is Stable) EmptyFlags else Mutable + println(i"add field for $sym") + val field = ctx.newSymbol( + owner = ctx.owner, + name = sym.name.asTermName.fieldName, + flags = Private | maybeMutable, + info = sym.info.resultType, + coord = tree.pos).enteredAfter(thisTransform) + var fieldInit = tree.rhs.changeOwner(sym, field) + val fieldDef = ValDef(field, fieldInit) + val getterDef = cpy.DefDef(tree)(rhs = ref(field)) + Thicket(fieldDef, getterDef) + } + else if (sym.isSetter) { + val Literal(Constant(())) = tree.rhs + assert(sym.field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") + val initializer = Assign(ref(sym.field), ref(tree.vparamss.head.head.symbol)) + cpy.DefDef(tree)(rhs = 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 index 8116c723a562..59bb8b910bf1 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -14,19 +14,19 @@ import DenotTransformers._ import StdNames._ import NameOps._ import ast.Trees._ -import util.Positions._ import collection.mutable +// todo: interface /** This phase performs the following transformations: * - * 1. (done in `traitDefs`) Map every concrete trait field + * 1. (done in `traitDefs`) Map every concrete trait getter * - * val x: T = expr + * def x(): T = expr * * to the pair of definitions: * - * val x: T - * protected def initial$x: T = { stats; expr } + * 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 @@ -45,33 +45,18 @@ import collection.mutable * 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 field ` val x: T` in M, + * 3.1 (done in `traitInits`) For every concrete trait getter ` def x(): T` in M, * in order of textual occurrence: * - * val x: T = super[M].initial$x + * def x(): T = super[M].initial$x() * - * 3.2 (done in `constrCall`) The call: + * 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) = () - * - * 3.4 (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.5 (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. */ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ @@ -81,166 +66,106 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => override def treeTransformPhase = thisTransform.next override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = - if (sym is Trait) { - lazy val newDecls = sym.decls.cloneScope - var hasInit = false - for (d <- sym.decls) - if (d.isTerm && !d.is(MethodOrDeferred)) { - hasInit = true - val initializer = ctx.newSymbol( - sym.symbol, - InitializerName(d.asTerm.name), - Protected | Synthetic | Method, - ExprType(d.info), - coord = d.coord) - newDecls.enter(initializer) - } - if (hasInit) { - val cinfo = sym.asClass.classInfo - sym.copySymDenotation(info = cinfo.derivedClassInfo(decls = newDecls)) - } - else sym - } - else if ((sym.isField || sym.isSetter) && sym.owner.is(Trait) && !sym.is(Deferred)) + if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait)) sym.copySymDenotation(initFlags = sym.flags | Deferred) else sym - private val MethodOrDeferred = Method | Deferred - private val PrivateOrDeferred = Private | Deferred - - private def traitDefs(cls: ClassSymbol, stats: List[Tree])(implicit ctx: Context): List[Tree] = { - val initBuf = new mutable.ListBuffer[Tree] - stats flatMap { - case stat: ValDef if !stat.rhs.isEmpty => - val vsym = stat.symbol - val isym = vsym.initializer - val rhs = Block( - initBuf.toList, - stat.rhs.changeOwner(vsym, isym).ensureConforms(isym.info.widen)) - initBuf.clear() - List( - cpy.ValDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), - DefDef(stat.symbol.initializer, 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 - } + 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 } - /** 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 - */ - private def rebindSuper(base: Symbol, acc: Symbol)(implicit ctx: Context): Symbol = { - var bcs = base.info.baseClasses.dropWhile(acc.owner != _).tail - var sym: Symbol = NoSymbol - val SuperAccessorName(memberName) = acc.name - ctx.debuglog("starting rebindsuper " + base + " " + memberName + ":" + acc.info + - " " + acc.owner + " " + base.info.baseClasses + "/" + bcs) - while (!bcs.isEmpty && sym == NoSymbol) { - val other = bcs.head.info.nonPrivateDecl(memberName) - if (ctx.settings.debug.value) - ctx.debuglog("rebindsuper " + bcs.head + " " + other + " " + other.info + - " " + other.symbol.is(Deferred)) - sym = other.matchingDenotation(base.thisType, acc.info).symbol - bcs = bcs.tail + 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 + } } - sym - } - - private def superAccessors(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = - for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) - yield polyDefDef(implementation(cls, superAcc.asTerm), forwarder(cls, rebindSuper(cls, superAcc))) - - private def traitInits(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = - for (field <- mixin.decls.filter(fld => fld.isField && !wasDeferred(fld)).toList) - yield ValDef(implementation(cls, field.asTerm), superRef(cls, field.initializer, cls.pos)) - private def setters(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = - for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) - yield DefDef(implementation(cls, setter.asTerm), unitLiteral.withPos(cls.pos)) - - private def implementation(cls: ClassSymbol, member: TermSymbol)(implicit ctx: Context): TermSymbol = - member.copy( - owner = cls, - name = member.name.stripScala2LocalSuffix, - flags = member.flags &~ Deferred).enteredAfter(thisTransform).asTerm - - private def methodOverrides(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): 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(cls, meth.asTerm), forwarder(cls, meth)) - } - - private def wasDeferred(sym: Symbol)(implicit ctx: Context) = - ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } + def transformSuper(tree: Tree): Tree = { + val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree + superRef(tree.symbol, tree.pos).appliedToArgs(args) + } - private def superRef(cls: ClassSymbol, target: Symbol, pos: Position)(implicit ctx: Context) = { - val inTrait = target.owner is Trait - Super( - qual = This(cls), - mix = if (inTrait) target.owner.name.asTypeName else tpnme.EMPTY, - inConstrCall = target.isConstructor && !target.owner.is(Trait), - mixinClass = if (inTrait) target.owner else NoSymbol - ).select(target) - } + val superCalls = ( + for (p <- impl.parents if p.symbol.isConstructor) + yield p.symbol.owner -> transformSuper(p) + ).toMap - private def forwarder(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = - (targs: List[Type]) => (vrefss: List[List[Tree]]) => - superRef(cls, target, cls.pos).appliedToTypes(targs).appliedToArgss(vrefss) + 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(Interface) || 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 +*/ + } + } - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { - val cls = tree.symbol.owner.asClass - val stats = tree.body - val superCls = cls.classInfo.parents.head.symbol - val mixins = cls.baseClasses.tail.takeWhile(_ ne superCls) + def wasDeferred(sym: Symbol) = + ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } - // If parent is a constructor call, pull out the call into a separate - // supercall constructor, which gets added to `superCalls`, and keep - // only the type. - val superCalls = new mutable.HashMap[Symbol, Tree] - def normalizeParent(tree: Tree) = tree match { - case superApp @ Apply(superSel @ Select(New(superType), nme.CONSTRUCTOR), superArgs) => - val constr = superSel.symbol - superCalls(constr.owner) = superRef(cls, constr, superSel.pos).appliedToArgs(superArgs) - superType - case tree: TypeTree => tree - } - val parentTypeTrees = tree.parents.map(normalizeParent) + def traitInits(mixin: ClassSymbol): List[Tree] = + for (getter <- mixin.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) + yield { + println(i"impl for getter $getter!") + DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone) + } - def supCalls(baseCls: Symbol): List[Tree] = superCalls.remove(baseCls) match { - case Some(call) => call :: Nil - case None => - if (baseCls is Interface) Nil - else superRef(cls, baseCls.primaryConstructor, cls.pos).appliedToNone :: Nil - } + 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(tree)( - parents = parentTypeTrees, + cpy.Template(impl)( + parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)), body = - if (cls is Trait) traitDefs(cls, stats) + if (cls is Trait) traitDefs(impl.body) else { val mixInits = mixins.flatMap { mixin => - assert(mixin is Trait) - traitInits(cls, mixin) ::: - supCalls(mixin) ::: - setters(cls, mixin) ::: - superAccessors(cls, mixin) ::: - methodOverrides(cls, mixin) + traitInits(mixin) ::: superCallOpt(mixin) ::: setters(mixin) } - supCalls(superCls) ::: mixInits ::: stats + superCallOpt(superCls) ::: mixInits ::: impl.body }) } -} \ No newline at end of file +} 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/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala new file mode 100644 index 000000000000..1fd8b122f4a5 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -0,0 +1,95 @@ +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. + */ +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 + println(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) + println(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/test/dotc/tests.scala b/test/dotc/tests.scala index 2b0d7eb0837c..39c6658d0644 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -104,12 +104,12 @@ class tests extends CompilerTest { @Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice) @Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice) @Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", twice)(allowDeepSubtypes) - @Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling", twice)(allowDeepSubtypes) + @Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling"/*, twice*/)(allowDeepSubtypes) @Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice) @Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", twice) @Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice) @Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice) - @Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer", twice) + @Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer"/*, twice*/) @Test def dotc_util = compileDir(dotcDir + "tools/dotc/util", twice) @Test def tools_io = compileDir(dotcDir + "tools/io", twice) //@Test def tools = compileDir(dotcDir + "tools", "-deep" :: Nil)(allowDeepSubtypes) From df0229e4754e8d512aef68c7d2ffbd1d79cb5df5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 12:02:54 +0100 Subject: [PATCH 25/34] Retract of optimization in computeDenot Needs a weakening of an assertion in assertErased --- src/dotty/tools/dotc/core/Types.scala | 11 +++++++++-- src/dotty/tools/dotc/transform/Erasure.scala | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 3953562b2655..5d95fae7600a 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1175,8 +1175,15 @@ object Types { case d => if (d.validFor.runId != ctx.period.runId) loadDenot - else if (ctx.erasedTypes && lastSymbol != null) - denotOfSym(lastSymbol) // avoid keeping non-sym denotations after erasure; they violate the assertErased contract + // 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 } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index b51a2360e28e..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 _ => } From a8cd662ce26e915fb045bfd34b7ac9aa88f69721 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 12:05:54 +0100 Subject: [PATCH 26/34] Fixed handling of ThisTypes in TypeErasure. Need to be treated like TermRefs. --- src/dotty/tools/dotc/TypeErasure.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 448dd3f57a6a..4a492560f581 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -108,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) } @@ -271,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) => From 0d0a37f755bece2b44de65ce4bbcf34184813fae Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 12:07:47 +0100 Subject: [PATCH 27/34] Better error message for adaptations of implicit methods Ig an implicit method has a result type which does not match the expected type, the new case fires. We avoid to diagnose missing arguments, because that's really confusing for an implicit method. --- src/dotty/tools/dotc/typer/Typer.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 59fda174df1e..a5396d445fa5 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1245,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 From 383ba355f1688ccdb694bfdb16c0c98feaca6e25 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 12:10:08 +0100 Subject: [PATCH 28/34] Reinstantiated full tests. The fix to Types in 9bb35512 (Retract of optimization in computeDenot) allowed the two tests to compile "twice" again. --- test/dotc/tests.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 39c6658d0644..2b0d7eb0837c 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -104,12 +104,12 @@ class tests extends CompilerTest { @Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice) @Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice) @Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", twice)(allowDeepSubtypes) - @Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling"/*, twice*/)(allowDeepSubtypes) + @Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling", twice)(allowDeepSubtypes) @Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice) @Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", twice) @Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice) @Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice) - @Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer"/*, twice*/) + @Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer", twice) @Test def dotc_util = compileDir(dotcDir + "tools/dotc/util", twice) @Test def tools_io = compileDir(dotcDir + "tools/io", twice) //@Test def tools = compileDir(dotcDir + "tools", "-deep" :: Nil)(allowDeepSubtypes) From 0ad4dea891701b8ca14014549056806a41d2f385 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 18:42:55 +0100 Subject: [PATCH 29/34] Fixed bug in treating Thickets in TreeTransform --- src/dotty/tools/dotc/transform/TreeTransform.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 2037bede8553..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)) From b07c30b09851334c40298725daa5503e97ae305a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 18:44:30 +0100 Subject: [PATCH 30/34] New functionality: changeOwnerAfter Changes owners after a phase without copying the tree. This should be more suitable for the changeOwner operations used in the tree transforms so far, which are linear, i.e. no tree duplication is needed. --- src/dotty/tools/dotc/ast/tpd.scala | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 6aa715c8ed4e..5aebbd6fe005 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 @@ -537,6 +537,28 @@ 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) { + println(i"change owner $from -> $to of $sym") + 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) From 60803fa016987e05ebb2e2c80ba84d04aafc6ef3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 18:45:17 +0100 Subject: [PATCH 31/34] Merge memoize with miniphases following it into one group. --- src/dotty/tools/dotc/Compiler.scala | 8 ++-- src/dotty/tools/dotc/transform/Memoize.scala | 40 +++++++++++++------- src/dotty/tools/dotc/transform/Mixin.scala | 1 - test/dotc/tests.scala | 2 +- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 9da00f38a01d..ae5d5c1003e7 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -51,12 +51,12 @@ class Compiler { List(new ElimByName, new InterceptedMethods, new Literalize, - new Getters), - List(new ResolveSuper), + new Getters, + new ResolveSuper), List(new Erasure), List(new Mixin), - List(new Memoize), - List(new CapturedVars, + List(new Memoize, + new CapturedVars, new Constructors), List(new LambdaLift, new Flatten, diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala index db9349bdb76f..6b14d77146a3 100644 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -37,28 +37,40 @@ import Decorators._ override def phaseName = "memoize" override def treeTransformPhase = thisTransform.next + 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 maybeMutable = if (sym is Stable) EmptyFlags else Mutable - println(i"add field for $sym") - val field = ctx.newSymbol( - owner = ctx.owner, - name = sym.name.asTermName.fieldName, - flags = Private | maybeMutable, - info = sym.info.resultType, - coord = tree.pos).enteredAfter(thisTransform) - var fieldInit = tree.rhs.changeOwner(sym, field) - val fieldDef = ValDef(field, fieldInit) - val getterDef = cpy.DefDef(tree)(rhs = ref(field)) + 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 - assert(sym.field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") - val initializer = Assign(ref(sym.field), ref(tree.vparamss.head.head.symbol)) - cpy.DefDef(tree)(rhs = initializer) + 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 diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 59bb8b910bf1..668aca215ad0 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -149,7 +149,6 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitInits(mixin: ClassSymbol): List[Tree] = for (getter <- mixin.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { - println(i"impl for getter $getter!") DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone) } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 2b0d7eb0837c..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,mixin,gettersSetters,restoreScopes" + "-Ycheck:resolveSuper,mixin,restoreScopes" ) val twice = List("#runs", "2", "-YnoDoubleBindings") From 06c50af6551f56314f0587878035d7d7c4d8307b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 18:48:49 +0100 Subject: [PATCH 32/34] Merge mixin with the group of miniphases following it. --- src/dotty/tools/dotc/Compiler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index ae5d5c1003e7..819b60d48332 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -54,8 +54,8 @@ class Compiler { new Getters, new ResolveSuper), List(new Erasure), - List(new Mixin), - List(new Memoize, + List(new Mixin, + new Memoize, new CapturedVars, new Constructors), List(new LambdaLift, From f2e3489e419a104ad5f30db68eeaf035a0f3339b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Nov 2014 10:36:37 +0100 Subject: [PATCH 33/34] More docs and removing print statements --- src/dotty/tools/dotc/Compiler.scala | 3 +-- src/dotty/tools/dotc/ast/tpd.scala | 4 +--- src/dotty/tools/dotc/transform/Getters.scala | 2 ++ src/dotty/tools/dotc/transform/Memoize.scala | 6 ++++++ src/dotty/tools/dotc/transform/Mixin.scala | 10 ++++++++-- src/dotty/tools/dotc/transform/ResolveSuper.scala | 9 ++++++--- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 819b60d48332..004a3868c8af 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -46,7 +46,6 @@ class Compiler { new TailRec), List(new PatternMatcher, new ExplicitOuter, - // new LazyValTranformContext().transformer, // disabled, awaiting fixes new Splitter), List(new ElimByName, new InterceptedMethods, @@ -55,7 +54,7 @@ class Compiler { new ResolveSuper), List(new Erasure), List(new Mixin, - new Memoize, + new Memoize, // TODO: Make LazyVals a part of this phase new CapturedVars, new Constructors), List(new LambdaLift, diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 5aebbd6fe005..735b218e3d56 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -546,10 +546,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def traverse(tree: Tree) = tree match { case tree: DefTree => val sym = tree.symbol - if (sym.denot(ctx.withPhase(trans)).owner == from) { - println(i"change owner $from -> $to of $sym") + if (sym.denot(ctx.withPhase(trans)).owner == from) sym.copySymDenotation(owner = to).installAfter(trans) - } if (sym.isWeakOwner) traverseChildren(tree) case _ => traverseChildren(tree) diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala index 55b70a790733..4ea9d2c6b6a4 100644 --- a/src/dotty/tools/dotc/transform/Getters.scala +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -36,6 +36,8 @@ import Decorators._ * * 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._ diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala index 6b14d77146a3..ef70b9ecf34a 100644 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -37,6 +37,12 @@ import Decorators._ 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)) { diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 668aca215ad0..1d342404a272 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -13,10 +13,10 @@ import Decorators._ import DenotTransformers._ import StdNames._ import NameOps._ +import Phases._ import ast.Trees._ import collection.mutable -// todo: interface /** This phase performs the following transformations: * * 1. (done in `traitDefs`) Map every concrete trait getter @@ -57,14 +57,20 @@ import collection.mutable * 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) diff --git a/src/dotty/tools/dotc/transform/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala index 1fd8b122f4a5..23ff45a7c0b1 100644 --- a/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -39,6 +39,9 @@ import collection.mutable * * 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._ @@ -61,11 +64,11 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th 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 - println(i"starting rebindsuper from $cls of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") + 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) - println(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}") + 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 } From 83734e1962c11b3e699ece5787caf845cbfa6c0d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Nov 2014 10:35:54 +0100 Subject: [PATCH 34/34] Generalize symbolic refs Previously, there was a switch in SymDenotation#termRef to create a WithFixedSym ref when after erasure. This was done to fix a broken test, but it's clearly incomplete. The scheme is now replaced by one which always creates WithFixedSym Termrefs after erasure. --- src/dotty/tools/dotc/core/SymDenotations.scala | 5 ++--- src/dotty/tools/dotc/core/Types.scala | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 0d92a8e5f60d..dfb58f68b20d 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -896,9 +896,8 @@ 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 = // TODO generalize - if (ctx.erasedTypes) TermRef.withFixedSym(owner.thisType, name.asTermName, symbol.asTerm) - else TermRef.withSigAndDenot(owner.thisType, name.asTermName, signature, this) + override def termRefWithSig(implicit ctx: Context): TermRef = + 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 5d95fae7600a..5228c077eb65 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1183,7 +1183,7 @@ object Types { // That's why the branch is disabled. // // else if (ctx.erasedTypes && lastSymbol != null) - // denotOfSym(lastSymbol) + // denotOfSym(lastSymbol) else d.current } @@ -1516,7 +1516,7 @@ object Types { * signature, if denotation is not yet completed. */ def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef = { - if ((prefix eq NoPrefix) || denot.symbol.isFresh) + if ((prefix eq NoPrefix) || denot.symbol.isFresh || ctx.erasedTypes) apply(prefix, denot.symbol.asTerm) else denot match { case denot: SymDenotation if denot.isCompleted => withSig(prefix, name, denot.signature) @@ -1538,7 +1538,7 @@ object Types { * (2) The name in the term ref need not be the same as the name of the Symbol. */ def withSymAndName(prefix: Type, sym: TermSymbol, name: TermName)(implicit ctx: Context): TermRef = - if ((prefix eq NoPrefix) || sym.isFresh) + if ((prefix eq NoPrefix) || sym.isFresh || ctx.erasedTypes) withFixedSym(prefix, name, sym) else if (sym.defRunId != NoRunId && sym.isCompleted) withSig(prefix, name, sym.signature) withSym (sym, sym.signature) @@ -1549,7 +1549,7 @@ object Types { * (which must be completed). */ def withSig(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef = - if ((prefix eq NoPrefix) || sym.isFresh) withFixedSym(prefix, sym.name, sym) + if ((prefix eq NoPrefix) || sym.isFresh || ctx.erasedTypes) withFixedSym(prefix, sym.name, sym) else withSig(prefix, sym.name, sym.signature).withSym(sym, sym.signature) /** Create a term ref with given prefix, name and signature */ @@ -1558,7 +1558,7 @@ object Types { /** Create a term ref with given prefix, name, signature, and initial denotation */ def withSigAndDenot(prefix: Type, name: TermName, sig: Signature, denot: Denotation)(implicit ctx: Context): TermRef = { - if ((prefix eq NoPrefix) || denot.symbol.isFresh) + if ((prefix eq NoPrefix) || denot.symbol.isFresh || ctx.erasedTypes) withFixedSym(prefix, denot.symbol.asTerm.name, denot.symbol.asTerm) else withSig(prefix, name, sig)