summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/transform/UnCurry.scala
blob: 7fc472df2f7881210ab12e5e6450696f2b91e706 (plain) (tree)
1
2
3
4
5
6
7
8
9
                            
                                

          
 

                       

                     
                                              
 
             
                                                                                                          







                                                                                    
                                                          
                                                               


                                                                            
                                                

                                                                           
                                                         
                                                                               
                                                   
                                                              
                                                                        
   
              




                                                                                                        

                                                                  
               

                                   
 

                                                                                       
 

                                                                                      
                                              
 
                                                      
                                                              

                         
                                                   












                                                           
                                                                                   
 


                                      

                                                      

                                                                    
 





                                                                           


                                                                                  

                            
                                                            
                                        

                           



                                                              
                
     
 

                                                                                         

                              
                                          
                                      

                                           





                                                                                             
                                                                          
                                                   
               
                   

     

                                                                                       

                                                                             
                                                                            

                                                                 
                                                                          

                                                       
                                                 


                                                                                                           



                                                                                         
                                                    
                                                           
                                                                  
       


                                                                                  
                                       


            







                                 
                                                





                                                                            

                                                                           
                                                                   








                                                                                                       





                                                                                 
                                                                   
                                                
                                                                                        
                                           


              







                                                         

                                                                                             
                                                                                      



                                                        
                                                                                 
                                                             

           
                                        
                                                                                 
                                                                                                        

                                    

                                     
             
                                                                    







                                     
                                                                        
                                                                                                         
      
                                                        
       

































                                                                                                                                                            
           
 


                                                                                                  
 





                                                                                                                      
 






                                                                                                                   
 




                                                                            
 






















                                                                                                                                  
             

                                                         
 
                                 

           


                                                                           
 




                                                                                              
         
 
                                                                                            






                                                                      
                        






                                                                                                   
             
           
         
 





                                                                                  


                                                                              
                                                                  
                                                            
           
                        
                                      

                                                                                         
             
           
         
 








                                                                                                                   





                                                                                                        
           
 
                      

                                                                                                                
                                                                
             
           

                                                                      
       


                                                                                                    
                                             
                                         

                                      
                           

                                                   





                                                                              



                                                                             
         
       

     


                                                                         
       

                                                         

                                                                                                                           

                                               

       
 
















                                                                                                                 
                                                                                                    
 

                                                                                        
                                           
                                                                       
                               
                              

                                   

       
                                                                                 


                                                                        
                                                                         
                                             
 
                                                                          

                                  
                                                  
                                                                                    
                                                 
                                             



                                             

       
                                                                              
                                          
                                                  

                                              

       
                           





                                                                         



                                                                                                                      
                                   
                                                

                                                                            
 













                                                                                               
                                                               
                                                                      
                 
                      
                                   
               
             
























                                                                                                                                
 

                                                        
 

                                                                                                            
 

                                                            
 


                                                                                    
 


                                                                           
 

                                                 
 

                                                              
 







                                                                                         
             




                                                         
     
 
                                                        
                                




                                                                                        
         





                                                                     

       
                  








                                                                       
                                 
                                                        


                                                                           









                                                                                    
                                             

                                                                                                                                              
                
                                                  
                       







                                                                                                                                               
                                                                                           




                                                    
                                              
               
                                                                     
                                                 
                                                               
                                                         
           
                                             
                                                  
                           
                                             


                                              


                                                                                      
                          
              

                                                                       
       
     




                                                                                 
                                                      



                                                                                                   
                                                                                                                              

                                          
       




                                                                                         


                                                                                        



                                             
       










                                                                                              

       

                                                   
                                                         
                                              

                        


                                                                  
       
                                                    
                                                                        
                                                                                      

                                                     
                                          

                                                    


                          
                                                                                                                       

                        
                                                
                                                     

                                                       
                      
                         

                                                                                    
               
             
         
                                                  


                                              
                                                                
 


                                                                        


                                                                                              
                                                                                                                       





                                                                                                                            


                                           
 
            
     
   
 
/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author
 */

package scala.tools.nsc
package transform

import symtab.Flags._
import scala.collection.{ mutable, immutable }

/*<export> */
/** - uncurry all symbol and tree types (@see UnCurryPhase) -- this includes normalizing all proper types.
 *  - for every curried parameter list:  (ps_1) ... (ps_n) ==> (ps_1, ..., ps_n)
 *  - for every curried application: f(args_1)...(args_n) ==> f(args_1, ..., args_n)
 *  - for every type application: f[Ts] ==> f[Ts]() unless followed by parameters
 *  - for every use of a parameterless function: f ==> f()  and  q.f ==> q.f()
 *  - for every def-parameter:  x: => T ==> x: () => T
 *  - for every use of a def-parameter: x ==> x.apply()
 *  - for every argument to a def parameter `x: => T':
 *      if argument is not a reference to a def parameter:
 *        convert argument `e` to (expansion of) `() => e'
 *  - for every repeated Scala parameter `x: T*' --> x: Seq[T].
 *  - for every repeated Java parameter `x: T...' --> x: Array[T], except:
 *    if T is an unbounded abstract type, replace --> x: Array[Object]
 *  - for every argument list that corresponds to a repeated Scala parameter
 *       (a_1, ..., a_n) => (Seq(a_1, ..., a_n))
 *  - for every argument list that corresponds to a repeated Java parameter
 *       (a_1, ..., a_n) => (Array(a_1, ..., a_n))
 *  - for every argument list that is an escaped sequence
 *       (a_1:_*) => (a_1) (possibly converted to sequence or array, as needed)
 *  - convert implicit method types to method types
 *  - convert non-trivial catches in try statements to matches
 *  - convert non-local returns to throws with enclosing try statements.
 */
/*</export> */
abstract class UnCurry extends InfoTransform
                          with reflect.internal.transform.UnCurry
                          with TypingTransformers with ast.TreeDSL {
  val global: Global               // need to repeat here because otherwise last mixin defines global as
                                   // SymbolTable. If we had DOT this would not be an issue
  import global._                  // the global environment
  import definitions._             // standard classes and methods
  import CODE._

  val phaseName: String = "uncurry"

  def newTransformer(unit: CompilationUnit): Transformer = new UnCurryTransformer(unit)
  override def changesBaseClasses = false

// ------ Type transformation --------------------------------------------------------

// uncurry and uncurryType expand type aliases

  /** Traverse tree omitting local method definitions.
   *  If a `return` is encountered, set `returnFound` to true.
   *  Used for MSIL only.
   */
  private object lookForReturns extends Traverser {
    var returnFound = false
    override def traverse(tree: Tree): Unit =  tree match {
      case Return(_) => returnFound = true
      case DefDef(_, _, _, _, _, _) => ;
      case _ => super.traverse(tree)
    }
    def found(tree: Tree) = {
      returnFound = false
      traverse(tree)
      returnFound
    }
  }

  class UnCurryTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {

    private var needTryLift = false
    private var inPattern = false
    private var inConstructorFlag = 0L
    private val byNameArgs = new mutable.HashSet[Tree]
    private val noApply = new mutable.HashSet[Tree]
    private val newMembers = mutable.ArrayBuffer[Tree]()
    private val repeatedParams = mutable.Map[Symbol, List[ValDef]]()

    @inline private def withInPattern[T](value: Boolean)(body: => T): T = {
      inPattern = value
      try body
      finally inPattern = !value
    }

    private lazy val serialVersionUIDAnnotation =
      AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())

    private var nprinted = 0

    override def transform(tree: Tree): Tree = try { //debug
      postTransform(mainTransform(tree))
    } catch {
      case ex: Throwable =>
        if (nprinted < 10) {
          Console.println("exception when traversing " + tree)
          nprinted += 1
        }
        throw ex
    }

    /* Is tree a reference `x` to a call by name parameter that needs to be converted to
     * x.apply()? Note that this is not the case if `x` is used as an argument to another
     * call by name parameter.
     */
    def isByNameRef(tree: Tree): Boolean =
      tree.isTerm && tree.hasSymbol &&
      isByNameParamType(tree.symbol.tpe) &&
      !byNameArgs(tree)

    /** Uncurry a type of a tree node.
     *  This function is sensitive to whether or not we are in a pattern -- when in a pattern
     *  additional parameter sections of a case class are skipped.
     */
    def uncurryTreeType(tp: Type): Type = tp match {
      case MethodType(params, MethodType(params1, restpe)) if inPattern =>
        uncurryTreeType(MethodType(params, restpe))
      case _ =>
        uncurry(tp)
    }

// ------- Handling non-local returns -------------------------------------------------

    /** The type of a non-local return expression with given argument type */
    private def nonLocalReturnExceptionType(argtype: Type) =
      appliedType(NonLocalReturnControlClass.typeConstructor, List(argtype))

    /** A hashmap from method symbols to non-local return keys */
    private val nonLocalReturnKeys = perRunCaches.newMap[Symbol, Symbol]()

    /** Return non-local return key for given method */
    private def nonLocalReturnKey(meth: Symbol) =
      nonLocalReturnKeys.getOrElseUpdate(meth,
        meth.newValue(unit.freshTermName("nonLocalReturnKey"), meth.pos, SYNTHETIC) setInfo ObjectClass.tpe
      )

    /** Generate a non-local return throw with given return expression from given method.
     *  I.e. for the method's non-local return key, generate:
     *
     *    throw new NonLocalReturnControl(key, expr)
     *  todo: maybe clone a pre-existing exception instead?
     *  (but what to do about exceptions that miss their targets?)
     */
    private def nonLocalReturnThrow(expr: Tree, meth: Symbol) = localTyper typed {
      Throw(
        nonLocalReturnExceptionType(expr.tpe.widen),
        Ident(nonLocalReturnKey(meth)),
        expr
      )
    }

    /** Transform (body, key) to:
     *
     *  {
     *    val key = new Object()
     *    try {
     *      body
     *    } catch {
     *      case ex: NonLocalReturnControl[_] =>
     *        if (ex.key().eq(key)) ex.value()
     *        else throw ex
     *    }
     *  }
     */
    private def nonLocalReturnTry(body: Tree, key: Symbol, meth: Symbol) = {
      localTyper typed {
        val extpe   = nonLocalReturnExceptionType(meth.tpe.finalResultType)
        val ex      = meth.newValue(nme.ex, body.pos) setInfo extpe
        val pat     = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(meth.tpe.finalResultType))
        val rhs = (
          IF   ((ex DOT nme.key)() OBJ_EQ Ident(key))
          THEN ((ex DOT nme.value)())
          ELSE (Throw(Ident(ex)))
        )
        val keyDef   = ValDef(key, New(ObjectClass.tpe))
        val tryCatch = Try(body, pat -> rhs)

        Block(List(keyDef), tryCatch)
      }
    }

// ------ Transforming anonymous functions and by-name-arguments ----------------

    /** Undo eta expansion for parameterless and nullary methods */
    def deEta(fun: Function): Tree = fun match {
      case Function(List(), Apply(expr, List())) if treeInfo.isExprSafeToInline(expr) =>
        if (expr hasSymbolWhich (_.isLazy))
          fun
        else
          expr
      case Function(List(), expr) if isByNameRef(expr) =>
        noApply += expr
        expr
      case _ =>
        fun
    }


    /*  Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to
     *
     *    class $anon() extends AbstractFunctionN[T_1, .., T_N, R] with Serializable {
     *      def apply(x_1: T_1, ..., x_N: T_n): R = body
     *    }
     *    new $anon()
     *
     *  transform a function node (x => body) of type PartialFunction[T, R] where
     *    body = expr match { case P_i if G_i => E_i }_i=1..n
     *  to:
     *
     //TODO: correct code template below
     *    class $anon() extends AbstractPartialFunction[T, R] with Serializable {
     *      def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = (expr: @unchecked) match {
     *        case P_1 if G_1 => E_1
     *        ...
     *        case P_n if G_n => E_n
     *        case _ => default(expr)
     *      }
     *      def isDefinedAt(x: T): boolean = (x: @unchecked) match {
     *        case P_1 if G_1 => true
     *        ...
     *        case P_n if G_n => true
     *        case _ => false
     *      }
     *    }
     *    new $anon()
     *
     *  However, if one of the patterns P_i if G_i is a default pattern,
     *  drop the last default clause in the definition of `apply` and generate for `_isDefinedAt` instead
     *
     *      def isDefinedAtCurrent(x: T): boolean = true
     */
    def transformFunction(fun: Function): Tree =
      deEta(fun) match {
        // nullary or parameterless
        case fun1 if fun1 ne fun => fun1
        case _ =>
          def owner     = fun.symbol.owner
          def targs     = fun.tpe.typeArgs
          def isPartial = fun.tpe.typeSymbol == PartialFunctionClass
          assert(!(opt.virtPatmat && isPartial)) // empty-selector matches have already been translated into instantiations of anonymous (partial) functions

          def parents =
            if (isFunctionType(fun.tpe)) List(abstractFunctionForFunctionType(fun.tpe), SerializableClass.tpe)
            else if (isPartial) List(appliedType(AbstractPartialFunctionClass.typeConstructor, targs), SerializableClass.tpe)
            else List(ObjectClass.tpe, fun.tpe, SerializableClass.tpe)

          val anonClass = owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation
          anonClass setInfo ClassInfoType(parents, newScope, anonClass)

          val (formals, restpe) = (targs.init, targs.last)

          def applyMethodDef = {
            val methSym = anonClass.newMethod(nme.apply, fun.pos, FINAL)
            methSym setInfoAndEnter MethodType(methSym newSyntheticValueParams formals, restpe)

            fun.vparams foreach  (_.symbol.owner =  methSym)
            fun.body changeOwner (fun.symbol     -> methSym)

            val body    = localTyper.typedPos(fun.pos)(fun.body)
            val methDef = DefDef(methSym, List(fun.vparams), body)

            // Have to repack the type to avoid mismatches when existentials
            // appear in the result - see SI-4869.
            methDef.tpt setType localTyper.packedType(body, methSym)
            methDef
          }

          // def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 =
          def applyOrElseMethodDef = {
            val methSym = anonClass.newMethod(fun.pos, nme.applyOrElse) setFlag (FINAL | OVERRIDE)

            val List(argtpe)            = formals
            val A1                      = methSym newTypeParameter(newTypeName("A1")) setInfo TypeBounds.upper(argtpe)
            val B1                      = methSym newTypeParameter(newTypeName("B1")) setInfo TypeBounds.lower(restpe)
            val methFormals             = List(A1.tpe, functionType(List(A1.tpe), B1.tpe))
            val params@List(x, default) = methSym newSyntheticValueParams methFormals
            methSym setInfoAndEnter polyType(List(A1, B1), MethodType(params, B1.tpe))

            val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), List(x))
            val body = localTyper.typedPos(fun.pos) { import CODE._
              gen.mkUncheckedMatch(gen.withDefaultCase(substParam(fun.body), scrut => REF(default) APPLY (REF(x))))
            }
            body.changeOwner(fun.symbol -> methSym)

            val methDef = DefDef(methSym, body)

            // Have to repack the type to avoid mismatches when existentials
            // appear in the result - see SI-4869.
            methDef.tpt setType localTyper.packedType(body, methSym)
            methDef
          }

          // duplicate before applyOrElseMethodDef is run so we start with the same symbols as applyOrElseMethodDef
          // otherwise `TreeSymSubstituter(fun.vparams map (_.symbol), params)` won't work as the subst has been run already
          val bodyForIDA = fun.body.duplicate
          def isDefinedAtMethodDef = {
            val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL)
            val params  = methSym newSyntheticValueParams formals
            methSym setInfoAndEnter MethodType(params, BooleanClass.tpe)

            val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params)
            def doSubst(x: Tree) = substParam(resetLocalAttrs(x)) // see pos/t1761 for why `resetLocalAttrs`
            object isDefinedAtTransformer extends gen.MatchMatcher {
              // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly
              override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = { import CODE._
                gen.mkUncheckedMatch(
                  if (cases exists treeInfo.isDefaultCase) TRUE_typed
                  else
                    doSubst(wrap(
                        Match(selector,
                            (cases map (c => deriveCaseDef(c)(x => TRUE_typed))) :+ (
                            DEFAULT ==> FALSE_typed)
                        )))
                )
              }
            }
            val body = isDefinedAtTransformer(bodyForIDA)
            body.changeOwner(fun.symbol -> methSym)

            DefDef(methSym, body)
          }

          val members =
            if (isPartial) List(applyOrElseMethodDef, isDefinedAtMethodDef)
            else List(applyMethodDef)

          localTyper.typedPos(fun.pos) {
            Block(
              List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, fun.pos)),
              Typed(New(anonClass.tpe), TypeTree(fun.tpe)))
          }
        }

    def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = {
      val isJava = fun.isJavaDefined
      def transformVarargs(varargsElemType: Type) = {
        def mkArrayValue(ts: List[Tree], elemtp: Type) =
          ArrayValue(TypeTree(elemtp), ts) setType arrayType(elemtp)

        // when calling into scala varargs, make sure it's a sequence.
        def arrayToSequence(tree: Tree, elemtp: Type) = {
          afterUncurry {
            localTyper.typedPos(pos) {
              val pt = arrayType(elemtp)
              val adaptedTree = // might need to cast to Array[elemtp], as arrays are not covariant
                if (tree.tpe <:< pt) tree
                else gen.mkCastArray(tree, elemtp, pt)

              gen.mkWrapArray(adaptedTree, elemtp)
            }
          }
        }

        // when calling into java varargs, make sure it's an array - see bug #1360
        def sequenceToArray(tree: Tree) = {
          val toArraySym = tree.tpe member nme.toArray
          assert(toArraySym != NoSymbol)
          def getManifest(tp: Type): Tree = {
            val manifestOpt = localTyper.findManifest(tp, false)
            // Don't want bottom types getting any further than this (SI-4024)
            if (tp.typeSymbol.isBottomClass) getManifest(AnyClass.tpe)
            else if (!manifestOpt.tree.isEmpty) manifestOpt.tree
            else if (tp.bounds.hi ne tp) getManifest(tp.bounds.hi)
            else localTyper.getManifestTree(tree, tp, false)
          }
          afterUncurry {
            localTyper.typedPos(pos) {
              Apply(gen.mkAttributedSelect(tree, toArraySym),
                    List(getManifest(tree.tpe.baseType(TraversableClass).typeArgs.head)))
            }
          }
        }

        var suffix: Tree =
          if (treeInfo isWildcardStarArgList args) {
            val Typed(tree, _) = args.last;
            if (isJava)
              if (tree.tpe.typeSymbol == ArrayClass) tree
              else sequenceToArray(tree)
            else
              if (tree.tpe.typeSymbol isSubClass TraversableClass) tree   // @PP: I suspect this should be SeqClass
              else arrayToSequence(tree, varargsElemType)
          }
          else {
            def mkArray = mkArrayValue(args drop (formals.length - 1), varargsElemType)
            if (isJava || inPattern) mkArray
            else if (args.isEmpty) gen.mkNil  // avoid needlessly double-wrapping an empty argument list
            else arrayToSequence(mkArray, varargsElemType)
          }

        afterUncurry {
          if (isJava && isPrimitiveArray(suffix.tpe) && isArrayOfSymbol(fun.tpe.params.last.tpe, ObjectClass)) {
            suffix = localTyper.typedPos(pos) {
              gen.mkRuntimeCall(nme.toObjectArray, List(suffix))
            }
          }
        }
        args.take(formals.length - 1) :+ (suffix setType formals.last)
      }

      val args1 = if (isVarArgTypes(formals)) transformVarargs(formals.last.typeArgs.head) else args

      map2(formals, args1) { (formal, arg) =>
        if (!isByNameParamType(formal)) {
          arg
        } else if (isByNameRef(arg)) {
          byNameArgs += arg
          arg setType functionType(List(), arg.tpe)
        } else {
          if (opt.verboseDebug) {
            val posstr  = arg.pos.source.path + ":" + arg.pos.line
            val permstr = if (fun.isPrivate) "private" else "notprivate"
            log("byname | %s | %s | %s".format(posstr, fun.fullName, permstr))
          }

          val result = localTyper.typed(
            Function(Nil, arg) setPos arg.pos).asInstanceOf[Function]
          new ChangeOwnerTraverser(currentOwner, result.symbol).traverse(arg)
          transformFunction(result)
        }
      }
    }

    /** Called if a tree's symbol is elidable.  If it's a DefDef,
     *  replace only the body/rhs with 0/false/()/null; otherwise replace
     *  the whole tree with it.
     */
    private def replaceElidableTree(tree: Tree): Tree = {
      tree match {
        case DefDef(_,_,_,_,_,_) =>
          deriveDefDef(tree)(rhs => Block(Nil, gen.mkZero(rhs.tpe)) setType rhs.tpe) setSymbol tree.symbol setType tree.tpe
        case _ =>
          gen.mkZero(tree.tpe) setType tree.tpe
      }
    }

    private def isSelfSynchronized(ddef: DefDef) = ddef.rhs match {
      case Apply(fn @ TypeApply(Select(sel, _), _), _) =>
        fn.symbol == Object_synchronized && sel.symbol == ddef.symbol.enclClass && !ddef.symbol.enclClass.isTrait
      case _ => false
    }

    /** If an eligible method is entirely wrapped in a call to synchronized
     *  locked on the same instance, remove the synchronized scaffolding and
     *  mark the method symbol SYNCHRONIZED for bytecode generation.
     */
    private def translateSynchronized(tree: Tree) = tree match {
      case dd @ DefDef(_, _, _, _, _, Apply(fn, body :: Nil)) if isSelfSynchronized(dd) =>
        log("Translating " + dd.symbol.defString + " into synchronized method")
        dd.symbol setFlag SYNCHRONIZED
        deriveDefDef(dd)(_ => body)
      case _ => tree
    }
    def isNonLocalReturn(ret: Return) = ret.symbol != currentOwner.enclMethod || currentOwner.isLazy

// ------ The tree transformers --------------------------------------------------------

    def mainTransform(tree: Tree): Tree = {
      @inline def withNeedLift(needLift: Boolean)(f: => Tree): Tree = {
        val saved = needTryLift
        needTryLift = needLift
        try f
        finally needTryLift = saved
      }

      /** A try or synchronized needs to be lifted anyway for MSIL if it contains
       *  return statements. These are disallowed in the CLR. By lifting
       *  such returns will be converted to throws.
       */
      def shouldBeLiftedAnyway(tree: Tree) = false && // buggy, see #1981
        forMSIL && lookForReturns.found(tree)

      /** Transform tree `t` to { def f = t; f } where `f` is a fresh name
       */
      def liftTree(tree: Tree) = {
        debuglog("lifting tree at: " + (tree.pos))
        val sym = currentOwner.newMethod(unit.freshTermName("liftedTree"), tree.pos)
        sym.setInfo(MethodType(List(), tree.tpe))
        tree.changeOwner(currentOwner -> sym)
        localTyper.typedPos(tree.pos)(Block(
          List(DefDef(sym, List(Nil), tree)),
          Apply(Ident(sym), Nil)
        ))
      }

      def withInConstructorFlag(inConstructorFlag: Long)(f: => Tree): Tree = {
        val saved = this.inConstructorFlag
        this.inConstructorFlag = inConstructorFlag
        try f
        finally this.inConstructorFlag = saved
      }

      val sym = tree.symbol
      // Take a pass looking for @specialize annotations and set all
      // their SPECIALIZE flags for cheaper recognition.
      if ((sym ne null) && (sym.isClass || sym.isMethod)) {
        for (tp <- sym.typeParams ; if tp hasAnnotation SpecializedClass)
          tp setFlag SPECIALIZED
      }
      val result = (
        // TODO - settings.noassertions.value temporarily retained to avoid
        // breakage until a reasonable interface is settled upon.
        if ((sym ne null) && (sym.elisionLevel.exists (_ < settings.elidebelow.value || settings.noassertions.value)))
          replaceElidableTree(tree)
        else translateSynchronized(tree) match {
          case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
            if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd)

            withNeedLift(false) {
              if (dd.symbol.isClassConstructor) {
                atOwner(sym) {
                  val rhs1 = (rhs: @unchecked) match {
                    case Block(stats, expr) =>
                      def transformInConstructor(stat: Tree) =
                        withInConstructorFlag(INCONSTRUCTOR) { transform(stat) }
                      val presupers = treeInfo.preSuperFields(stats) map transformInConstructor
                      val rest = stats drop presupers.length
                      val supercalls = rest take 1 map transformInConstructor
                      val others = rest drop 1 map transform
                      treeCopy.Block(rhs, presupers ::: supercalls ::: others, transform(expr))
                  }
                  treeCopy.DefDef(
                    dd, mods, name, transformTypeDefs(tparams),
                    transformValDefss(vparamss), transform(tpt), rhs1)
                }
              } else {
                super.transform(dd)
              }
            }
          case ValDef(_, _, _, rhs) =>
            if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit)
            // a local variable that is mutable and free somewhere later should be lifted
            // as lambda lifting (coming later) will wrap 'rhs' in an Ref object.
            if (!sym.owner.isSourceMethod)
              withNeedLift(true) { super.transform(tree) }
            else
              super.transform(tree)
          case UnApply(fn, args) =>
            val fn1 = withInPattern(false)(transform(fn))
            val args1 = transformTrees(fn.symbol.name match {
              case nme.unapply    => args
              case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeListFromReturnTypeSeq(fn.tpe))
              case _              => sys.error("internal error: UnApply node has wrong symbol")
            })
            treeCopy.UnApply(tree, fn1, args1)

          case Apply(fn, args) =>
            if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head))
              transform(treeCopy.Apply(tree, fn, List(liftTree(args.head))))
            else
              withNeedLift(true) {
                val formals = fn.tpe.paramTypes
                treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals)))
              }

          case Assign(Select(_, _), _) =>
            withNeedLift(true) { super.transform(tree) }

          case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) =>
            withNeedLift(true) { super.transform(tree) }

          case ret @ Return(_) if (isNonLocalReturn(ret)) =>
            withNeedLift(true) { super.transform(ret) }

          case Try(block, catches, finalizer) =>
            if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree))
            else super.transform(tree)

          case CaseDef(pat, guard, body) =>
            val pat1 = withInPattern(true)(transform(pat))
            treeCopy.CaseDef(tree, pat1, transform(guard), transform(body))

          case fun @ Function(_, _) =>
            mainTransform(transformFunction(fun))

          case Template(_, _, _) =>
            withInConstructorFlag(0) { super.transform(tree) }

          case _ =>
            val tree1 = super.transform(tree)
            if (isByNameRef(tree1)) {
              val tree2 = tree1 setType functionType(Nil, tree1.tpe)
              return {
                if (noApply contains tree2) tree2
                else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil))
              }
            }
            tree1
        }
      )
      assert(result.tpe != null, result + " tpe is null")
      result setType uncurryTreeType(result.tpe)
    }

    def postTransform(tree: Tree): Tree = afterUncurry {
      def applyUnary(): Tree = {
        // TODO_NMT: verify that the inner tree of a type-apply also gets parens if the
        // whole tree is a polymorphic nullary method application
        def removeNullary() = tree.tpe match {
          case MethodType(_, _)           => tree
          case tp                         => tree setType MethodType(Nil, tp.resultType)
        }
        if (tree.symbol.isMethod && !tree.tpe.isInstanceOf[PolyType])
          gen.mkApplyIfNeeded(removeNullary())
        else if (tree.isType)
          TypeTree(tree.tpe) setPos tree.pos
        else
          tree
      }

      tree match {
        /* Some uncurry post transformations add members to templates.
         * When inside a template, the following sequence is available:
         * - newMembers
         * Any entry in this sequence will be added into the template
         * once the template transformation has finished.
         *
         * In particular, this case will add:
         * - synthetic Java varargs forwarders for repeated parameters
         */
        case Template(_, _, _) =>
          localTyper = typer.atOwner(tree, currentClass)
          try deriveTemplate(tree)(transformTrees(newMembers.toList) ::: _)
          finally newMembers.clear()

        case dd @ DefDef(_, _, _, vparamss0, _, rhs0) =>
          val flatdd = copyDefDef(dd)(
            vparamss = List(vparamss0.flatten),
            rhs = nonLocalReturnKeys get dd.symbol match {
              case Some(k) => atPos(rhs0.pos)(nonLocalReturnTry(rhs0, k, dd.symbol))
              case None    => rhs0
            }
          )
          addJavaVarargsForwarders(dd, flatdd)

        case Try(body, catches, finalizer) =>
          if (opt.virtPatmat) { if(catches exists (cd => !treeInfo.isCatchCase(cd))) debugwarn("VPM BUG! illegal try/catch "+ catches); tree }
          else if (catches forall treeInfo.isCatchCase) tree
          else {
            val exname = unit.freshTermName("ex$")
            val cases =
              if ((catches exists treeInfo.isDefaultCase) || (catches.last match {  // bq: handle try { } catch { ... case ex:Throwable => ...}
                    case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) =>
                      true
                    case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) =>
                      true
                    case _ =>
                      false
                  })) catches
              else catches :+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Throw(Ident(exname)))
            val catchall =
              atPos(tree.pos) {
                CaseDef(
                  Bind(exname, Ident(nme.WILDCARD)),
                  EmptyTree,
                  Match(Ident(exname), cases))
              }
            debuglog("rewrote try: " + catches + " ==> " + catchall);
            val catches1 = localTyper.typedCases(
              List(catchall), ThrowableClass.tpe, WildcardType)
            treeCopy.Try(tree, body, catches1, finalizer)
          }
        case Apply(Apply(fn, args), args1) =>
          treeCopy.Apply(tree, fn, args ::: args1)
        case Ident(name) =>
          assert(name != tpnme.WILDCARD_STAR)
          applyUnary()
        case Select(_, _) | TypeApply(_, _) =>
          applyUnary()
        case ret @ Return(expr) if (isNonLocalReturn(ret)) =>
          debuglog("non local return in "+ret.symbol+" from "+currentOwner.enclMethod)
          atPos(ret.pos)(nonLocalReturnThrow(expr, ret.symbol))
        case TypeTree() =>
          tree
        case _ =>
          if (tree.isType) TypeTree(tree.tpe) setPos tree.pos else tree
      }
    }

    /* Analyzes repeated params if method is annotated as `varargs`.
     * If the repeated params exist, it saves them into the `repeatedParams` map,
     * which is used later.
     */
    private def saveRepeatedParams(dd: DefDef): Unit =
      if (dd.symbol.isConstructor)
        unit.error(dd.symbol.pos, "A constructor cannot be annotated with a `varargs` annotation.")
      else treeInfo.repeatedParams(dd) match {
        case Nil  =>
          unit.error(dd.symbol.pos, "A method without repeated parameters cannot be annotated with the `varargs` annotation.")
        case reps =>
          repeatedParams(dd.symbol) = reps
      }

    /* Called during post transform, after the method argument lists have been flattened.
     * It looks for the method in the `repeatedParams` map, and generates a Java-style
     * varargs forwarder. It then adds the forwarder to the `newMembers` sequence.
     */
    private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef): DefDef = {
      if (!dd.symbol.hasAnnotation(VarargsClass) || !repeatedParams.contains(dd.symbol))
        return flatdd

      def toSeqType(tp: Type): Type = {
        val arg = elementType(ArrayClass, tp)
        seqType(arg)
      }
      def toArrayType(tp: Type): Type = {
        val arg = elementType(SeqClass, tp)
        // to prevent generation of an `Object` parameter from `Array[T]` parameter later
        // as this would crash the Java compiler which expects an `Object[]` array for varargs
        //   e.g.        def foo[T](a: Int, b: T*)
        //   becomes     def foo[T](a: Int, b: Array[Object])
        //   instead of  def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object)
        arrayType(
          if (arg.typeSymbol.isTypeParameterOrSkolem) ObjectClass.tpe
          else arg
        )
      }

      val reps          = repeatedParams(dd.symbol)
      val rpsymbols     = reps.map(_.symbol).toSet
      val theTyper      = typer.atOwner(dd, currentClass)
      val flatparams    = flatdd.vparamss.head

      // create the type
      val forwformals = flatparams map {
        case p if rpsymbols(p.symbol) => toArrayType(p.symbol.tpe)
        case p                        => p.symbol.tpe
      }
      val forwresult = dd.symbol.tpe.finalResultType
      val forwformsyms = map2(forwformals, flatparams)((tp, oldparam) =>
        currentClass.newValueParameter(oldparam.name, oldparam.symbol.pos).setInfo(tp)
      )
      def mono = MethodType(forwformsyms, forwresult)
      val forwtype = dd.symbol.tpe match {
        case MethodType(_, _) => mono
        case PolyType(tps, _) => PolyType(tps, mono)
      }

      // create the symbol
      val forwsym = currentClass.newMethod(dd.name, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags) setInfo forwtype

      // create the tree
      val forwtree = theTyper.typedPos(dd.pos) {
        val locals = map2(forwsym ARGS, flatparams) {
          case (_, fp) if !rpsymbols(fp.symbol) => null
          case (argsym, fp)                     =>
            Block(Nil,
              gen.mkCast(
                gen.mkWrapArray(Ident(argsym), elementType(ArrayClass, argsym.tpe)),
                seqType(elementType(SeqClass, fp.symbol.tpe))
              )
            )
        }
        val seqargs = map2(locals, forwsym ARGS) {
          case (null, argsym) => Ident(argsym)
          case (l, _)         => l
        }
        val end = if (forwsym.isConstructor) List(UNIT) else Nil

        DEF(forwsym) === BLOCK(
          Apply(gen.mkAttributedRef(flatdd.symbol), seqargs) :: end : _*
        )
      }

      // check if the method with that name and those arguments already exists in the template
      currentClass.info.member(forwsym.name).alternatives.find(s => s != forwsym && s.tpe.matches(forwsym.tpe)) match {
        case Some(s) => unit.error(dd.symbol.pos,
                                   "A method with a varargs annotation produces a forwarder method with the same signature "
                                   + s.tpe + " as an existing method.")
        case None =>
          // enter symbol into scope
          currentClass.info.decls enter forwsym
          // add the method to `newMembers`
          newMembers += forwtree
      }

      flatdd
    }
  }
}