summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
blob: 5811c279c88a977852a7222e6cf86da9b30fb6d2 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                                


          
 




                                                     





                                                                          


                          
 
                                                              

                                                                         

                                         
 
                                                                    
                                      
 

                                                 
                                                                                            

                                       
                                                           
 


                                                                                             
                                                                       

                                      
                                                             
                                       
                                                                             
                                      
                                                                                




                                                                                          
             
                                                
                        
                                       
                               
                                          

                                                                
                                       




                                                                                       
                                               

                            
                                          

                                                  


                                                                        
                                            




                                                            
                                               
                                      

                                                                                         

                                                                                                
                         


         
                                                           
                                                                                               
 






                                                                             
                                               



                                                                 
                                                                   
                                                                    


                                    
                                                               

                                                           
                                                                           
 
                                                   

                                                 
                                                    
            

                                                                            



                                                





                                                                              




                                                                 





                                           
                      


                                                                   



       

                                                                             









                                                                                          







                                                                                            
                                                             
 
                                            
 
                             
                                     



                                                                                          












                                                                         




                                          

                                                    

                                                  




                                                                                   





                                                                                              








                                                                                          
          

                                                         
                                                                                                     

                                                    
          

                                                 

                                
                                           
                 
                                                                                        






                                                                 
                                                 


                                                           




                                                                                


                                                                      



                                                  




                                                               
                              









                                                                                            




                                                                                      





                                                                                                       


                                                                                     





                                                                                                
             





                                                                                                           
                                                  
                                                  
                                                                                                                                                                              

                                                                                 
                                              


                                                          
                                         
                                                                          
                                              
               
             


                                                            
 



                              



                                                               
                                                                                      

                                                                                                 
                                                                            

                                                                        
                                                              
 

                                                  
                             








                                                                              
                     
                                          
                                                                              
                                                  
                 
                                    
                                                  
                                                                              
                                            
                                            
                                                
                                                                                             

                                       







































                                                                                                                                  






                                                                 


       







                                                         





                                                                
                                                                     




       
/* NSC -- new Scala compiler
 * Copyright 2005-2006 LAMP/EPFL
 * @author
 */
// $Id$

package scala.tools.nsc.transform

import symtab._
import Flags._
import scala.collection.mutable.{HashMap, ListBuffer}
import matching.{TransMatcher, PatternNodes, CodeFactory, PatternMatchers}
abstract class ExplicitOuter extends InfoTransform
with TransMatcher
with PatternNodes
with CodeFactory
with PatternMatchers {
  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"
  override def changesBaseClasses = false

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

  private def outerClass(clazz: Symbol): Symbol =
    if (clazz.owner.isClass) clazz.owner
    else outerClass(if (clazz.isClassLocalToConstructor) clazz.owner.owner else clazz.owner)

  private def isStatic(clazz: Symbol) =
    clazz.isPackageClass || outerClass(clazz).isStaticOwner

  /** 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 mixins except interfaces
   *  Leave all other types unchanged.
   */
  def transformInfo(sym: Symbol, tp: Type): Type = tp match {
    case MethodType(formals, restpe) =>
      if (sym.owner.isTrait && ((sym hasFlag SUPERACCESSOR) || sym.isModule))
        sym.makeNotPrivate(sym.owner);
      if (sym.owner.isTrait && (sym hasFlag PROTECTED)) sym setFlag notPROTECTED
      if (sym.isConstructor && !isStatic(sym.owner)) {
        // Move outer paramater to first position
        MethodType(outerClass(sym.owner).toInterface.thisType :: formals, restpe)
        //MethodType(formals ::: List(outerClass(sym.owner).toInterface.thisType), restpe)
      }
      else tp
    case ClassInfoType(parents, decls, clazz) =>
      var decls1 = decls
      if (!(clazz hasFlag INTERFACE)) {
        if (!isStatic(clazz)) {
          decls1 = newScope(decls1.toList)
          val outerAcc = clazz.newMethod(clazz.pos, nme.OUTER)
          if (clazz.isTrait || (decls.toList exists (.isClass)))
            outerAcc.expandName(clazz);
          decls1 enter (
            outerAcc setFlag (PARAMACCESSOR | ACCESSOR | STABLE)
                     setInfo MethodType(List(), outerClass(clazz).thisType));
          decls1 enter (clazz.newValue(clazz.pos, nme.getterToLocal(outerAcc.name))
            setFlag (LOCAL | PRIVATE | PARAMACCESSOR | (outerAcc getFlag EXPANDEDNAME))
            setInfo outerClass(clazz).thisType)
        }
        if (clazz.isTrait) {
          decls1 = newScope(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.symbol.info.decls.elems
      // note: tp.decls does not work here, because tp might be a ThisType, in which case
      // its decls would be the decls of the required type of the class.
    while (e != null && !(e.sym.originalName.startsWith(nme.OUTER) && (e.sym hasFlag ACCESSOR)))
      e = e.next;
    assert(e != null, tp)
    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.mkAttributedIdent(outerParam)
      else outerSelect(gen.mkAttributedThis(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 = {
      val otp = outerClass(base.tpe.symbol).thisType
      Apply(
        Select(base, outerMember(base.tpe)) setType MethodType(List(), otp),
        List()) setType otp
    }

    override def transform(tree: Tree): Tree = {
      try {//debug
        val savedOuterParam = outerParam;
        tree match {
          case Template(_, _) =>
            outerParam = NoSymbol
          case DefDef(_, _, _, vparamss, _, _) =>
            if (tree.symbol.isConstructor && !(isStatic(tree.symbol.owner))) {
              // moved outer param to first position
              val firstParam = vparamss.head.head
//              val lastParam = vparamss.head.last
              assert(firstParam.name.startsWith(nme.OUTER), tree)
              outerParam = firstParam.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 {

    /** 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)
     *   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 {
          val constr = clazz.primaryConstructor
          atPhase(currentRun.explicitOuterPhase) {
            // necessary so that we do not include an outer parameter already here;
            // this will be added later in transform.
            DefDef(constr, 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(mixinClass: Symbol): Tree =
          atPos(tree.pos) {
            Apply(
              localTyper.typedOperator {
                Select(This(clazz), mixinClass.primaryConstructor)
              },
              List()) setType UnitClass.tpe; // don't type this with typed(...),
                                              // as constructor arguments might be missing
          }
        /*
        val mixinConstructorCalls =
          for (val mixinClass <- clazz.info.parents.tail;
               !(mixinClass.symbol hasFlag INTERFACE) && mixinClass.symbol != ScalaObjectClass) yield
            mixinConstructorCall(mixinClass.symbol);
        tree match {
        */
        val mixinConstructorCalls: List[Tree] = {
          val ps = clazz.info.parents
          if (ps.isEmpty) List()
          else {
            val superClass = ps.head.symbol
            for {
              val mclazz <- clazz.info.baseClasses.tail.takeWhile(superClass ne).reverse
              mclazz.needsImplClass && mclazz != ScalaObjectClass
            } yield mixinConstructorCall(mclazz)
          }
        }

        //val result =
        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
        }

        //System.out.println("new constructor of "+clazz+": "+result);
        //result
      }

      /** 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) || (currentOwner hasFlag lateINTERFACE)) {
              if (!isStatic(currentOwner))
                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) =>
            rhs match {
              case Literal(_) =>
                val rhs1 = Block(List(), rhs) setPos rhs.pos setType rhs.tpe;
                transform(copy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1))
              case _ =>
                val clazz = sym.owner.toInterface
                val vparamss1 =
                  if (isStatic(clazz)) vparamss
                  else { // (4)
                    val outerField = outerMember(clazz.info).accessed;
                    val outerParam = sym.newValueParameter(sym.pos, nme.OUTER) setInfo outerField.info;
                    // moved outer param to first position
                    List((ValDef(outerParam) setType NoType) :: vparamss.head)
//                    List(vparamss.head ::: List(ValDef(outerParam) setType NoType))
                  }
                val rhs1 =
                  if (sym.isPrimaryConstructor && sym.isClassConstructor && clazz != ArrayClass)
                    addMixinConstructorCalls(rhs, clazz); // (3)
                  else rhs;
                copy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs1)
            }
          case This(qual) =>
            if (sym == currentOwner.enclClass || (sym hasFlag MODULE) && sym.isStatic) tree
            else atPos(tree.pos)(outerPath(outerValue, sym)); // (5)
          case Apply(sel @ Select(qual, name), args)
          if ((name == nme.CONSTRUCTOR || name == nme.MIXIN_CONSTRUCTOR) && !isStatic(sel.symbol.owner)) =>
            val outerVal = atPos(tree.pos) {
              if (name == nme.MIXIN_CONSTRUCTOR) {
                val mclazz = sym.owner.toInterface
                //System.out.println("adding mixin constructor for " + currentOwner.enclClass + " " + mclazz + " " + currentOwner.enclClass.thisType.baseType(mclazz));//DEBUG
                var pre = currentOwner.enclClass.thisType.baseType(mclazz).prefix
                if (pre == NoPrefix) pre = outerClass(mclazz).thisType
                gen.mkAttributedQualifier(pre)
              } else if (qual.isInstanceOf[This]) {
                assert(outerParam != NoSymbol); outerValue
              } else {
                var pre = qual.tpe.prefix
                if (pre == NoPrefix) pre = outerClass(sym.owner).thisType;
                gen.mkAttributedQualifier(pre)
              }
            }
            // moved outer parameter to first position
            copy.Apply(tree, sel, outerVal :: args)
//            copy.Apply(tree, sel, args ::: List(outerVal))

          case _ =>
            tree
        }
        super.transform(tree1)
      }
    }

    /** The second step performs the following transformations:
     *   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 or trait.
     *   5. Remove `private' and `protected' modifiers from type symbols
     */
    private val secondTransformer = new OuterPathTransformer {

      /** The second-step transformation method */
      override def transform(tree: Tree): Tree = {
        val sym = tree.symbol

        val tree1 =
          if(tree.isInstanceOf[Match]) {
            //Console.println("calling super.transform of Match with ncases "+
            //                tree.asInstanceOf[Match].cases.length)
            tree
          } else {
            super.transform(tree)
          }
        tree1 match {
          case DefDef(_, _, _, _, _, _) =>
            if (sym.owner.isTrait && (sym hasFlag (ACCESSOR | SUPERACCESSOR)))
              sym.makeNotPrivate(sym.owner); //(2)
            tree1
          case Select(qual, name) =>
            val enclClass = currentOwner.enclClass
            if (enclClass != sym.owner && enclClass != sym.moduleClass) // (3)
              sym.makeNotPrivate(sym.owner);
            val qsym = qual.tpe.widen.symbol
            if ((sym hasFlag PROTECTED) && //(4)
                (qsym.isTrait || !(qual.isInstanceOf[Super] || (qsym isSubClass enclClass))))
              sym setFlag notPROTECTED;
            tree1

/*<--- begin transmatch experimental */
      case Match(selector, cases) =>
        val tid = cunit.fresh.newName("tidmark")

          if(settings.debug.value)
            Console.println("transforming patmat with tidmark "+tid+" ncases = "+cases.length)
        if((cases.length > 1)&&( treeInfo.isDefaultCase(cases(0))))
          assert(false,"transforming too much, "+tid)
        val nselector = transform(selector).setType(selector.tpe)
        val ncases = cases map { transform }
        ExplicitOuter.this.resultType = tree.tpe
        //Console.println("TransMatcher currentOwner ="+currentOwner+")")
        //Console.println("TransMatcher selector.tpe ="+selector.tpe+")")
        //Console.println("TransMatcher resultType ="+resultType+")")
          def mytransform(tree: Tree): Tree = {
            val sym = tree.symbol
            tree match {
              case This(qual) =>
                if (sym == currentOwner.enclClass || (sym hasFlag Flags.MODULE) && sym.isStatic) tree
                else atPos(tree.pos)(outerPath(outerValue, sym)); // (5)
              case Select(qual,name) =>
                Select(mytransform(qual),name)
            }
          }
        val t_untyped = ExplicitOuter.this.handlePattern(nselector, ncases.asInstanceOf[List[CaseDef]], currentOwner, mytransform)


        //Console.println("t_untyped "+t_untyped.toString())
        val t         = atPos(tree.pos) { typer.atOwner(tree,currentOwner).typed(t_untyped, resultType) }

          //t = transform(t)
        //val t         = atPos(tree.pos) { typed(t_untyped, resultType) }
        //val t         = atPos(tree.pos) { typed(t_untyped) }
        //Console.println("t typed "+t.toString())
          if(settings.debug.value)
            Console.println("finished translation of "+tid)
        t
/*<--- end transmatch experimental */

          case _ =>
            if (sym != null && sym.isType) {//(5)
              if (sym hasFlag PRIVATE) sym setFlag notPRIVATE
              if (sym hasFlag PROTECTED) sym setFlag notPROTECTED
            }
            tree1
        }
      }
    }

    // needed in TransMatcher
    override def transformUnit(unit: CompilationUnit) = {
      cunit = unit
      super.transformUnit(unit)
      cunit = null
    }


    /** 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))
      }
  }
}