diff options
Diffstat (limited to 'src/dotty/tools/dotc/transform/Mixin.scala')
-rw-r--r-- | src/dotty/tools/dotc/transform/Mixin.scala | 99 |
1 files changed, 80 insertions, 19 deletions
diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 63e680414..de6cde8f2 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -20,7 +20,7 @@ import collection.mutable /** This phase performs the following transformations: * - * 1. (done in `traitDefs`) Map every concrete trait getter + * 1. (done in `traitDefs` and `transformSym`) Map every concrete trait getter * * <mods> def x(): T = expr * @@ -46,32 +46,45 @@ 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 getter `<mods> def x(): T` in M, - * in order of textual occurrence, produce the following: + * 3.1 (done in `traitInits`) For every parameter accessor `<mods> def x(): T` in M, + * in order of textual occurrence, add * - * 3.1.1 If `x` is also a member of `C`, and M is a Dotty trait: + * <mods> def x() = e + * + * where `e` is the constructor argument in C that corresponds to `x`. Issue + * an error if no such argument exists. + * + * 3.2 (done in `traitInits`) For every concrete trait getter `<mods> def x(): T` in M + * which is not a parameter accessor, in order of textual occurrence, produce the following: + * + * 3.2.1 If `x` is also a member of `C`, and M is a Dotty trait: * * <mods> def x(): T = super[M].initial$x() * - * 3.1.2 If `x` is also a member of `C`, and M is a Scala 2.x trait: + * 3.2.2 If `x` is also a member of `C`, and M is a Scala 2.x trait: * * <mods> def x(): T = _ * - * 3.1.3 If `x` is not a member of `C`, and M is a Dotty trait: + * 3.2.3 If `x` is not a member of `C`, and M is a Dotty trait: * * super[M].initial$x() * - * 3.1.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added. + * 3.2.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added. * * - * 3.2 (done in `superCallOpt`) The call: + * 3.3 (done in `superCallOpt`) The call: * * super[M].<init> * - * 3.3 (done in `setters`) For every concrete setter `<mods> def x_=(y: T)` in M: + * 3.4 (done in `setters`) For every concrete setter `<mods> def x_=(y: T)` in M: * * <mods> def x_=(y: T) = () * + * 4. (done in `transformTemplate` and `transformSym`) Drop all parameters from trait + * constructors. + * + * 5. (done in `transformSym`) Drop ParamAccessor flag from all parameter accessors in traits. + * * 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 @@ -86,7 +99,9 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => 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).ensureNotPrivate + sym.copySymDenotation(initFlags = sym.flags &~ ParamAccessor | Deferred).ensureNotPrivate + else if (sym.isConstructor && sym.owner.is(Trait) && sym.info.firstParamTypes.nonEmpty) + sym.copySymDenotation(info = MethodType(Nil, sym.info.resultType)) else sym @@ -111,7 +126,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitDefs(stats: List[Tree]): List[Tree] = { val initBuf = new mutable.ListBuffer[Tree] stats.flatMap({ - case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) => + case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) => // make initializer that has all effects of previous getter, // replace getter rhs with empty tree. val vsym = stat.symbol @@ -131,15 +146,22 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => }) ++ initBuf } - def transformSuper(tree: Tree): Tree = { + /** Map constructor call to a pair of a supercall and a list of arguments + * to be used as initializers of trait parameters if the target of the call + * is a trait. + */ + def transformConstructor(tree: Tree): (Tree, List[Tree]) = { val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree - superRef(tree.symbol, tree.pos).appliedToArgs(args) + val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil) + (superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs) } - val superCalls = ( + val superCallsAndArgs = ( for (p <- impl.parents if p.symbol.isConstructor) - yield p.symbol.owner -> transformSuper(p) + yield p.symbol.owner -> transformConstructor(p) ).toMap + val superCalls = superCallsAndArgs.mapValues(_._1) + val initArgs = superCallsAndArgs.mapValues(_._2) def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match { case Some(call) => @@ -155,24 +177,63 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def wasDeferred(sym: Symbol) = ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } - def traitInits(mixin: ClassSymbol): List[Tree] = + def traitInits(mixin: ClassSymbol): List[Tree] = { + var argNum = 0 + def nextArgument() = initArgs.get(mixin) match { + case Some(arguments) => + try arguments(argNum) finally argNum += 1 + case None => + val (msg, pos) = impl.parents.find(_.tpe.typeSymbol == mixin) match { + case Some(parent) => ("lacks argument list", parent.pos) + case None => + ("""is indirectly implemented, + |needs to be implemented directly so that arguments can be passed""".stripMargin, + cls.pos) + } + ctx.error(i"parameterized $mixin $msg", pos) + EmptyTree + } + for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { val isScala2x = mixin.is(Scala2x) def default = Underscore(getter.info.resultType) def initial = transformFollowing(superRef(initializer(getter)).appliedToNone) - if (isCurrent(getter) || getter.is(ExpandedName)) + + /** A call to the implementation of `getter` in `mixin`'s implementation class */ + def lazyGetterCall = { + def canbeImplClassGetter(sym: Symbol) = sym.info.firstParamTypes match { + case t :: Nil => t.isDirectRef(mixin) + case _ => false + } + val implClassGetter = mixin.implClass.info.nonPrivateDecl(getter.name) + .suchThat(canbeImplClassGetter).symbol + ref(mixin.implClass).select(implClassGetter).appliedTo(This(cls)) + } + + if (isCurrent(getter) || getter.is(ExpandedName)) { + val rhs = + if (ctx.atPhase(thisTransform)(implicit ctx => getter.is(ParamAccessor))) nextArgument() + else if (isScala2x) + if (getter.is(Lazy)) lazyGetterCall + else Underscore(getter.info.resultType) + else transformFollowing(superRef(initializer(getter)).appliedToNone) // transformFollowing call is needed to make memoize & lazy vals run - transformFollowing( - DefDef(implementation(getter.asTerm), if (isScala2x) default else initial)) + transformFollowing(DefDef(implementation(getter.asTerm), rhs)) + } else if (isScala2x) EmptyTree else initial } + } def setters(mixin: ClassSymbol): List[Tree] = for (setter <- mixin.info.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) yield DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos)) cpy.Template(impl)( + constr = + if (cls.is(Trait) && impl.constr.vparamss.flatten.nonEmpty) + cpy.DefDef(impl.constr)(vparamss = Nil :: Nil) + else impl.constr, parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)), body = if (cls is Trait) traitDefs(impl.body) |