aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-09-23 12:44:31 +0200
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2014-10-11 08:24:36 +0200
commit8ec91d472f28ec1bf1c957815e5ca987410b7c47 (patch)
tree2c46ff35141ac9c889590fc73d0778405803358f
parent478b97a1cbc67af97bb8be98a36c5b8173898f2b (diff)
downloaddotty-8ec91d472f28ec1bf1c957815e5ca987410b7c47.tar.gz
dotty-8ec91d472f28ec1bf1c957815e5ca987410b7c47.tar.bz2
dotty-8ec91d472f28ec1bf1c957815e5ca987410b7c47.zip
Completed constructors phase
1) Type parameters are now copied to accessors 2) Constructors also work for traits 2) makes it possible do to mixin after constructors.
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala7
-rw-r--r--src/dotty/tools/dotc/transform/Constructors.scala205
2 files changed, 141 insertions, 71 deletions
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index 64c87ecb8..de27282fb 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -543,12 +543,15 @@ object Flags {
/** Labeled private[this] */
final val PrivateLocal = allOf(Private, Local)
- /** A private parameter accessor */
+ /** A private[this] parameter accessor */
final val PrivateLocalParamAccessor = allOf(Private, Local, ParamAccessor)
- /** A private parameter */
+ /** A private[this] parameter */
final val PrivateLocalParam = allOf(Private, Local, Param)
+ /** A private parameter accessor */
+ final val PrivateParamAccessor = allOf(Private, ParamAccessor)
+
/** A local parameter */
final val ParamAndLocal = allOf(Param, Local)
diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala
index d03b90a27..3d2f5a0d7 100644
--- a/src/dotty/tools/dotc/transform/Constructors.scala
+++ b/src/dotty/tools/dotc/transform/Constructors.scala
@@ -16,9 +16,11 @@ import SymDenotations._
import Types._
import Decorators._
import DenotTransformers._
-import ExplicitOuter.outerParamAccessor
+import collection.mutable
-/** This transform moves initializers from body to constructor.
+/** This transform
+ * - moves initializers from body to constructor.
+ * - makes all supercalls explicit
*/
class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform =>
import tpd._
@@ -28,21 +30,37 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
override def treeTransformPhase = thisTransform.next
+ /** Symbols that are owned by either <local dummy> or a class field move into the
+ * primary constructor.
+ */
override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = {
def ownerBecomesConstructor(owner: Symbol): Boolean =
- (owner.isLocalDummy ||
- owner.isTerm && !owner.is(Method) && owner.owner.isClass) &&
- !owner.enclosingClass.is(Trait) // TODO: Remove qualification once Mixin is operational
+ (owner.isLocalDummy || owner.isTerm && !owner.is(Method)) &&
+ owner.owner.isClass
if (ownerBecomesConstructor(sym.owner))
sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor)
else sym
}
- private def intoConstr(accessors: List[Symbol], params: List[Symbol]) = new TreeMap {
+ /** @return true if after ExplicitOuter, all references from this tree go via an
+ * outer link, so no parameter accessors need to be rewired to parameters
+ */
+ private def noDirectRefsFrom(tree: Tree)(implicit ctx: Context) =
+ tree.isDef && tree.symbol.isClass && !tree.symbol.is(InSuperCall)
+
+ /** Adjustments performed when moving code into the constructor:
+ * (1) Replace references to param accessors by constructor parameters
+ * except possibly references to mutable variables, if `excluded = Mutable`.
+ * (Mutable parameters should be replaced only during the super call)
+ * (2) If the parameter accessor reference was to an alias getter,
+ * drop the () when replacing by the parameter.
+ */
+ class IntoConstrMap(accessors: List[Symbol], params: List[Symbol]) extends TreeMap {
+ private var excluded: FlagSet = _
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
case Ident(_) | Select(This(_), _) =>
val sym = tree.symbol
- if (sym is ParamAccessor) {
+ if (sym is (ParamAccessor, butNot = excluded)) {
val param = sym.subst(accessors, params)
if (param ne sym) ref(param).withPos(tree.pos)
else tree
@@ -50,79 +68,128 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
else tree
case Apply(fn, Nil) =>
val fn1 = transform(fn)
- if ((fn1 ne fn) &&
- fn1.symbol.is(Param) &&
- fn1.symbol.owner.isPrimaryConstructor) {
- // Two possible cases, which each need their adaptation:
- if (fn1.symbol.initial.info.isInstanceOf[ExprType])
- // it's either a call-by-name parameter, which is erased to Function0,
- // then we need to insert an apply.
- cpy.Apply(tree)(Select(fn1, nme.apply), Nil).ensureConforms(tree.tpe)
- else
- // or original accessor was an alias accessor, then we need to drop the ()
- fn1
- }
+ if ((fn1 ne fn) && fn1.symbol.is(Param) && fn1.symbol.owner.isPrimaryConstructor)
+ fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass
else cpy.Apply(tree)(fn1, Nil)
case _ =>
- super.transform(tree)
+ if (noDirectRefsFrom(tree)) tree else super.transform(tree)
}
- }
- private def splitStats(stats: List[Tree])(implicit ctx: Context): (List[Tree], List[Tree]) = stats match {
- case stat :: stats1 =>
- val (constrStats, clsStats) = splitStats(stats1)
- stat match {
- case stat @ ValDef(mods, name, tpt, rhs) if !rhs.isEmpty =>
- val inits =
- if (isWildcardArg(rhs)) Nil
- else Assign(ref(stat.symbol), rhs).withPos(stat.pos) :: Nil
- (inits ::: constrStats, cpy.ValDef(stat)(rhs = EmptyTree) :: clsStats)
- case _: DefTree =>
- (constrStats, stat :: clsStats)
- case _ =>
- (stat :: constrStats, clsStats)
- }
- case Nil =>
- (Nil, Nil)
+ def apply(tree: Tree, excluded: FlagSet)(implicit ctx: Context): Tree = {
+ this.excluded = excluded
+ transform(tree)
+ }
}
override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = {
val cls = ctx.owner.asClass
- if (cls is Trait) tree
- else {
- val constr @ DefDef(_, nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr
- val (superApp @ Apply(
- superSel @ Select(
- superNew @ New(superType),
- nme.CONSTRUCTOR),
- superArgs)) :: traitParents = tree.parents
- var accessors = cls.paramAccessors.filterNot(_.isSetter)
- var vparamsWithOuter = vparams
- if (!accessors.hasSameLengthAs(vparams)) {
- accessors.reverse match {
- case last :: _ if (last.name == nme.OUTER) =>
- accessors = last :: accessors.init
- vparamsWithOuter = ValDef(last.asTerm) :: vparams
+ val constr @ DefDef(_, nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr
+
+ // Produce aligned accessors and constructor parameters. We have to adjust
+ // for any outer parameters, which are last in the sequence of original
+ // parameter accessors but should come first in the constructor parameter list.
+ var accessors = cls.paramAccessors.filterNot(_.isSetter)
+ var vparamsWithOuter = vparams
+ if (!accessors.hasSameLengthAs(vparams)) {
+ accessors.reverse match {
+ case last :: _ if (last.name == nme.OUTER) =>
+ accessors = last :: accessors.init // align wth calling convention
+ vparamsWithOuter = ValDef(last.asTerm) :: vparams
+ case _ =>
+ }
+ assert(accessors.hasSameLengthAs(vparamsWithOuter),
+ i"lengths differ for $cls, param accs = $accessors, params = $vparamsWithOuter")
+ }
+ val paramSyms = vparamsWithOuter map (_.symbol)
+
+ val intoConstr = new IntoConstrMap(accessors, paramSyms)
+ 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(_, excluded = if (toClass) Mutable else EmptyFlags))
+ 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)
+
+ // Split class body into statements that go into constructor and
+ // definitions that are kept as members of the class.
+ def splitStats(stats: List[Tree]): (List[Tree], List[Tree]) = stats match {
+ case stat :: stats1 =>
+ val (constrStats, clsStats) = splitStats(stats1)
+ stat match {
+ case stat @ ValDef(mods, name, tpt, rhs) =>
+ val inits =
+ if (rhs.isEmpty || isWildcardArg(rhs)) Nil
+ else Assign(ref(stat.symbol), intoConstr(rhs, excluded = Mutable))
+ .withPos(stat.pos) :: Nil
+ (inits ::: constrStats, cpy.ValDef(stat)(rhs = EmptyTree) :: clsStats)
+ case _: DefTree =>
+ (constrStats, stat :: clsStats)
case _ =>
+ (intoConstr(stat, excluded = Mutable) :: constrStats, clsStats)
}
- assert(accessors.hasSameLengthAs(vparamsWithOuter),
- i"lengths differ for $cls, param accs = $accessors, params = $vparamsWithOuter")
- }
- val mappedArgs = superArgs.map(
- intoConstr(accessors, vparamsWithOuter.map(_.symbol)).transform)
- val superCall =
- cpy.Apply(superApp)(
- cpy.Select(superSel)(
- Super(This(cls), tpnme.EMPTY, inConstrCall = true).withPos(superNew.pos),
- nme.CONSTRUCTOR),
- mappedArgs)
- val (constrStats, clsStats) = splitStats(tree.body)
- def normalizeOwner(stat: Tree) = {
+ case Nil =>
+ (Nil, Nil)
+ }
+ val (constrStats, clsStats) = splitStats(tree.body)
+
+ // Collect all private parameter accessors that need to be retained
+ // because they are accessed after the constructor has finished.
+ val collectRetained = new TreeAccumulator[Set[Symbol]] {
+ override def apply(retained: Set[Symbol], tree: Tree) = tree match {
+ case tree: RefTree =>
+ val sym = tree.symbol
+ foldOver(
+ if (sym.is(PrivateParamAccessor) && sym.owner == cls) retained + sym else retained,
+ tree)
+ case _ =>
+ if (noDirectRefsFrom(tree)) retained else foldOver(retained, tree)
}
- cpy.Template(tree)(
- constr = cpy.DefDef(constr)(rhs = Block(superCall :: constrStats, unitLiteral)),
- parents = superType :: traitParents,
- body = clsStats)
}
+ val retainedPrivate = collectRetained(collectRetained(Set[Symbol](), constrStats), clsStats)
+ def isRetained(acc: Symbol) =
+ (!acc.is(Private) || acc.is(NotJavaPrivate) || retainedPrivate(acc))
+
+ val accessorFields = accessors.filterNot(_ is Method)
+ val (retainedAccessors, droppedAccessors) = accessorFields.partition(isRetained)
+
+ // The initializers for the retained accessors */
+ val copyParams = retainedAccessors.map(acc =>
+ Assign(ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos))
+
+ // Drop accessors that are not retained from class scope
+ if (droppedAccessors.nonEmpty) {
+ val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error
+ cls.copy(
+ info = clsInfo.derivedClassInfo(
+ decls = clsInfo.decls.filteredScope(!droppedAccessors.contains(_))))
+ }
+
+ cpy.Template(tree)(
+ constr = cpy.DefDef(constr)(
+ rhs = Block(superCalls.toList ::: copyParams ::: constrStats, unitLiteral)),
+ parents = parentTypeTrees,
+ body = clsStats filter {
+ case vdef: ValDef => !droppedAccessors.contains(vdef.symbol)
+ case _ => true
+ })
}
} \ No newline at end of file