summaryrefslogblamecommitdiff
path: root/sources/scala/tools/nsc/transform/ExplicitOuter.scala
blob: 44a64ffa69b4112384a1889c858f85f5570a05e5 (plain) (tree)
































                                                                                             
                                                             
                                       
                                                   







                                                                                







                                                                                       
                               













                                                                        





                                                                                       


                                                                                                




























                                                                             
                                                                                       











                                                                             
                                                                 














                                                                    









































                                                                                      





















                                                                                              
                                                             


                                             





                                                                                          

                                               


                              
                              












                                                                         
                                                                 










                                                                                              
                                                                          
















                                                                                             
                                                                                




                                                  



                                             










                                                                                  
                                   


                                              
                                                                      

                                                                                                   

                      
                                                                                                  




                                                                         
                                                                    





                                                                                            

                                                                





                                                                        




                                                                                                  



                                                          
                              





                                                                       
                                                                                      



















                                                                                                 








                                                                                                 
                                          

                                                                              
                 
                                    
                                                           
                                            


                                                                             























                                                                     
/* NSC -- new scala compiler
 * Copyright 2005 LAMP/EPFL
 * @author
 */
// $Id$
package scala.tools.nsc.transform;

import symtab._;
import Flags._;
import util.ListBuffer;
import collection.mutable.HashMap;

abstract class ExplicitOuter extends InfoTransform {
  import global._;
  import definitions._;
  import posAssigner.atPos;

  override def phaseNewFlags: long = notPRIVATE | notPROTECTED;

  /** the following two members override abstract members in Transform */
  val phaseName: String = "explicitouter";

  final val needSuperAccessors = false;

  protected def newTransformer(unit: CompilationUnit): Transformer =
    new ExplicitOuterTransformer(unit);

  /** The type transformation method:
   *  1. Add an outer paramter to the formal parameters of a constructor or mixin constructor
   *     in a non-static class;
   *  2. Add a mixin constructor $init$ to all traits except interfaces
   *  Leave all other types unchanged.
   */
  def transformInfo(sym: Symbol, tp: Type): Type = tp match {
    case MethodType(formals, restpe) =>
      if (sym.isConstructor && !sym.owner.isStatic)
	MethodType(formals ::: List(sym.owner.owner.enclClass.thisType), restpe)
      else tp;
    case ClassInfoType(parents, decls, clazz) =>
      var decls1 = decls;
      if (!(clazz hasFlag INTERFACE)) {
	if (!clazz.isStatic) {
	  decls1 = new Scope(decls1.toList);
	  val outerType = clazz.owner.enclClass.thisType;
	  val outerAcc = clazz.newMethod(clazz.pos, nme.OUTER);
	  if ((clazz hasFlag TRAIT) || (decls.toList exists (.isClass)))
            outerAcc.expandName(clazz);
	  decls1 enter (
	    outerAcc setFlag (PARAMACCESSOR | ACCESSOR | STABLE | FINAL)
		     setInfo MethodType(List(), outerType));
	  decls1 enter (clazz.newValue(clazz.pos, nme.getterToLocal(outerAcc.name))
	    setFlag (LOCAL | PRIVATE | PARAMACCESSOR | (outerAcc getFlag EXPANDEDNAME))
	    setInfo outerType);
	}
	if (clazz.isTrait) {
	  decls1 = new Scope(decls1.toList);
	  decls1 enter makeMixinConstructor(clazz);
	}
      }
      if (decls1 eq decls) tp else ClassInfoType(parents, decls1, clazz)
    case PolyType(tparams, restp) =>
      val restp1 = transformInfo(sym, restp);
      if (restp eq restp1) tp else PolyType(tparams, restp1)
    case _ =>
      tp
  }

  private def outerMember(tp: Type): Symbol = {
    var e = tp.decls.elems;
    while (!(e.sym.name.startsWith(nme.OUTER) && (e.sym hasFlag ACCESSOR))) e = e.next;
    e.sym
  }

  private def makeMixinConstructor(clazz: Symbol): Symbol =
    clazz.newMethod(clazz.pos, nme.MIXIN_CONSTRUCTOR) setInfo MethodType(List(), UnitClass.tpe);

  /** A base class for transformers that maintain `outerParam' values for
   *  outer parameters of constructors.
   *  The class provides methods for referencing via outer.
   */
  class OuterPathTransformer extends Transformer {

    /** The directly enclosing outer parameter, if we are in a constructor */
    protected var outerParam: Symbol = NoSymbol;

    /** The first outer selection from currently transformed tree
     */
    protected def outerValue: Tree =
      if (outerParam != NoSymbol) gen.Ident(outerParam)
      else outerSelect(gen.This(currentOwner.enclClass));

    /** The path
     *     `base'.$outer ... .$outer
     *  which refers to the outer instance `to' of value `base
     */
    protected def outerPath(base: Tree, to: Symbol): Tree =
      if (base.tpe.symbol == to) base else outerPath(outerSelect(base), to);

    /** Select and apply outer accessor from `base'
     */
    private def outerSelect(base: Tree): Tree = {
      assert(base.tpe != null, base);
      assert(base.tpe.symbol != null, base);
      val clazz = base.tpe.symbol.owner.enclClass;
      Apply(
	Select(base, outerMember(base.tpe)) setType MethodType(List(), clazz.thisType),
	List()) setType clazz.thisType
    }

    override def transform(tree: Tree): Tree = {
      try {//debug
	val savedOuterParam = outerParam;
	tree match {
	  case Template(_, _) =>
            outerParam = NoSymbol;
	  case DefDef(_, _, _, vparamss, _, _) =>
	    if (tree.symbol.isConstructor && !(tree.symbol.owner.isStatic)) {
	      val lastParam = vparamss.head.last;
	      assert(lastParam.name.startsWith(nme.OUTER), tree);
	      outerParam = lastParam.symbol
	    }
	  case _ =>
	}
	val result = super.transform(tree);
	outerParam = savedOuterParam;
	result
      } catch {//debug
	case ex: Throwable =>
	  System.out.println("exception when transforming " + tree);
	throw ex
      }
    }
  }

  class ExplicitOuterTransformer(unit: CompilationUnit) extends Transformer {

    /** A map that takes triples consisting of
     *   - the class symbol that contains the accessor
     *   - the symbol accessed by the accessor
     *   - the mixin qualifier of the symbol
     *  to super accessor symbols.
     *  //todo: not clear whether we need to this for all super calls symbols, or just
     *        protected ones (or all calls to protected from inner classes)?
     */
    private val superAccessors = new HashMap[Triple[Symbol, Symbol, Name], Symbol];

    /** Generate a superclass accessor symbol and enter in `superAccessors' unless one
     *  exists already.
     */
    def superAccessor(clazz: Symbol, accessed: Symbol, mix: Name): Symbol =
      superAccessors.get(Triple(clazz, accessed, mix)) match {
	case Some(accessor) =>
	  accessor
	case None =>
	  val accessor = makeSuperAccessor(clazz, accessed, mix);
	  superAccessors(Triple(clazz, accessed, mix)) = accessor;
	  accessor
      }

    /** Generate a superclass acessor symbol named `NAME$super$MIX' in class `class'
     *  where NAME is the name of the accessed symbol `accessed'
     *  and MIX is the mixin qualifier of the super call `mix'.
     *  The method is initially `private'; will be widened later
     */
    def makeSuperAccessor(clazz: Symbol, accessed: Symbol, mix: Name): Symbol = {
      assert(accessed.isMethod);
      val accessorName = newTermName(
	accessed.name.toString() + "$super" +
	(if (mix == nme.EMPTY.toTypeName) "" else "$" + mix));
      val accessor = clazz.newMethod(clazz.pos, accessorName)
	setFlag PRIVATE
	setInfo clazz.tpe.memberType(accessed);
      clazz.info.decls enter accessor;
      accessor
    }

    /** The first step performs the following transformations:
     *   1. A class which is not an interface and is not static gets an outer link
     *      (@see outerDefs)
     *   2. A mixin which is not also an interface gets a mixin constructor
     *      (@see mixinConstructorDef)
     *   3. Constructor bodies are augmented by calls to supermixin constructors
     *      (@see addMixinConstructorCalls)
     *   4. A constructor of a class with an outer link gets an outer parameter.
     *   5. A reference C.this where C refers to an outer class is replaced by a selection
     *        this.$outer ... .$outer (@see outerPath)
     *   6. A reference C.super.M where C refers to an outer class and M is a term member of C
     *      is replaced by a selection
     *        this.$outer ... .$outer.M$super
     *      where M$super is the super accessor symbol of M (@see superAccessor).
     *   7. A call to a constructor Q.<init>(args) or Q.$init$(args) where Q != this and
     *      the constructor belongs to a non-static class is augmented by an outer argument.
     *      E.g. Q.<init>(args, OUTER) where OUTER is the qualifier corresponding to the
     *      singleton type Q.
     *   8. A call to a constructor this.<init>(args) in a secondary constructor
     *      is augmented to this.<init>(args, OUTER) where OUTER is the last parameter
     *      of the secondary constructor.
     */
    private val firstTransformer = new OuterPathTransformer {

      var localTyper: analyzer.Typer = typer;

      /** The two definitions
       *    val outer : C.this.type _;
       *    def outer(): C.this.type  = outer ;
       *  Here, C is the class enclosing the class `clazz' containing the two definitions.
       */
      def outerDefs(clazz: Symbol): List[Tree] = {
	val outerDef = outerMember(clazz.info);
	val outerVal = outerDef.accessed;
	List(
	  localTyper.typed {
	    atPos(clazz.pos) {
	      ValDef(outerVal)
	    }
	  },
	  localTyper.typed {
	    atPos(clazz.pos) {
	      DefDef(outerDef, vparamss => Select(This(clazz), outerVal))
	    }
	  })
      }

      /** The mixin constructor definition
       *    def $init$(): Unit = ()
       */
      def mixinConstructorDef(clazz: Symbol): Tree = localTyper.typed {
	DefDef(clazz.primaryConstructor, vparamss => Literal(()))
      }

      /** Add calls to supermixin constructors
       *     super[mix].$init$()
       *  to `tree'. `tree' which is assumed to be the body of a constructor of class `clazz'.
       */
      def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = {
	def mixinConstructorCall(mixin: Symbol): Tree =
	  atPos(tree.pos) {
	    Apply(
	      localTyper.typedOperator {
		Select(Super(clazz, mixin.name), mixin.primaryConstructor)
	      },
	      List()) setType UnitClass.tpe; // don't type this with typed(...),
 	                                     // as constructor arguments might be missing
	  }
	val mixinConstructorCalls =
	  for (val mixin <- clazz.info.parents.tail; !(mixin.symbol hasFlag INTERFACE)) yield
	    mixinConstructorCall(mixin.symbol);
	tree match {
	  case Block(supercall :: stats, expr) =>
            assert(supercall match {
              case Apply(Select(Super(_, _), _), _) => true
              case _ => false
            });
	    copy.Block(tree, supercall :: mixinConstructorCalls ::: stats, expr);
	  case Block(_, _) =>
	    assert(false, tree);  tree
	  case expr =>
	    Block(mixinConstructorCalls, expr) setType expr.tpe setPos expr.pos;
	}
      }

      /** The first-step transformation method */
      override def transform(tree: Tree): Tree = {
	val sym = tree.symbol;
	val tree1 = tree match {
	  case Template(parents, decls) =>
	    val savedLocalTyper = localTyper;
	    localTyper = localTyper.atOwner(tree, currentOwner);
	    var decls1 = decls;
	    if (!(currentOwner hasFlag INTERFACE)) {
	      if (!currentOwner.isStatic)
		decls1 = decls1 ::: outerDefs(currentOwner); // (1)
	      if (currentOwner.isTrait)
		decls1 = decls1 ::: List(mixinConstructorDef(currentOwner)) // (2)
	    }
	    localTyper = savedLocalTyper;
	    copy.Template(tree, parents, decls1);
	  case constrDef @ DefDef(mods, name, tparams, vparamss, tpt, rhs)
	  if (sym.isConstructor) =>
	    val vparamss1 =
	      if (sym.owner.isStatic) vparamss
	      else { // (4)
		val outerField = outerMember(sym.owner.info).accessed;
		val outerParam = sym.newValueParameter(sym.pos, nme.OUTER) setInfo outerField.info;
		List(vparamss.head ::: List(ValDef(outerParam) setType NoType))
	      }
	    val rhs1 =
	      if ((sym.isPrimaryConstructor || sym.isMixinConstructor) && sym.owner != ArrayClass)
		addMixinConstructorCalls(rhs, sym.owner); // (3)
	      else rhs;
	    copy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs1);
	  case This(qual) =>
	    if (sym == currentOwner.enclClass || sym.isStatic) tree
	    else atPos(tree.pos)(outerPath(outerValue, sym)); // (5)
	  case Select(qual @ Super(_, mix), name) =>
            val qsym = qual.symbol;
	    if (!needSuperAccessors || tree.symbol.isType || qsym == currentOwner.enclClass)
	      tree
	    else atPos(tree.pos) { // (6)
	      val accessor = superAccessor(qsym, tree.symbol, mix);
	      Select(if (qsym.isStatic) This(qsym)
		     else outerPath(outerValue, qsym), accessor)
                setType accessor.tpe
	    }
	  case Apply(sel @ Select(qual, name), args)
	  if ((name == nme.CONSTRUCTOR || name == nme.MIXIN_CONSTRUCTOR)
	      && !sel.symbol.owner.isStatic) =>
	    val outerVal =
              atPos(tree.pos) {
	        if (qual.isInstanceOf[This]) { assert(outerParam != NoSymbol); outerValue } // (8)
		else if (qual.tpe.prefix == NoPrefix) gen.This(currentOwner.enclClass)
                else gen.mkQualifier(qual.tpe.prefix); // (7)
              }
	    copy.Apply(tree, sel, args ::: List(outerVal))
	  case _ =>
	    tree
	}
	super.transform(tree1)
      }
    }

    /** The second step performs the following transformations:
     *   1. Add definitions of superaccessors to the members of a class
     *      (@see makeSuperAccessorDefs)
     *   2. Remove private modifiers from members M of mixins T. (@see makeNotPrivate)
     *   3. Remove `private' modifier from class members M that are accessed from an inner class.
     *   4. Remove `protected' modifier from class members M that are accessed
     *      without a super qualifier accessed from an inner class. (@see makeNotPrivate)
     *   5. Remove `private' and `protected' modifiers from type symbols
     */
    private val secondTransformer = new Transformer {

      /** Add a definition for each super accessor in `clazz':
       *    def NAME$super$MIX(args) = super[MIX].NAME(args)
       */
      def makeSuperAccessorDefs(clazz: Symbol, typer: analyzer.Typer): List[Tree] =
	(for (val Pair(Triple(owner, accessed, mix), accessor) <- superAccessors.elements;
	      owner == clazz) yield
	  typer.typed {
	    atPos(clazz.pos) {
	      DefDef(accessor, vparamss =>
		Apply(Select(Super(clazz, mix), accessed), vparamss.head map Ident))
	    }
	  }).toList;

      /** The second-step transformation method */
      override def transform(tree: Tree): Tree = {
	val sym = tree.symbol;
	val tree1 = super.transform(tree);
	tree1 match {
	  case Template(parents, stats) => // (1)
	    val accessors = makeSuperAccessorDefs(sym.owner, typer.atOwner(tree1, currentOwner));
	    if (accessors.isEmpty) tree1
	    else copy.Template(tree1, parents, stats ::: accessors)
          case DefDef(_, _, _, _, _, _) =>
            if (sym.owner.isTrait && (sym hasFlag (ACCESSOR | SUPERACCESSOR)))
              sym.makeNotPrivate(sym.owner);
            tree1
	  case Select(qual, name) =>
	    if (currentOwner.enclClass != sym.owner) // (3)
              sym.makeNotPrivate(sym.owner);
	    if ((sym hasFlag PROTECTED) &&
		!(qual.isInstanceOf[Super] ||
		  (qual.tpe.widen.symbol isSubClass currentOwner.enclClass)))
	      sym setFlag notPROTECTED;
	    tree1
	  case _ =>
	    if (sym != null && sym.isType) {
	      if (sym hasFlag PRIVATE) sym setFlag notPRIVATE;
	      if (sym hasFlag PROTECTED) sym setFlag notPROTECTED;
	    }
	    tree1
	}
      }
    }

    /** The main transformation method:
     *  First, perform step 1 on whole tree of compilation unit.
     *  Then, perform step 2 on resulting tree
     */
    override def transform(tree: Tree) =
      atPhase(phase.next) {
	secondTransformer.transform(firstTransformer.transform(tree))
      }
  }
}