aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-08-31 18:08:32 +0200
committerMartin Odersky <odersky@gmail.com>2014-08-31 18:08:32 +0200
commit47b1d735e8323b2587aeb4b2a7ce5e214d9f1f8d (patch)
treeacca2788c66b97ee4b20f8e29acdb6130b3832f9 /src
parent8f321f2afd0cd363492665b410a70476c8a4b751 (diff)
downloaddotty-47b1d735e8323b2587aeb4b2a7ce5e214d9f1f8d.tar.gz
dotty-47b1d735e8323b2587aeb4b2a7ce5e214d9f1f8d.tar.bz2
dotty-47b1d735e8323b2587aeb4b2a7ce5e214d9f1f8d.zip
New phase: outerAccessors
The new phase replaces attachOuter. It creates outer accessors where needed but does not yet define outer parameters or pass outer arguments. It should run before pattern matcher, so that pattern matcher can access the outer fields of scrutinees.
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/Compiler.scala5
-rw-r--r--src/dotty/tools/dotc/transform/OuterAccessors.scala164
-rw-r--r--src/dotty/tools/dotc/transform/SymUtils.scala3
3 files changed, 169 insertions, 3 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index ac2e91cec..060f1fcbd 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -54,15 +54,14 @@ class Compiler {
new ElimRepeated,
new ElimLocals),
List(new ExtensionMethods),
- List(new TailRec),
+ List(new TailRec, new OuterAccessors),
List(new PatternMatcher,
// new LazyValTranformContext().transformer, // disabled, awaiting fixes
new Splitter),
List(new ElimByName,
new TypeTestsCasts,
new InterceptedMethods,
- new Literalize,
- new AttachOuter),
+ new Literalize),
List(new Erasure)
)
diff --git a/src/dotty/tools/dotc/transform/OuterAccessors.scala b/src/dotty/tools/dotc/transform/OuterAccessors.scala
new file mode 100644
index 000000000..5e6257e7f
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/OuterAccessors.scala
@@ -0,0 +1,164 @@
+package dotty.tools.dotc
+package transform
+
+import TreeTransforms._
+import core.DenotTransformers._
+import core.Symbols._
+import core.Contexts._
+import core.Types._
+import core.Flags._
+import core.Decorators._
+import core.StdNames.nme
+import core.Names._
+import ast.Trees._
+import SymUtils._
+import util.Attachment
+import collection.mutable
+
+/** This phase decorates News and parent constructors of non-static inner classes
+ * with an attachment indicating the outer reference as a tree. This is necessary because
+ * outer prefixes are erased, and explicit outer runs only after erasure.
+ */
+class OuterAccessors extends MiniPhaseTransform with InfoTransformer { thisTransformer =>
+ import OuterAccessors._
+ import ast.tpd._
+
+ val Outer = new Attachment.Key[Tree]
+
+ override def phaseName: String = "outerAccessors"
+
+ override def treeTransformPhase = thisTransformer.next
+
+ /** Add outer accessors if a class always needs an outer pointer */
+ override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match {
+ case tp @ ClassInfo(_, cls, _, decls, _) if needsOuterAlways(cls) =>
+ val newDecls = decls.cloneScope
+ newOuterAccessors(cls).foreach(newDecls.enter)
+ tp.derivedClassInfo(decls = newDecls)
+ case _ =>
+ tp
+ }
+
+ /** A new outer accessor or param accessor */
+ private def newOuterSym(owner: ClassSymbol, cls: ClassSymbol, name: TermName, flags: FlagSet)(implicit ctx: Context) = {
+ ctx.newSymbol(owner, name, Synthetic | flags, cls.owner.enclosingClass.typeRef, coord = cls.coord)
+ }
+
+ /** A new outer accessor for class `cls` which is a member of `owner` */
+ private def newOuterAccessor(owner: ClassSymbol, cls: ClassSymbol)(implicit ctx: Context) = {
+ val deferredIfTrait = if (cls.is(Trait)) Deferred else EmptyFlags
+ newOuterSym(owner, cls, cls.outerAccName, Final | Stable | deferredIfTrait)
+ }
+
+ /** A new param accessor for the outer field in class `cls` */
+ private def newOuterParamAccessor(cls: ClassSymbol)(implicit ctx: Context) =
+ newOuterSym(cls, cls, nme.OUTER, Private | ParamAccessor)
+
+ /** The outer accessor and potentially outer param accessor needed for class `cls` */
+ private def newOuterAccessors(cls: ClassSymbol)(implicit ctx: Context) =
+ newOuterAccessor(cls, cls) :: (if (cls is Trait) Nil else newOuterParamAccessor(cls) :: Nil)
+
+ /** First, add outer accessors if a class does not have them yet and it references an outer this.
+ * If the class has outer accessors, implement them.
+ * Furthermore, if a parent trait might have outer accessors (decided by needsOuterIfReferenced),
+ * provide an implementation for the outer accessor by computing the parent's
+ * outer from the parent type prefix. If the trait ends up not having an outer accessor
+ * after all, the implementation is redundant, but does not harm.
+ * The same logic is not done for non-trait parent classes because for them the outer
+ * pointer is passed in the super constructor, which will be implemented later in
+ * a separate phase which needs to run after erasure. However, we make sure here
+ * that the super class constructor is indeed a New, and not just a type.
+ */
+ override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val cls = ctx.owner.asClass
+ val isTrait = cls.is(Trait)
+ if (needsOuterIfReferenced(cls) && !needsOuterAlways(cls) && referencesOuter(cls, impl))
+ newOuterAccessors(cls).foreach(_.enteredAfter(thisTransformer))
+ if (hasOuter(cls)) {
+ val outerAcc = cls.info.member(cls.outerAccName).symbol.asTerm
+ val newDefs = new mutable.ListBuffer[Tree]
+ if (isTrait)
+ newDefs += DefDef(outerAcc, EmptyTree)
+ else {
+ val outerParamAcc = cls.info.decl(nme.OUTER).symbol.asTerm
+ newDefs += ValDef(outerParamAcc, EmptyTree)
+ newDefs += DefDef(outerAcc, ref(outerParamAcc))
+ }
+ val parents1 =
+ for (parent <- impl.parents) yield {
+ val parentCls = parent.tpe.classSymbol.asClass
+ if (parentCls.is(Trait)) {
+ if (needsOuterIfReferenced(parentCls)) {
+ val outerAccImpl = newOuterAccessor(cls, parentCls).enteredAfter(thisTransformer)
+ newDefs += DefDef(outerAccImpl, singleton(outerPrefix(parent.tpe)))
+ }
+ parent
+ }
+ else parent match { // ensure class parent is a constructor
+ case parent: TypeTree => New(parent.tpe, Nil).withPos(impl.pos)
+ case _ => parent
+ }
+ }
+ cpy.Template(impl)(parents = parents1, body = impl.body ++ newDefs)
+ }
+ else impl
+ }
+}
+
+object OuterAccessors {
+ import ast.tpd._
+
+ private val LocalInstantiationSite = Module | Private
+
+ /** Class needs an outer pointer, provided there is a reference to an outer this in it. */
+ def needsOuterIfReferenced(cls: ClassSymbol)(implicit ctx: Context): Boolean = !(
+ cls.isStatic ||
+ cls.owner.enclosingClass.isStaticOwner ||
+ cls.is(Interface)
+ )
+
+ /** Class unconditionally needs an outer pointer. This is the case if
+ * the class needs an outer pointer if referenced and one of the following holds:
+ * - we might not know at all instantiation sites whether outer is referenced or not
+ * - we need to potentially pass along outer to a parent class or trait
+ */
+ def needsOuterAlways(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ needsOuterIfReferenced(cls) &&
+ (!hasLocalInstantiation(cls) || // needs outer because we might not know whether outer is referenced or not
+ cls.classInfo.parents.exists(parent => // needs outer to potentially pass along to parent
+ needsOuterIfReferenced(parent.classSymbol.asClass)))
+
+ /** Class is always instantiated in the compilation unit where it is defined */
+ def hasLocalInstantiation(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ cls.owner.isTerm || cls.is(LocalInstantiationSite)
+
+ /** Class has outer accessor. Can be called only after phase OuterAccessors. */
+ def hasOuter(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ cls.info.decl(cls.outerAccName).exists
+
+ /** Template `impl` of class `cls` references an outer this which goes to
+ * a class that is not a static owner.
+ */
+ def referencesOuter(cls: ClassSymbol, impl: Template)(implicit ctx: Context): Boolean =
+ existsSubTreeOf(impl) {
+ case thisTree @ This(_) =>
+ val thisCls = thisTree.symbol
+ thisCls != cls && !thisCls.isStaticOwner && cls.isContainedIn(thisCls)
+ case _ =>
+ false
+ }
+
+ /** The outer prefix implied by type `tpe` */
+ def outerPrefix(tpe: Type)(implicit ctx: Context): Type = tpe match {
+ case tpe: TypeRef =>
+ tpe.symbol match {
+ case cls: ClassSymbol =>
+ if (tpe.prefix eq NoPrefix) cls.owner.enclosingClass.thisType
+ else tpe.prefix
+ case _ =>
+ outerPrefix(tpe.underlying)
+ }
+ case tpe: TypeProxy =>
+ outerPrefix(tpe.underlying)
+ }
+} \ 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 181072983..97f2a9f47 100644
--- a/src/dotty/tools/dotc/transform/SymUtils.scala
+++ b/src/dotty/tools/dotc/transform/SymUtils.scala
@@ -6,6 +6,7 @@ import Types._
import Contexts._
import Symbols._
import Decorators._
+import Names._
import StdNames.nme
import NameOps._
import language.implicitConversions
@@ -21,4 +22,6 @@ class SymUtils(val self: Symbol) extends AnyVal {
def isTypeTestOrCast(implicit ctx: Context): Boolean =
self == defn.Any_asInstanceOf || self == defn.Any_isInstanceOf
+
+ def outerAccName(implicit ctx: Context): TermName = nme.OUTER.expandedName(self)
}