/* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ package scala package tools.nsc package transform import symtab._ import Flags.{ CASE => _, _ } import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.tools.nsc.settings.ScalaVersion /** This class ... * * @author Martin Odersky * @version 1.0 */ abstract class ExplicitOuter extends InfoTransform with TypingTransformers with ast.TreeDSL { import global._ import definitions._ import CODE._ /** The following flags may be set by this phase: */ override def phaseNewFlags: Long = notPROTECTED /** the name of the phase: */ val phaseName: String = "explicitouter" /** This class does not change linearization */ override def changesBaseClasses = false protected def newTransformer(unit: CompilationUnit): Transformer = new ExplicitOuterTransformer(unit) /** Is given clazz an inner class? */ private def isInner(clazz: Symbol) = !clazz.isPackageClass && !clazz.outerClass.isStaticOwner private def haveSameOuter(parent: Type, clazz: Symbol) = { val owner = clazz.owner val parentSym = parent.typeSymbol parentSym.isClass && owner.isClass && (owner isSubClass parentSym.owner) && owner.thisType =:= parent.prefix } /** Does given clazz define an outer field? */ def hasOuterField(clazz: Symbol) = { val parent = clazz.info.firstParent // space optimization: inherit the $outer pointer from the parent class if // we know that it will point to the correct instance. def canReuseParentOuterField = !parent.typeSymbol.isJavaDefined && haveSameOuter(parent, clazz) isInner(clazz) && !clazz.isTrait && !canReuseParentOuterField } private def outerField(clazz: Symbol): Symbol = { val result = clazz.info.member(nme.OUTER_LOCAL) assert(result != NoSymbol, "no outer field in "+clazz+" at "+phase) result } private val innerClassConstructorParamName: TermName = newTermName("arg" + nme.OUTER) class RemoveBindingsTransformer(toRemove: Set[Symbol]) extends Transformer { override def transform(tree: Tree) = tree match { case Bind(_, body) if toRemove(tree.symbol) => super.transform(body) case _ => super.transform(tree) } } def outerAccessor(clazz: Symbol): Symbol = { val firstTry = clazz.info.decl(nme.expandedName(nme.OUTER, clazz)) if (firstTry != NoSymbol && firstTry.outerSource == clazz) firstTry else findOrElse(clazz.info.decls)(_.outerSource == clazz)(NoSymbol) } def newOuterAccessor(clazz: Symbol) = { val accFlags = SYNTHETIC | ARTIFACT | STABLE | ( if (clazz.isTrait) DEFERRED else 0 ) val sym = clazz.newMethod(nme.OUTER, clazz.pos, accFlags) val restpe = if (clazz.isTrait) clazz.outerClass.tpe_* else clazz.outerClass.thisType sym expandName clazz sym.referenced = clazz sym setInfo MethodType(Nil, restpe) } def newOuterField(clazz: Symbol) = { val accFlags = SYNTHETIC | ARTIFACT | PARAMACCESSOR | ( if (clazz.isEffectivelyFinal) PrivateLocal else PROTECTED ) val sym = clazz.newValue(nme.OUTER_LOCAL, clazz.pos, accFlags) sym setInfo clazz.outerClass.thisType } /** * Will the outer accessor of the `clazz` subsume the outer accessor of * `mixin`? * * This arises when an inner object mixes in its companion trait. * * {{{ * class C { * trait T { C.this } // C$T$$$outer$ : C * object T extends T { C.this } // C$T$$$outer$ : C.this.type * } * }}} * * See SI-7242. }} */ private def skipMixinOuterAccessor(clazz: Symbol, mixin: Symbol) = { // Reliant on the current scheme for name expansion, the expanded name // of the outer accessors in a trait and its companion object are the same. // If the assumption is one day falsified, run/t7424.scala will let us know. clazz.fullName == mixin.fullName } /**
* The type transformation method: *
** Add an outer accessor $outer$$C to every inner class * with fully qualified name C that is not an interface. * The outer accessor is abstract for traits, concrete for other * classes. *
** 3a. Also add overriding accessor defs to every class that inherits * mixin classes with outer accessor defs (unless the superclass * already inherits the same mixin). *
** which refers to the outer instance of class to of * value base. The result is typed but not positioned. */ protected def outerPath(base: Tree, from: Symbol, to: Symbol): Tree = { //Console.println("outerPath from "+from+" to "+to+" at "+base+":"+base.tpe) //assert(base.tpe.widen.baseType(from.toInterface) != NoType, ""+base.tpe.widen+" "+from.toInterface)//DEBUG if (from == to || from.isImplClass && from.toInterface == to) base else outerPath(outerSelect(base), from.outerClass, to) } override def transform(tree: Tree): Tree = { def sym = tree.symbol val savedOuterParam = outerParam try { tree match { case Template(_, _, _) => outerParam = NoSymbol case DefDef(_, _, _, (param :: _) :: _, _, _) if sym.isClassConstructor && isInner(sym.owner) => outerParam = param.symbol assert(outerParam.name startsWith nme.OUTER, outerParam.name) case _ => } super.transform(tree) } finally outerParam = savedOuterParam } } /**`base'.$outer$$C1 ... .$outer$$Cn
* The phase performs the following transformations on terms: *
** An class which is not an interface and is not static gets an outer * accessor (@see outerDefs). *
** 1a. A class which is not a trait gets an outer field. *
** Note: The whole transform is run in phase explicitOuter.next. *
*/ class ExplicitOuterTransformer(unit: CompilationUnit) extends OuterPathTransformer(unit) { transformer => /** The definition tree of the outer accessor of current class */ def outerFieldDef: Tree = ValDef(outerField(currentClass)) /** The definition tree of the outer accessor of current class */ def outerAccessorDef: Tree = localTyper typed { val acc = outerAccessor(currentClass) val rhs = if (acc.isDeferred) EmptyTree else Select(This(currentClass), outerField(currentClass)) DefDef(acc, rhs) } /** The definition tree of the outer accessor for class mixinClass. * * @param mixinClass The mixin class which defines the abstract outer * accessor which is implemented by the generated one. * @pre mixinClass is an inner class */ def mixinOuterAccessorDef(mixinClass: Symbol): Tree = { val outerAcc = outerAccessor(mixinClass) overridingSymbol currentClass def mixinPrefix = (currentClass.thisType baseType mixinClass).prefix assert(outerAcc != NoSymbol, "No outer accessor for inner mixin " + mixinClass + " in " + currentClass) assert(outerAcc.alternatives.size == 1, s"Multiple outer accessors match inner mixin $mixinClass in $currentClass : ${outerAcc.alternatives.map(_.defString)}") // I added the mixinPrefix.typeArgs.nonEmpty condition to address the // crash in SI-4970. I feel quite sure this can be improved. val path = ( if (mixinClass.owner.isTerm) gen.mkAttributedThis(mixinClass.owner.enclClass) else if (mixinPrefix.typeArgs.nonEmpty) gen.mkAttributedThis(mixinPrefix.typeSymbol) else gen.mkAttributedQualifier(mixinPrefix) ) // Need to cast for nested outer refs in presence of self-types. See ticket #3274. localTyper typed DefDef(outerAcc, gen.mkCast(transformer.transform(path), outerAcc.info.resultType)) } /** The main transformation method */ override def transform(tree: Tree): Tree = { val sym = tree.symbol if (sym != null && sym.isType) { //(9) if (sym.isPrivate) sym setFlag notPRIVATE if (sym.isProtected) sym setFlag notPROTECTED } tree match { case Template(parents, self, decls) => val newDefs = new ListBuffer[Tree] atOwner(tree, currentOwner) { if (!currentClass.isInterface || (currentClass hasFlag lateINTERFACE)) { if (isInner(currentClass)) { if (hasOuterField(currentClass)) newDefs += outerFieldDef // (1a) newDefs += outerAccessorDef // (1) } if (!currentClass.isTrait) for (mc <- currentClass.mixinClasses) if (outerAccessor(mc) != NoSymbol && !skipMixinOuterAccessor(currentClass, mc)) newDefs += mixinOuterAccessorDef(mc) } } super.transform( deriveTemplate(tree)(decls => if (newDefs.isEmpty) decls else decls ::: newDefs.toList ) ) case DefDef(_, _, _, vparamss, _, rhs) => if (sym.isClassConstructor) { rhs match { case Literal(_) => sys.error("unexpected case") //todo: remove case _ => val clazz = sym.owner val vparamss1 = if (isInner(clazz)) { // (4) if (isUnderConstruction(clazz.outerClass)) { reporter.error(tree.pos, s"Implementation restriction: ${clazz.fullLocationString} requires premature access to ${clazz.outerClass}.") } val outerParam = sym.newValueParameter(nme.OUTER, sym.pos) setInfo clazz.outerClass.thisType ((ValDef(outerParam) setType NoType) :: vparamss.head) :: vparamss.tail } else vparamss super.transform(copyDefDef(tree)(vparamss = vparamss1)) } } else super.transform(tree) case This(qual) => if (sym == currentClass || sym.hasModuleFlag && sym.isStatic) tree else atPos(tree.pos)(outerPath(outerValue, currentClass.outerClass, sym)) // (5) case Select(qual, name) => // make not private symbol accessed from inner classes, as well as // symbols accessed from @inline methods // // See SI-6552 for an example of why `sym.owner.enclMethod hasAnnotation ScalaInlineClass` // is not suitable; if we make a method-local class non-private, it mangles outer pointer names. if (currentClass != sym.owner || (closestEnclMethod(currentOwner) hasAnnotation ScalaInlineClass)) sym.makeNotPrivate(sym.owner) val qsym = qual.tpe.widen.typeSymbol if (sym.isProtected && //(4) (qsym.isTrait || !(qual.isInstanceOf[Super] || (qsym isSubClass currentClass)))) sym setFlag notPROTECTED super.transform(tree) case Apply(sel @ Select(qual, nme.CONSTRUCTOR), args) if isInner(sel.symbol.owner) => val outerVal = atPos(tree.pos)(qual match { // it's a call between constructors of same class case _: This => assert(outerParam != NoSymbol, tree) outerValue case _ => gen.mkAttributedQualifier(qual.tpe.prefix match { case NoPrefix => sym.owner.outerClass.thisType case x => x }) }) super.transform(treeCopy.Apply(tree, sel, outerVal :: args)) // for the new pattern matcher // base.