aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala
blob: b0bd40578c17097e2591de08d62e737a3559de37 (plain) (tree)
1
2
3
4
5
6
7
8
9

                   
                 


                                             

                                                                                  
                                         
                                                         
                                           
                                             
                                 
                                   
                                         
                                     
                               
                                 
                                  

                       
              

                                                                           


















                                                                                                                               
                                                                                                                                             













                                                                                                                                   


                                                 
 
                                              
 






                                                                          









                                                                      
                                                                            
                                                                      
                                                                        








                                                                              
 

                                                                







                                                                                                      








                                                                                                  
                                                                                                        
                                                                                                  
                                                                                                    







                                                                                                          
                                                                                                           
                                                                                             
 

                                                                                            
                                                                                    
                                                                                                                       

                                                                                         
                                                                                                                                                

                                                                                             
                                                                                                                                            
 



                                                                                              





                                                                   


                                         


                                                                                                   
                                                             

                                                           
                                                          
                                                




                                                     

   





                                                                          
                                                              
                                                                                

                                                 

                                                                                                         
 


                                                                                                     

                                             

                                                                                        




                                                                                       

                    
       

   
                                                 
                                    

   
                                                                
 
                                                                                                                      
 


                                                                                                                                         






                                                                                         
                                                                                               




                                                                                             
                                                                                     











                                                                                              
                                                           







                               

                                                                                                                                  
















                                                                       
                                                  








                                                                     








                                                                 
                                                                       
                                                                 
                                                                   







                                                                         
                                                             
                                                           








                                                                     








                                                                 
                                                                       
                                                                 
                                                                   







                                                                         
                                                             
                                                           
                                                             

     
                                                       
                                                                         

     
                                                                                                                                 

                       
                                                                     







                                                                                                                                       








                                                                                                                                 
                                                                                                                                          
                                                                                                                                 
                                                                                                                                    

















                                                                                                                                             








                                                                                                                                  
                                                                                                                                           
                                                                                                                                  
                                                                                                                                     









                                                                                                                                              

     
                                                                                                                   

                                                                                                                      
       







                                       








                                     
                                        
                                     
                                      







                                         
                                   
                                  








                                        








                                      
                                         
                                      
                                       







                                          
                                    
                                   
                                    




                                                                                      
                                    
 

                                                     
                                           


                                           
                                                                                                                                                 



                                    







                                                                                                                               


                           
                                    
                                                                                                      

                                                          

                                                          


                                                                                 






                                        
                                                            

     






                                                                                                         









                                                                                                   
                                                                                                            
                                                                                                   
                                                                                                      







                                                                                                               
                                                                                                    
                                                                                          
 



                                                                   



                                                                                                                                     
                                                                
                                 


                                                                                                                 




                                                                                                                            
                                          
                                                                                         








                                                                                                                              
                                          
                                                                                          








                                                                                                                          
                                          
                                                                                        








                                                                                                                            
                                          
                                                                                         








                                                                                                                            
                                          
                                                                                         








                                                                                                                                    
                                          
                                                                                             








                                                                                                                        
                                          
                                                                                       






                                                              

                                                                                                                            
                                          
                                                                                         








                                                                                                                              
                                          
                                                                                          








                                                                                                                                
                                          
                                                                                           








                                                                                                                            
                                          
                                                                                         








                                                                                                                      
                                          
                                                                                      








                                                                                                                                
                                          
                                                                                           








                                                                                                                            
                                          
                                                                                         








                                                                                                                                
                                          
                                                                                           








                                                                                                                              
                                          
                                                                                          








                                                                                                                        
                                          
                                                                                       






                                                              

                                                                                                                                      
                                          
                                                                                              






                                                                                   










                                                                                                                                

                                                                                                                                  
                                          
                                                                                            






                                                                             

                                                                                                                          
                                          
                                                                                        








                                                                                                                                        
                                          
                                                                                               








                                                                                                                              
                                          
                                                                                          








                                                                                                                              
                                          
                                                                                          








                                                                                                                                
                                          
                                                                                           








                                                                                                                                
                                          
                                                                                           








                                                                                                                                  
                                          
                                                                                            








                                                                                                                                      
                                          
                                                                                              





                                                                                   








                                                                                                                          


                                                                                                                           
                                                                                         



                                   



                                                                                                                             






                                                                                        
                                                                                                                           






                                                                                








                                                                          

                                                                                   




                                                                                      
                                   
                                                             


                                                                                                                                 



                                                    
                                           

                 
 





                                                                                          

                                                                                                                                    

                           

                                                                                                                            


                                                              
                                                                                                                              


                                                                  
                                                                                          
           
                          
                                                                                                                          

                                       

                                                                                    

                                                                                                                                
                                                                                                                              

                                       


                                                                                      
                                                                                              

                            
                                                                                                                              

                                       




                                                                                                           
                                                                                                                

                             
                                                                                                                                

                                       
                                                                                      
                                                                                            







                                                                                                                                  
                                                                                                                          


                                                            
                                                                                                                            


                                                             
                                                                                      

                           
                                                                                                                            


                                                           
                                                                     
                                                                                 

                               
                                                                                                                                    


                                                           
                                                                  
                                                                                             

                             
                                                                                                                                


                                                                  
                                                                                                                        


                                                           
                                                                     
           
                           
                                                                                                                            



                                                             
                                                                                 

                            
                                                                                                                              



                                                           
                                                                                   

                           
                                                                                                                            

                                       
                                                                               
                                                             
                                                                                   

                        
                                                                                                                      




                                                               
                                                                                 

                             
                                                                                                                                

                                       
                                                                

                                                             
                                                                                            

                           
                                                                                                                            


                                                                     
                                                                       
                                                                                       

                             
                                                                                                                                

                                       
                                                                                      

                                                               
                                                                                              

                            
                                                                                                                              


                                                             
                                
                                                                                    


                                                                                       
                                                                                     

                         
                                                                                                                        


                                                              
                                                                                                           
                                                                       
                                                                                          
           
                                
                                                                                                                                      

                                       
                                                                    

                                                                                                     
           




                                                                                                                                
                                                                                            

                                                                                                            
                              
                                                                                                                                  
                                       
                                                                    
                                 
                                                                                                                                        

                                       
                                                                    
                                                                                               

                             
                                                                                                                                


                                                           

                                                                            
                                                                                                      

                              
                                                                                                                                  


                                                                    
                                                                                              
                                                                
                                                                               
                                                                                                            

                                
                                                                                                                                      

                                       
                                                     
                                                              
                                                                                            
                                                                                                 
           

                                                             
                    

                                                           

       

                                              
                                                                                                                                                                                        











                                                                                                    




                                                                                                       

                  

     


                                                                                                                                        
                                          
                                                                                              



                                                     
                                                                                                                                        
                                                                                               
                                                        
                                                                        
                                                                                 
                                                                                     


                                                                       
     
 
                                                                                                                   




                                                                                                            

                                                                                                                              
   
 
package dotty.tools
package dotc
package transform

import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.DenotTransformers.{InfoTransformer, DenotTransformer}
import dotty.tools.dotc.core.Denotations.SingleDenotation
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.core.SymDenotations.SymDenotation
import dotty.tools.dotc.core.Symbols.Symbol
import dotty.tools.dotc.core.Flags.PackageVal
import dotty.tools.dotc.core.Mode
import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.util.DotClass
import scala.annotation.tailrec
import config.Printers.transforms
import scala.util.control.NonFatal

object TreeTransforms {
  import tpd._

  /** The base class of tree transforms. For each kind of tree K, there are
   *  two methods which can be overridden:
   *
   *  prepareForK // return a new TreeTransform which gets applied to the K
   *  // node and its children
   *  transformK // transform node of type K
   *
   *  If a transform does not need to visit a node or any of its children, it
   *  signals this fact by returning a NoTransform from a prepare method.
   *
   *  If all transforms in a group are NoTransforms, the tree is no longer traversed.
   *
   *
   *            Performance analysis: Taking the dotty compiler frontend as a use case, we are aiming for a warm performance of
   *            about 4000 lines / sec. This means 6 seconds for a codebase of 24'000 lines. Of these the frontend consumes
   *            over 2.5 seconds, erasure and code generation will most likely consume over 1 second each. So we would have
   *            about 1 sec for all other transformations in our budget. Of this second, let's assume a maximum of 20% for
   *            the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with
   *            0.2sec, or roughly 600M processor cycles.
   *
   *            Now, to the amount of work that needs to be done. The codebase produces an average of about 250'000 trees after typechecking.
   *            Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100
   *            micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison,
   *            scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits
   *            of a node by a transformation. Each visit has a budget of 20 processor cycles.
   *
   *            A more detailed breakdown: I assume that about one third of all transformations have real work to do for each node.
   *            This might look high, but keep in mind that the most common nodes are Idents and Selects, and most transformations
   *            touch these. By contrast the amount of work for generating new transformations should be negligible.
   *
   *            So, in 400 clock cycles we need to (1) perform a pattern match according to the type of node, (2) generate new
   *            transformations if applicable, (3) reconstitute the tree node from the result of transforming the children, and
   *            (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable
   *            for achieving this goal, but there can be no wasted cycles anywhere.
   */
  abstract class TreeTransform extends DotClass {

    def phase: MiniPhase

    def treeTransformPhase: Phase = phase.next

    def prepareForIdent(tree: Ident)(implicit ctx: Context) = this
    def prepareForSelect(tree: Select)(implicit ctx: Context) = this
    def prepareForThis(tree: This)(implicit ctx: Context) = this
    def prepareForSuper(tree: Super)(implicit ctx: Context) = this
    def prepareForApply(tree: Apply)(implicit ctx: Context) = this
    def prepareForTypeApply(tree: TypeApply)(implicit ctx: Context) = this
    def prepareForLiteral(tree: Literal)(implicit ctx: Context) = this
    def prepareForNew(tree: New)(implicit ctx: Context) = this
    def prepareForTyped(tree: Typed)(implicit ctx: Context) = this
    def prepareForAssign(tree: Assign)(implicit ctx: Context) = this
    def prepareForBlock(tree: Block)(implicit ctx: Context) = this
    def prepareForIf(tree: If)(implicit ctx: Context) = this
    def prepareForClosure(tree: Closure)(implicit ctx: Context) = this
    def prepareForMatch(tree: Match)(implicit ctx: Context) = this
    def prepareForCaseDef(tree: CaseDef)(implicit ctx: Context) = this
    def prepareForReturn(tree: Return)(implicit ctx: Context) = this
    def prepareForTry(tree: Try)(implicit ctx: Context) = this
    def prepareForSeqLiteral(tree: SeqLiteral)(implicit ctx: Context) = this
    def prepareForInlined(tree: Inlined)(implicit ctx: Context) = this
    def prepareForTypeTree(tree: TypeTree)(implicit ctx: Context) = this
    def prepareForBind(tree: Bind)(implicit ctx: Context) = this
    def prepareForAlternative(tree: Alternative)(implicit ctx: Context) = this
    def prepareForTypeDef(tree: TypeDef)(implicit ctx: Context) = this
    def prepareForUnApply(tree: UnApply)(implicit ctx: Context) = this
    def prepareForValDef(tree: ValDef)(implicit ctx: Context) = this
    def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = this
    def prepareForTemplate(tree: Template)(implicit ctx: Context) = this
    def prepareForPackageDef(tree: PackageDef)(implicit ctx: Context) = this
    def prepareForStats(trees: List[Tree])(implicit ctx: Context) = this

    def prepareForUnit(tree: Tree)(implicit ctx: Context) = this

    def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformThis(tree: This)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformSuper(tree: Super)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformLiteral(tree: Literal)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformTyped(tree: Typed)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformMatch(tree: Match)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformCaseDef(tree: CaseDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformReturn(tree: Return)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformTry(tree: Try)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformInlined(tree: Inlined)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformBind(tree: Bind)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformAlternative(tree: Alternative)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformUnApply(tree: UnApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformPackageDef(tree: PackageDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree
    def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees
    def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = tree

    def transformUnit(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = tree

    /** Transform tree using all transforms of current group (including this one) */
    def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, 0)

    /** Transform subtree using all transforms following the current one in this group */
    def transformFollowingDeep(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, phase.idx + 1)

    /** Transform single node using all transforms following the current one in this group */
    def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transformSingle(tree, phase.idx + 1)

    def atGroupEnd[T](action : Context => T)(implicit ctx: Context, info: TransformerInfo) = {
      val last = info.transformers(info.transformers.length - 1)
      action(ctx.withPhase(last.phase.next))
    }
  }

  /** A phase that defines a TreeTransform to be used in a group */
  trait MiniPhase extends Phase { thisPhase =>
    def treeTransform: TreeTransform

    /** id of this mini phase in group */
    var idx: Int = _

    /** List of names of phases that should have finished their processing of all compilation units
     *  before this phase starts
     */
    def runsAfterGroupsOf: Set[Class[_ <: Phase]] = Set.empty

    protected def mkTreeTransformer = new TreeTransformer {
      override def phaseName: String = thisPhase.phaseName
      override def miniPhases = Array(thisPhase)
    }

    override def run(implicit ctx: Context): Unit = {
      mkTreeTransformer.run
    }
  }

  /** A mini phase that is its own tree transform */
  abstract class MiniPhaseTransform extends TreeTransform with MiniPhase {
    def treeTransform = this
    def phase = this
 }

  /** A helper trait to transform annotations on MemberDefs */
  trait AnnotationTransformer extends MiniPhaseTransform with DenotTransformer {

    val annotationTransformer = mkTreeTransformer
    override final def treeTransformPhase = this
      // need to run at own phase because otherwise we get ahead of ourselves in transforming denotations

    abstract override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation =
      super.transform(ref) match {
        case ref1: SymDenotation if ref1.symbol.isDefinedInCurrentRun =>
          val annots = ref1.annotations
          val annotTrees = annots.map(_.tree)
          val annotTrees1 = annotTrees.mapConserve(annotationTransformer.macroTransform)
          if (annotTrees eq annotTrees1) ref1
          else {
            val derivedAnnots = (annots, annotTrees1).zipped.map((annot, annotTree1) =>
              annot.derivedAnnotation(annotTree1))
            ref1.copySymDenotation(annotations = derivedAnnots)
          }
        case ref1 =>
          ref1
      }
  }

  @sharable val NoTransform = new TreeTransform {
    def phase = unsupported("phase")
  }

  type Mutator[T] = (TreeTransform, T, Context) => TreeTransform

  class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group: TreeTransformer)

  /** This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX
   *  Thanks to Martin for this idea
   *  @see NXTransformations.index for format of plan
   */
  class NXTransformations {

    private def hasRedefinedMethod(cls: Class[_], name: String): Boolean =
      if (cls.getDeclaredMethods.exists(_.getName == name)) cls != classOf[TreeTransform]
      else hasRedefinedMethod(cls.getSuperclass, name)

    /** Create an index array `next` of size one larger than the size of `transforms` such that
     *  for each index i, `next(i)` is the smallest index j such that
     *
     *  i <= j
     *  j == transforms.length || transform(j) defines a non-default method with given `name`
     */
    private def index(transformations: Array[Class[_]], name: String): Array[Int] = {
      val len = transformations.length
      val next = new Array[Int](len + 1)
      var nextTransform: Int = len

      /* loop invariant: nextTransform == the smallest j such that
       * i < j and
       * j == transforms.length || transform(j) defines a non-default method with given `name`
       */
      next(len) = len
      var i = len - 1
      while (i >= 0) {
        // update nextTransform if this phase redefines the method
        if (hasRedefinedMethod(transformations(i), name)) {
          nextTransform = i
        }
        next(i) = nextTransform
        i -= 1
      }
      next
    }

    private def indexUpdate(prev: Array[Int], changedTransformation: Class[_], index: Int, name: String, copy: Boolean = true) = {
      val isDefinedNow = hasRedefinedMethod(changedTransformation, name)
      val wasDefinedBefore = prev(index) == index
      if (isDefinedNow == wasDefinedBefore) prev
      else {
        val result = if (copy) prev.clone() else prev
        val oldValue = result(index)
        val newValue =
          if (wasDefinedBefore /* && !isDefinedNow */ ) prev(index + 1)
          else index // isDefinedNow
        var i = index
        while (i >= 0 && result(i) == oldValue) {
          result(i) = newValue
          i -= 1
        }
        result
      }
    }

    def this(transformations: Array[Class[_]]) = {
      this()
      nxPrepIdent = index(transformations, "prepareForIdent")
      nxPrepSelect = index(transformations, "prepareForSelect")
      nxPrepThis = index(transformations, "prepareForThis")
      nxPrepSuper = index(transformations, "prepareForSuper")
      nxPrepApply = index(transformations, "prepareForApply")
      nxPrepTypeApply = index(transformations, "prepareForTypeApply")
      nxPrepLiteral = index(transformations, "prepareForLiteral")
      nxPrepNew = index(transformations, "prepareForNew")
      nxPrepTyped = index(transformations, "prepareForTyped")
      nxPrepAssign = index(transformations, "prepareForAssign")
      nxPrepBlock = index(transformations, "prepareForBlock")
      nxPrepIf = index(transformations, "prepareForIf")
      nxPrepClosure = index(transformations, "prepareForClosure")
      nxPrepCaseDef = index(transformations, "prepareForCaseDef")
      nxPrepMatch = index(transformations, "prepareForMatch")
      nxPrepReturn = index(transformations, "prepareForReturn")
      nxPrepTry = index(transformations, "prepareForTry")
      nxPrepSeqLiteral = index(transformations, "prepareForSeqLiteral")
      nxPrepInlined = index(transformations, "prepareForInlined")
      nxPrepTypeTree = index(transformations, "prepareForTypeTree")
      nxPrepBind = index(transformations, "prepareForBind")
      nxPrepAlternative = index(transformations, "prepareForAlternative")
      nxPrepUnApply = index(transformations, "prepareForUnApply")
      nxPrepValDef = index(transformations, "prepareForValDef")
      nxPrepDefDef = index(transformations, "prepareForDefDef")
      nxPrepTypeDef = index(transformations, "prepareForTypeDef")
      nxPrepTemplate = index(transformations, "prepareForTemplate")
      nxPrepPackageDef = index(transformations, "prepareForPackageDef")
      nxPrepStats = index(transformations, "prepareForStats")
      nxPrepUnit = index(transformations, "prepareForUnit")

      nxTransIdent = index(transformations, "transformIdent")
      nxTransSelect = index(transformations, "transformSelect")
      nxTransThis = index(transformations, "transformThis")
      nxTransSuper = index(transformations, "transformSuper")
      nxTransApply = index(transformations, "transformApply")
      nxTransTypeApply = index(transformations, "transformTypeApply")
      nxTransLiteral = index(transformations, "transformLiteral")
      nxTransNew = index(transformations, "transformNew")
      nxTransTyped = index(transformations, "transformTyped")
      nxTransAssign = index(transformations, "transformAssign")
      nxTransBlock = index(transformations, "transformBlock")
      nxTransIf = index(transformations, "transformIf")
      nxTransClosure = index(transformations, "transformClosure")
      nxTransMatch = index(transformations, "transformMatch")
      nxTransCaseDef = index(transformations, "transformCaseDef")
      nxTransReturn = index(transformations, "transformReturn")
      nxTransTry = index(transformations, "transformTry")
      nxTransSeqLiteral = index(transformations, "transformSeqLiteral")
      nxTransInlined = index(transformations, "transformInlined")
      nxTransTypeTree = index(transformations, "transformTypeTree")
      nxTransBind = index(transformations, "transformBind")
      nxTransAlternative = index(transformations, "transformAlternative")
      nxTransUnApply = index(transformations, "transformUnApply")
      nxTransValDef = index(transformations, "transformValDef")
      nxTransDefDef = index(transformations, "transformDefDef")
      nxTransTypeDef = index(transformations, "transformTypeDef")
      nxTransTemplate = index(transformations, "transformTemplate")
      nxTransPackageDef = index(transformations, "transformPackageDef")
      nxTransStats = index(transformations, "transformStats")
      nxTransUnit = index(transformations, "transformUnit")
      nxTransOther = index(transformations, "transformOther")
    }

    def this(transformations: Array[TreeTransform]) = {
      this(transformations.map(_.getClass).asInstanceOf[Array[Class[_]]])
    }

    def this(prev: NXTransformations, changedTransformation: TreeTransform, transformationIndex: Int, reuse: Boolean = false) = {
      this()
      val copy = !reuse
      val changedTransformationClass = changedTransformation.getClass
      nxPrepIdent = indexUpdate(prev.nxPrepIdent, changedTransformationClass, transformationIndex, "prepareForIdent", copy)
      nxPrepSelect = indexUpdate(prev.nxPrepSelect, changedTransformationClass, transformationIndex, "prepareForSelect", copy)
      nxPrepThis = indexUpdate(prev.nxPrepThis, changedTransformationClass, transformationIndex, "prepareForThis", copy)
      nxPrepSuper = indexUpdate(prev.nxPrepSuper, changedTransformationClass, transformationIndex, "prepareForSuper", copy)
      nxPrepApply = indexUpdate(prev.nxPrepApply, changedTransformationClass, transformationIndex, "prepareForApply", copy)
      nxPrepTypeApply = indexUpdate(prev.nxPrepTypeApply, changedTransformationClass, transformationIndex, "prepareForTypeApply", copy)
      nxPrepLiteral = indexUpdate(prev.nxPrepLiteral, changedTransformationClass, transformationIndex, "prepareForLiteral", copy)
      nxPrepNew = indexUpdate(prev.nxPrepNew, changedTransformationClass, transformationIndex, "prepareForNew", copy)
      nxPrepTyped = indexUpdate(prev.nxPrepTyped, changedTransformationClass, transformationIndex, "prepareForTyped", copy)
      nxPrepAssign = indexUpdate(prev.nxPrepAssign, changedTransformationClass, transformationIndex, "prepareForAssign", copy)
      nxPrepBlock = indexUpdate(prev.nxPrepBlock, changedTransformationClass, transformationIndex, "prepareForBlock", copy)
      nxPrepIf = indexUpdate(prev.nxPrepIf, changedTransformationClass, transformationIndex, "prepareForIf", copy)
      nxPrepClosure = indexUpdate(prev.nxPrepClosure, changedTransformationClass, transformationIndex, "prepareForClosure", copy)
      nxPrepMatch = indexUpdate(prev.nxPrepMatch, changedTransformationClass, transformationIndex, "prepareForMatch", copy)
      nxPrepCaseDef = indexUpdate(prev.nxPrepCaseDef, changedTransformationClass, transformationIndex, "prepareForCaseDef", copy)
      nxPrepReturn = indexUpdate(prev.nxPrepReturn, changedTransformationClass, transformationIndex, "prepareForReturn", copy)
      nxPrepTry = indexUpdate(prev.nxPrepTry, changedTransformationClass, transformationIndex, "prepareForTry", copy)
      nxPrepSeqLiteral = indexUpdate(prev.nxPrepSeqLiteral, changedTransformationClass, transformationIndex, "prepareForSeqLiteral", copy)
      nxPrepInlined = indexUpdate(prev.nxPrepInlined, changedTransformationClass, transformationIndex, "prepareForInlined", copy)
      nxPrepTypeTree = indexUpdate(prev.nxPrepTypeTree, changedTransformationClass, transformationIndex, "prepareForTypeTree", copy)
      nxPrepBind = indexUpdate(prev.nxPrepBind, changedTransformationClass, transformationIndex, "prepareForBind", copy)
      nxPrepAlternative = indexUpdate(prev.nxPrepAlternative, changedTransformationClass, transformationIndex, "prepareForAlternative", copy)
      nxPrepUnApply = indexUpdate(prev.nxPrepUnApply, changedTransformationClass, transformationIndex, "prepareForUnApply", copy)
      nxPrepValDef = indexUpdate(prev.nxPrepValDef, changedTransformationClass, transformationIndex, "prepareForValDef", copy)
      nxPrepDefDef = indexUpdate(prev.nxPrepDefDef, changedTransformationClass, transformationIndex, "prepareForDefDef", copy)
      nxPrepTypeDef = indexUpdate(prev.nxPrepTypeDef, changedTransformationClass, transformationIndex, "prepareForTypeDef", copy)
      nxPrepTemplate = indexUpdate(prev.nxPrepTemplate, changedTransformationClass, transformationIndex, "prepareForTemplate", copy)
      nxPrepPackageDef = indexUpdate(prev.nxPrepPackageDef, changedTransformationClass, transformationIndex, "prepareForPackageDef", copy)
      nxPrepStats = indexUpdate(prev.nxPrepStats, changedTransformationClass, transformationIndex, "prepareForStats", copy)

      nxTransIdent = indexUpdate(prev.nxTransIdent, changedTransformationClass, transformationIndex, "transformIdent", copy)
      nxTransSelect = indexUpdate(prev.nxTransSelect, changedTransformationClass, transformationIndex, "transformSelect", copy)
      nxTransThis = indexUpdate(prev.nxTransThis, changedTransformationClass, transformationIndex, "transformThis", copy)
      nxTransSuper = indexUpdate(prev.nxTransSuper, changedTransformationClass, transformationIndex, "transformSuper", copy)
      nxTransApply = indexUpdate(prev.nxTransApply, changedTransformationClass, transformationIndex, "transformApply", copy)
      nxTransTypeApply = indexUpdate(prev.nxTransTypeApply, changedTransformationClass, transformationIndex, "transformTypeApply", copy)
      nxTransLiteral = indexUpdate(prev.nxTransLiteral, changedTransformationClass, transformationIndex, "transformLiteral", copy)
      nxTransNew = indexUpdate(prev.nxTransNew, changedTransformationClass, transformationIndex, "transformNew", copy)
      nxTransTyped = indexUpdate(prev.nxTransTyped, changedTransformationClass, transformationIndex, "transformTyped", copy)
      nxTransAssign = indexUpdate(prev.nxTransAssign, changedTransformationClass, transformationIndex, "transformAssign", copy)
      nxTransBlock = indexUpdate(prev.nxTransBlock, changedTransformationClass, transformationIndex, "transformBlock", copy)
      nxTransIf = indexUpdate(prev.nxTransIf, changedTransformationClass, transformationIndex, "transformIf", copy)
      nxTransClosure = indexUpdate(prev.nxTransClosure, changedTransformationClass, transformationIndex, "transformClosure", copy)
      nxTransMatch = indexUpdate(prev.nxTransMatch, changedTransformationClass, transformationIndex, "transformMatch", copy)
      nxTransCaseDef = indexUpdate(prev.nxTransCaseDef, changedTransformationClass, transformationIndex, "transformCaseDef", copy)
      nxTransReturn = indexUpdate(prev.nxTransReturn, changedTransformationClass, transformationIndex, "transformReturn", copy)
      nxTransTry = indexUpdate(prev.nxTransTry, changedTransformationClass, transformationIndex, "transformTry", copy)
      nxTransSeqLiteral = indexUpdate(prev.nxTransSeqLiteral, changedTransformationClass, transformationIndex, "transformSeqLiteral", copy)
      nxTransInlined = indexUpdate(prev.nxTransInlined, changedTransformationClass, transformationIndex, "transformInlined", copy)
      nxTransTypeTree = indexUpdate(prev.nxTransTypeTree, changedTransformationClass, transformationIndex, "transformTypeTree", copy)
      nxTransBind = indexUpdate(prev.nxTransBind, changedTransformationClass, transformationIndex, "transformBind", copy)
      nxTransAlternative = indexUpdate(prev.nxTransAlternative, changedTransformationClass, transformationIndex, "transformAlternative", copy)
      nxTransUnApply = indexUpdate(prev.nxTransUnApply, changedTransformationClass, transformationIndex, "transformUnApply", copy)
      nxTransValDef = indexUpdate(prev.nxTransValDef, changedTransformationClass, transformationIndex, "transformValDef", copy)
      nxTransDefDef = indexUpdate(prev.nxTransDefDef, changedTransformationClass, transformationIndex, "transformDefDef", copy)
      nxTransTypeDef = indexUpdate(prev.nxTransTypeDef, changedTransformationClass, transformationIndex, "transformTypeDef", copy)
      nxTransTemplate = indexUpdate(prev.nxTransTemplate, changedTransformationClass, transformationIndex, "transformTemplate", copy)
      nxTransPackageDef = indexUpdate(prev.nxTransPackageDef, changedTransformationClass, transformationIndex, "transformPackageDef", copy)
      nxTransStats = indexUpdate(prev.nxTransStats, changedTransformationClass, transformationIndex, "transformStats", copy)
      nxTransOther = indexUpdate(prev.nxTransOther, changedTransformationClass, transformationIndex, "transformOther", copy)
    }

    /** Those arrays are used as "execution plan" in order to only execute non-trivial transformations\preparations
     *  for every integer i array(i) contains first non trivial transformation\preparation on particular tree subtype.
     *  If no nontrivial transformation are left stored value is greater than  transformers.size
     */
    var nxPrepIdent: Array[Int] = _
    var nxPrepSelect: Array[Int] = _
    var nxPrepThis: Array[Int] = _
    var nxPrepSuper: Array[Int] = _
    var nxPrepApply: Array[Int] = _
    var nxPrepTypeApply: Array[Int] = _
    var nxPrepLiteral: Array[Int] = _
    var nxPrepNew: Array[Int] = _
    var nxPrepTyped: Array[Int] = _
    var nxPrepAssign: Array[Int] = _
    var nxPrepBlock: Array[Int] = _
    var nxPrepIf: Array[Int] = _
    var nxPrepClosure: Array[Int] = _
    var nxPrepMatch: Array[Int] = _
    var nxPrepCaseDef: Array[Int] = _
    var nxPrepReturn: Array[Int] = _
    var nxPrepTry: Array[Int] = _
    var nxPrepSeqLiteral: Array[Int] = _
    var nxPrepInlined: Array[Int] = _
    var nxPrepTypeTree: Array[Int] = _
    var nxPrepBind: Array[Int] = _
    var nxPrepAlternative: Array[Int] = _
    var nxPrepUnApply: Array[Int] = _
    var nxPrepValDef: Array[Int] = _
    var nxPrepDefDef: Array[Int] = _
    var nxPrepTypeDef: Array[Int] = _
    var nxPrepTemplate: Array[Int] = _
    var nxPrepPackageDef: Array[Int] = _
    var nxPrepStats: Array[Int] = _
    var nxPrepUnit: Array[Int] = _

    var nxTransIdent: Array[Int] = _
    var nxTransSelect: Array[Int] = _
    var nxTransThis: Array[Int] = _
    var nxTransSuper: Array[Int] = _
    var nxTransApply: Array[Int] = _
    var nxTransTypeApply: Array[Int] = _
    var nxTransLiteral: Array[Int] = _
    var nxTransNew: Array[Int] = _
    var nxTransTyped: Array[Int] = _
    var nxTransAssign: Array[Int] = _
    var nxTransBlock: Array[Int] = _
    var nxTransIf: Array[Int] = _
    var nxTransClosure: Array[Int] = _
    var nxTransMatch: Array[Int] = _
    var nxTransCaseDef: Array[Int] = _
    var nxTransReturn: Array[Int] = _
    var nxTransTry: Array[Int] = _
    var nxTransSeqLiteral: Array[Int] = _
    var nxTransInlined: Array[Int] = _
    var nxTransTypeTree: Array[Int] = _
    var nxTransBind: Array[Int] = _
    var nxTransAlternative: Array[Int] = _
    var nxTransUnApply: Array[Int] = _
    var nxTransValDef: Array[Int] = _
    var nxTransDefDef: Array[Int] = _
    var nxTransTypeDef: Array[Int] = _
    var nxTransTemplate: Array[Int] = _
    var nxTransPackageDef: Array[Int] = _
    var nxTransStats: Array[Int] = _
    var nxTransUnit: Array[Int] = _
    var nxTransOther: Array[Int] = _
  }

  /** A group of tree transforms that are applied in sequence during the same phase */
  abstract class TreeTransformer extends Phase {

    def miniPhases: Array[MiniPhase]

    override def run(implicit ctx: Context): Unit = {
      val curTree = ctx.compilationUnit.tpdTree
      val newTree = macroTransform(curTree)
      ctx.compilationUnit.tpdTree = newTree
    }

    def mutateTransformers[T](info: TransformerInfo, mutator: Mutator[T], mutationPlan: Array[Int], tree: T, cur: Int)(implicit ctx: Context) = {
      var transformersCopied = false
      var nxCopied = false
      var result = info.transformers
      var resultNX = info.nx
      var i = mutationPlan(cur)
        // @DarkDimius You commented on the previous version
        //
        //     var i = mutationPlan(0) // if TreeTransform.transform() method didn't exist we could have used mutationPlan(cur)
        //
        // But we need to use `cur` or otherwise we call prepare actions preceding the
        // phase that issued a transformFollowing. This can lead to "denotation not defined
        // here" errors. Note that tests still pass with the current modified code.
      val l = result.length
      var allDone = i < l
      while (i < l) {
        val oldTransform = result(i)
        val newTransform = mutator(oldTransform, tree, ctx.withPhase(oldTransform.treeTransformPhase))
        allDone = allDone && (newTransform eq NoTransform)
        if (!(oldTransform eq newTransform)) {
          if (!transformersCopied) result = result.clone()
          transformersCopied = true
          result(i) = newTransform
          if (!(newTransform.getClass == oldTransform.getClass)) {
            resultNX = new NXTransformations(resultNX, newTransform, i, nxCopied)
            nxCopied = true
          }
        }
        i = mutationPlan(i + 1)
      }
      if (allDone) null
      else if (!transformersCopied) info
      else new TransformerInfo(result, resultNX, info.group)
    }

    val prepForIdent: Mutator[Ident] = (trans, tree, ctx) => trans.prepareForIdent(tree)(ctx)
    val prepForSelect: Mutator[Select] = (trans, tree, ctx) => trans.prepareForSelect(tree)(ctx)
    val prepForThis: Mutator[This] = (trans, tree, ctx) => trans.prepareForThis(tree)(ctx)
    val prepForSuper: Mutator[Super] = (trans, tree, ctx) => trans.prepareForSuper(tree)(ctx)
    val prepForApply: Mutator[Apply] = (trans, tree, ctx) => trans.prepareForApply(tree)(ctx)
    val prepForTypeApply: Mutator[TypeApply] = (trans, tree, ctx) => trans.prepareForTypeApply(tree)(ctx)
    val prepForNew: Mutator[New] = (trans, tree, ctx) => trans.prepareForNew(tree)(ctx)
    val prepForTyped: Mutator[Typed] = (trans, tree, ctx) => trans.prepareForTyped(tree)(ctx)
    val prepForAssign: Mutator[Assign] = (trans, tree, ctx) => trans.prepareForAssign(tree)(ctx)
    val prepForLiteral: Mutator[Literal] = (trans, tree, ctx) => trans.prepareForLiteral(tree)(ctx)
    val prepForBlock: Mutator[Block] = (trans, tree, ctx) => trans.prepareForBlock(tree)(ctx)
    val prepForIf: Mutator[If] = (trans, tree, ctx) => trans.prepareForIf(tree)(ctx)
    val prepForClosure: Mutator[Closure] = (trans, tree, ctx) => trans.prepareForClosure(tree)(ctx)
    val prepForMatch: Mutator[Match] = (trans, tree, ctx) => trans.prepareForMatch(tree)(ctx)
    val prepForCaseDef: Mutator[CaseDef] = (trans, tree, ctx) => trans.prepareForCaseDef(tree)(ctx)
    val prepForReturn: Mutator[Return] = (trans, tree, ctx) => trans.prepareForReturn(tree)(ctx)
    val prepForTry: Mutator[Try] = (trans, tree, ctx) => trans.prepareForTry(tree)(ctx)
    val prepForSeqLiteral: Mutator[SeqLiteral] = (trans, tree, ctx) => trans.prepareForSeqLiteral(tree)(ctx)
    val prepForInlined: Mutator[Inlined] = (trans, tree, ctx) => trans.prepareForInlined(tree)(ctx)
    val prepForTypeTree: Mutator[TypeTree] = (trans, tree, ctx) => trans.prepareForTypeTree(tree)(ctx)
    val prepForBind: Mutator[Bind] = (trans, tree, ctx) => trans.prepareForBind(tree)(ctx)
    val prepForAlternative: Mutator[Alternative] = (trans, tree, ctx) => trans.prepareForAlternative(tree)(ctx)
    val prepForUnApply: Mutator[UnApply] = (trans, tree, ctx) => trans.prepareForUnApply(tree)(ctx)
    val prepForValDef: Mutator[ValDef] = (trans, tree, ctx) => trans.prepareForValDef(tree)(ctx)
    val prepForDefDef: Mutator[DefDef] = (trans, tree, ctx) => trans.prepareForDefDef(tree)(ctx)
    val prepForTypeDef: Mutator[TypeDef] = (trans, tree, ctx) => trans.prepareForTypeDef(tree)(ctx)
    val prepForTemplate: Mutator[Template] = (trans, tree, ctx) => trans.prepareForTemplate(tree)(ctx)
    val prepForPackageDef: Mutator[PackageDef] = (trans, tree, ctx) => trans.prepareForPackageDef(tree)(ctx)
    val prepForStats: Mutator[List[Tree]] = (trans, trees, ctx) => trans.prepareForStats(trees)(ctx)
    val prepForUnit: Mutator[Tree] = (trans, tree, ctx) => trans.prepareForUnit(tree)(ctx)

    val initialTransformationsCache = miniPhases.zipWithIndex.map {
      case (miniPhase, id) =>
        miniPhase.idx = id
        miniPhase.treeTransform
    }

    val initialInfoCache = new TransformerInfo(initialTransformationsCache, new NXTransformations(initialTransformationsCache), this)

    def macroTransform(t: Tree)(implicit ctx: Context): Tree = {
      val info = initialInfoCache
      implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForUnit, info.nx.nxPrepUnit, t, 0)
      if (mutatedInfo eq null) t
      else goUnit(transform(t, mutatedInfo, 0), mutatedInfo.nx.nxTransUnit(0))
    }

    @tailrec
    final private[TreeTransforms] def goIdent(tree: Ident, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformIdent(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Ident => goIdent(t, info.nx.nxTransIdent(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goSelect(tree: Select, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformSelect(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Select => goSelect(t, info.nx.nxTransSelect(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goThis(tree: This, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformThis(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: This => goThis(t, info.nx.nxTransThis(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goSuper(tree: Super, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformSuper(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Super => goSuper(t, info.nx.nxTransSuper(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goApply(tree: Apply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Apply => goApply(t, info.nx.nxTransApply(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goTypeApply(tree: TypeApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformTypeApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: TypeApply => goTypeApply(t, info.nx.nxTransTypeApply(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goNew(tree: New, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformNew(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: New => goNew(t, info.nx.nxTransNew(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goTyped(tree: Typed, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformTyped(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Typed => goTyped(t, info.nx.nxTransTyped(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goAssign(tree: Assign, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformAssign(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Assign => goAssign(t, info.nx.nxTransAssign(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goLiteral(tree: Literal, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformLiteral(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Literal => goLiteral(t, info.nx.nxTransLiteral(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goBlock(tree: Block, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformBlock(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Block => goBlock(t, info.nx.nxTransBlock(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goIf(tree: If, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformIf(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: If => goIf(t, info.nx.nxTransIf(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goClosure(tree: Closure, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformClosure(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Closure => goClosure(t, info.nx.nxTransClosure(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goMatch(tree: Match, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformMatch(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Match => goMatch(t, info.nx.nxTransMatch(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goCaseDef(tree: CaseDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformCaseDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: CaseDef => goCaseDef(t, info.nx.nxTransCaseDef(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goReturn(tree: Return, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformReturn(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Return => goReturn(t, info.nx.nxTransReturn(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goTry(tree: Try, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformTry(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Try => goTry(t, info.nx.nxTransTry(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goSeqLiteral(tree: SeqLiteral, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformSeqLiteral(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: SeqLiteral => goSeqLiteral(t, info.nx.nxTransSeqLiteral(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goInlined(tree: Inlined, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformInlined(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Inlined => goInlined(t, info.nx.nxTransInlined(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goTypeTree(tree: TypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformTypeTree(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: TypeTree => goTypeTree(t, info.nx.nxTransTypeTree(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goBind(tree: Bind, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformBind(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Bind => goBind(t, info.nx.nxTransBind(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goAlternative(tree: Alternative, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformAlternative(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Alternative => goAlternative(t, info.nx.nxTransAlternative(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goValDef(tree: ValDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformValDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: ValDef => goValDef(t, info.nx.nxTransValDef(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goDefDef(tree: DefDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformDefDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: DefDef => goDefDef(t, info.nx.nxTransDefDef(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goUnApply(tree: UnApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformUnApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: UnApply => goUnApply(t, info.nx.nxTransUnApply(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goTypeDef(tree: TypeDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformTypeDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: TypeDef => goTypeDef(t, info.nx.nxTransTypeDef(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goTemplate(tree: Template, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformTemplate(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: Template => goTemplate(t, info.nx.nxTransTemplate(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goPackageDef(tree: PackageDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        trans.transformPackageDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match {
          case t: PackageDef => goPackageDef(t, info.nx.nxTransPackageDef(cur + 1))
          case t => transformSingle(t, cur + 1)
        }
      } else tree
    }

    @tailrec
    final private[TreeTransforms] def goUnit(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        val t = trans.transformUnit(tree)(ctx.withPhase(trans.treeTransformPhase), info)
        goUnit(t, info.nx.nxTransUnit(cur + 1))
      } else tree
    }

    final private[TreeTransforms] def goOther(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        val t = trans.transformOther(tree)(ctx.withPhase(trans.treeTransformPhase), info)
        transformSingle(t, cur + 1)
      } else tree
    }

    final private[TreeTransforms] def goNamed(tree: NameTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree =
      tree match {
        case tree: Ident => goIdent(tree, info.nx.nxTransIdent(cur))
        case tree: Select => goSelect(tree, info.nx.nxTransSelect(cur))
        case tree: Bind => goBind(tree, cur)
        case tree: ValDef if !tree.isEmpty => goValDef(tree, info.nx.nxTransValDef(cur))
        case tree: DefDef => goDefDef(tree, info.nx.nxTransDefDef(cur))
        case tree: TypeDef => goTypeDef(tree, info.nx.nxTransTypeDef(cur))
        case _ => tree
      }

    final private[TreeTransforms] def goUnnamed(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree =
      tree match {
        case tree: This => goThis(tree, info.nx.nxTransThis(cur))
        case tree: Super => goSuper(tree, info.nx.nxTransSuper(cur))
        case tree: Apply => goApply(tree, info.nx.nxTransApply(cur))
        case tree: TypeApply => goTypeApply(tree, info.nx.nxTransTypeApply(cur))
        case tree: Literal => goLiteral(tree, info.nx.nxTransLiteral(cur))
        case tree: New => goNew(tree, info.nx.nxTransNew(cur))
        case tree: Typed => goTyped(tree, info.nx.nxTransTyped(cur))
        case tree: Assign => goAssign(tree, info.nx.nxTransAssign(cur))
        case tree: Block => goBlock(tree, info.nx.nxTransBlock(cur))
        case tree: If => goIf(tree, info.nx.nxTransIf(cur))
        case tree: Closure => goClosure(tree, info.nx.nxTransClosure(cur))
        case tree: Match => goMatch(tree, info.nx.nxTransMatch(cur))
        case tree: CaseDef => goCaseDef(tree, info.nx.nxTransCaseDef(cur))
        case tree: Return => goReturn(tree, info.nx.nxTransReturn(cur))
        case tree: Try => goTry(tree, info.nx.nxTransTry(cur))
        case tree: SeqLiteral => goSeqLiteral(tree, info.nx.nxTransSeqLiteral(cur))
        case tree: Inlined => goInlined(tree, info.nx.nxTransInlined(cur))
        case tree: TypeTree => goTypeTree(tree, info.nx.nxTransTypeTree(cur))
        case tree: Alternative => goAlternative(tree, info.nx.nxTransAlternative(cur))
        case tree: UnApply => goUnApply(tree, info.nx.nxTransUnApply(cur))
        case tree: Template => goTemplate(tree, info.nx.nxTransTemplate(cur))
        case tree: PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur))
        case Thicket(trees) => tree
        case tree => goOther(tree, info.nx.nxTransOther(cur))
      }

    final private[TreeTransforms] def transformSingle(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree =
      if (cur < info.transformers.length) {
        tree match {
          // split one big match into 2 smaller ones
          case tree: NameTree => goNamed(tree, cur)
          case tree => goUnnamed(tree, cur)
        }
      } else tree

    // TODO merge with localCtx in MacroTransform
    // Generally: If we will keep MacroTransform, merge common behavior with TreeTransform
    def localContext(sym: Symbol)(implicit ctx: Context) = {
      val owner = if (sym is PackageVal) sym.moduleClass else sym
      ctx.fresh.setOwner(owner)
    }

    final private[TreeTransforms] def transformNamed(tree: NameTree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree =
      tree match {
        case tree: Ident =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForIdent, info.nx.nxPrepIdent, tree, cur)
            // Dotty deviation: implicits need explicit type
          if (mutatedInfo eq null) tree
          else goIdent(tree, mutatedInfo.nx.nxTransIdent(cur))
        case tree: Select =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSelect, info.nx.nxPrepSelect, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val qual = transform(tree.qualifier, mutatedInfo, cur)
            goSelect(cpy.Select(tree)(qual, tree.name), mutatedInfo.nx.nxTransSelect(cur))
          }
        case tree: Bind =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBind, info.nx.nxPrepBind, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val body = transform(tree.body, mutatedInfo, cur)
            goBind(cpy.Bind(tree)(tree.name, body), mutatedInfo.nx.nxTransBind(cur))
          }
        case tree: ValDef if !tree.isEmpty => // As a result of discussing with Martin: emptyValDefs shouldn't be copied // NAME
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForValDef, info.nx.nxPrepValDef, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val nestedCtx = if (tree.symbol.exists) localContext(tree.symbol) else ctx
            val tpt = transform(tree.tpt, mutatedInfo, cur)(nestedCtx)
            val rhs = transform(tree.rhs, mutatedInfo, cur)(nestedCtx)
            goValDef(cpy.ValDef(tree)(tree.name, tpt, rhs), mutatedInfo.nx.nxTransValDef(cur))
          }
        case tree: DefDef =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForDefDef, info.nx.nxPrepDefDef, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val nestedCtx = localContext(tree.symbol)
            val tparams = transformSubTrees(tree.tparams, mutatedInfo, cur)(nestedCtx)
            val vparams = tree.vparamss.mapConserve(x => transformSubTrees(x, mutatedInfo, cur)(nestedCtx))
            val tpt = transform(tree.tpt, mutatedInfo, cur)(nestedCtx)
            val rhs = transform(tree.rhs, mutatedInfo, cur)(nestedCtx)
            goDefDef(cpy.DefDef(tree)(tree.name, tparams, vparams, tpt, rhs), mutatedInfo.nx.nxTransDefDef(cur))
          }
        case tree: TypeDef =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeDef, info.nx.nxPrepTypeDef, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val rhs = transform(tree.rhs, mutatedInfo, cur)(localContext(tree.symbol))
            goTypeDef(cpy.TypeDef(tree)(tree.name, rhs), mutatedInfo.nx.nxTransTypeDef(cur))
          }
        case _ =>
          tree
      }

    final private[TreeTransforms] def transformUnnamed(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree =
      tree match {
        case tree: This =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForThis, info.nx.nxPrepThis, tree, cur)
          if (mutatedInfo eq null) tree
          else goThis(tree, mutatedInfo.nx.nxTransThis(cur))
        case tree: Super =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSuper, info.nx.nxPrepSuper, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val qual = transform(tree.qual, mutatedInfo, cur)
            goSuper(cpy.Super(tree)(qual, tree.mix), mutatedInfo.nx.nxTransSuper(cur))
          }
        case tree: Apply =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForApply, info.nx.nxPrepApply, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val fun = transform(tree.fun, mutatedInfo, cur)
            val args = transformSubTrees(tree.args, mutatedInfo, cur)
            goApply(cpy.Apply(tree)(fun, args), mutatedInfo.nx.nxTransApply(cur))
          }
        case tree: TypeApply =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeApply, info.nx.nxPrepTypeApply, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val fun = transform(tree.fun, mutatedInfo, cur)
            val args = transformTrees(tree.args, mutatedInfo, cur)
            goTypeApply(cpy.TypeApply(tree)(fun, args), mutatedInfo.nx.nxTransTypeApply(cur))
          }
        case tree: Literal =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForLiteral, info.nx.nxPrepLiteral, tree, cur)
          if (mutatedInfo eq null) tree
          else goLiteral(tree, mutatedInfo.nx.nxTransLiteral(cur))
        case tree: New =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForNew, info.nx.nxPrepNew, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val tpt = transform(tree.tpt, mutatedInfo, cur)
            goNew(cpy.New(tree)(tpt), mutatedInfo.nx.nxTransNew(cur))
          }
        case tree: Typed =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTyped, info.nx.nxPrepTyped, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val expr = transform(tree.expr, mutatedInfo, cur)
            val tpt = transform(tree.tpt, mutatedInfo, cur)
            goTyped(cpy.Typed(tree)(expr, tpt), mutatedInfo.nx.nxTransTyped(cur))
          }
        case tree: Assign =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAssign, info.nx.nxPrepAssign, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val lhs = transform(tree.lhs, mutatedInfo, cur)
            val rhs = transform(tree.rhs, mutatedInfo, cur)
            goAssign(cpy.Assign(tree)(lhs, rhs), mutatedInfo.nx.nxTransAssign(cur))
          }
        case tree: Block =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBlock, info.nx.nxPrepBlock, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val stats = transformStats(tree.stats, ctx.owner, mutatedInfo, cur)
            val expr = transform(tree.expr, mutatedInfo, cur)
            goBlock(cpy.Block(tree)(stats, expr), mutatedInfo.nx.nxTransBlock(cur))
          }
        case tree: If =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForIf, info.nx.nxPrepIf, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val cond = transform(tree.cond, mutatedInfo, cur)
            val thenp = transform(tree.thenp, mutatedInfo, cur)
            val elsep = transform(tree.elsep, mutatedInfo, cur)
            goIf(cpy.If(tree)(cond, thenp, elsep), mutatedInfo.nx.nxTransIf(cur))
          }
        case tree: Closure =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForClosure, info.nx.nxPrepClosure, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val env = transformTrees(tree.env, mutatedInfo, cur)
            val meth = transform(tree.meth, mutatedInfo, cur)
            val tpt = transform(tree.tpt, mutatedInfo, cur)
            goClosure(cpy.Closure(tree)(env, meth, tpt), mutatedInfo.nx.nxTransClosure(cur))
          }
        case tree: Match =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForMatch, info.nx.nxPrepMatch, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val selector = transform(tree.selector, mutatedInfo, cur)
            val cases = transformSubTrees(tree.cases, mutatedInfo, cur)
            goMatch(cpy.Match(tree)(selector, cases), mutatedInfo.nx.nxTransMatch(cur))
          }
        case tree: CaseDef =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val pat = transform(tree.pat, mutatedInfo, cur)(ctx.addMode(Mode.Pattern))
            val guard = transform(tree.guard, mutatedInfo, cur)
            val body = transform(tree.body, mutatedInfo, cur)
            goCaseDef(cpy.CaseDef(tree)(pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur))
          }
        case tree: Return =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForReturn, info.nx.nxPrepReturn, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val expr = transform(tree.expr, mutatedInfo, cur)
            val from = tree.from
              // don't transform the `from` part, as this is not a normal ident, but
              // a pointer to the enclosing method. Transforming this as a normal ident
              // can go wrong easily. If a transformation is needed, it should be
              // the responsibility of the transformReturn method to handle this also.
            goReturn(cpy.Return(tree)(expr, from), mutatedInfo.nx.nxTransReturn(cur))
          }
        case tree: Try =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTry, info.nx.nxPrepTry, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val block = transform(tree.expr, mutatedInfo, cur)
            val cases1 = tree.cases.mapConserve(transform(_, mutatedInfo, cur)).asInstanceOf[List[CaseDef]]
            val finalizer = transform(tree.finalizer, mutatedInfo, cur)
            goTry(cpy.Try(tree)(block, cases1, finalizer), mutatedInfo.nx.nxTransTry(cur))
          }
        case tree: SeqLiteral =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSeqLiteral, info.nx.nxPrepSeqLiteral, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val elems = transformTrees(tree.elems, mutatedInfo, cur)
            val elemtpt = transform(tree.elemtpt, mutatedInfo, cur)
            goSeqLiteral(cpy.SeqLiteral(tree)(elems, elemtpt), mutatedInfo.nx.nxTransSeqLiteral(cur))
          }
        case tree: Inlined =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForInlined, info.nx.nxPrepInlined, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val bindings = transformSubTrees(tree.bindings, mutatedInfo, cur)
            val expansion = transform(tree.expansion, mutatedInfo, cur)(inlineContext(tree))
            goInlined(cpy.Inlined(tree)(tree.call, bindings, expansion), mutatedInfo.nx.nxTransInlined(cur))
          }
        case tree: TypeTree =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur)
          if (mutatedInfo eq null) tree
          else goTypeTree(tree, mutatedInfo.nx.nxTransTypeTree(cur))
        case tree: Alternative =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAlternative, info.nx.nxPrepAlternative, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val trees = transformTrees(tree.trees, mutatedInfo, cur)
            goAlternative(cpy.Alternative(tree)(trees), mutatedInfo.nx.nxTransAlternative(cur))
          }
        case tree: UnApply =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForUnApply, info.nx.nxPrepUnApply, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val fun = transform(tree.fun, mutatedInfo, cur)
            val implicits = transformTrees(tree.implicits, mutatedInfo, cur)
            val patterns = transformTrees(tree.patterns, mutatedInfo, cur)
            goUnApply(cpy.UnApply(tree)(fun, implicits, patterns), mutatedInfo.nx.nxTransUnApply(cur))
          }
        case tree: Template =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTemplate, info.nx.nxPrepTemplate, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val constr = transformSub(tree.constr, mutatedInfo, cur)
            val parents = transformTrees(tree.parents, mutatedInfo, cur)(ctx.superCallContext)
            val self = transformSub(tree.self, mutatedInfo, cur)
            val body = transformStats(tree.body, tree.symbol, mutatedInfo, cur)
            goTemplate(cpy.Template(tree)(constr, parents, self, body), mutatedInfo.nx.nxTransTemplate(cur))
          }
        case tree: PackageDef =>
          implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForPackageDef, info.nx.nxPrepPackageDef, tree, cur)
          if (mutatedInfo eq null) tree
          else {
            val nestedCtx = localContext(tree.symbol)
            val pid = transformSub(tree.pid, mutatedInfo, cur)
            val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx)
            goPackageDef(cpy.PackageDef(tree)(pid, stats), mutatedInfo.nx.nxTransPackageDef(cur))
          }
        case Thicket(trees) =>
          cpy.Thicket(tree)(transformTrees(trees, info, cur))
        case tree =>
          implicit val originalInfo: TransformerInfo = info
          goOther(tree, info.nx.nxTransOther(cur))
      }

    private var crashingTree: Tree = EmptyTree

    def transform(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show} at ${ctx.phase}", transforms, show = true) {
      try
        if (cur < info.transformers.length) {
          // if cur > 0 then some of the symbols can be created by already performed transformations
          // this means that their denotations could not exists in previous period
          val pctx = ctx.withPhase(info.transformers(cur).treeTransformPhase)
          tree match {
            //split one big match into 2 smaller ones
            case tree: NameTree => transformNamed(tree, info, cur)(pctx)
            case tree => transformUnnamed(tree, info, cur)(pctx)
          }
        } else tree
      catch {
        case NonFatal(ex) =>
          if (tree ne crashingTree) {
            crashingTree = tree
            println(i"exception while transforming $tree of class ${tree.getClass} # ${tree.uniqueId}")
          }
          throw ex
      }
    }

    @tailrec
    final private[TreeTransforms] def goStats(trees: List[Tree], cur: Int)(implicit ctx: Context, info: TransformerInfo): List[Tree] = {
      if (cur < info.transformers.length) {
        val trans = info.transformers(cur)
        val stats = trans.transformStats(trees)(ctx.withPhase(trans.treeTransformPhase), info)
        goStats(stats, info.nx.nxTransStats(cur + 1))
      } else trees
    }

    def transformStats(trees: List[Tree], exprOwner: Symbol, info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tree] = {
      val newInfo = mutateTransformers(info, prepForStats, info.nx.nxPrepStats, trees, current)
      def transformStat(stat: Tree): Tree = stat match {
        case _: Import | _: DefTree => transform(stat, newInfo, current)
        case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat)
        case _ => transform(stat, newInfo, current)(ctx.exprContext(stat, exprOwner))
      }
      val newTrees = flatten(trees.mapconserve(transformStat))
      goStats(newTrees, newInfo.nx.nxTransStats(current))(ctx, newInfo)
    }

    def transformTrees(trees: List[Tree], info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tree] =
      flatten(trees mapConserve (x => transform(x, info, current)))

    def transformSub[Tr <: Tree](tree: Tr, info: TransformerInfo, current: Int)(implicit ctx: Context): Tr =
      transform(tree, info, current).asInstanceOf[Tr]

    def transformSubTrees[Tr <: Tree](trees: List[Tr], info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tr] =
      transformTrees(trees, info, current)(ctx).asInstanceOf[List[Tr]]
  }
}