aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/Phases.scala
blob: 44608296a2b1d2b8a01666967a565bfdfb4756cb (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                        

                 
                                                    
                                                
                    

                          
                   
                             
                                                         
                                                                                            
                                   
                
                                  
              


                  
 

                 
                                                     

                                
                                                 



                                                                 
 
                                    


                                                     

                                                                   


                                                                         
                                                      

 

               



                                
                                                                                  
 
                                  
                                 
                                  
                                                               
                                                                                                              
     
 
                                    
                                    
                                                               
     
 

                                                  
                                





                                                                                     


                                                                            


                                                                                                       
        
                                                
                                                                                                                                                                   
                                              
                                                        
                                                 


                                                      


                                                                                                    

        
               
 


                                                                           
                          


                                                                                        









                                                                                                                            
                                               

                                                                                                                    
               
                                                               

                                                             
                                                 

                                          
             
                                      
                                                                                  

                                         
                                     
           
         
 

              
                           
     
 

                                                             
                                                                                          
       




                                                                   
                                                              



                                                                                  
                                                        

                                                                    







                                                  


                                                                                                                    








                                    




                                                                




                                         

              



                                                                


                               

                             





                                                     
       

                   
                                                          



                                         
                                                
                                                                                
     
 
                                                                                          
 


                                                                           



                                                                
                                                         
                                  
                   
                                                              

               
                          

     
                                                              

                                                               
                                                                   
                                                                         
                                                                                 
                                                               
                                                                                       
                                                                     
                                                                     
                                                               
                                                                           
                                                               
                                                                 

                                     
                                         
                                             
                                                   
                                                           
                                         
                                                                 
                                       
                                               
                                         
                                                     
                                         
                                           

                                                                      

   
                                
 
                                                                               





                                                                          
                         
 
                                                                 
                                                     
 

                                        

                                                                                           
                                                                             


                               
 
                                       
 





                                                                                      
 




                                                                      




                                                                          

                       
                              
 
                                                        



                                          
                                      

                                         

                                                                        

                                                                         
       




                                     
 



                                                                                                                       
                                                                                                                                      
 


                                                                                                              
                   
                                            



                                                                                  
                                                                                     
     
 

                                                                                     
                               

                             
                           
                                                                         
 
                           
                                                             
 
                                                                               
 
                        

                                                          
                                     
   
 



                                                                           






                                                                                                                                 






                                                                                          
 
package dotty.tools.dotc
package core

import Periods._
import Contexts._
import dotty.tools.backend.jvm.{LabelDefs, GenBCode}
import dotty.tools.dotc.core.Symbols.ClassSymbol
import util.DotClass
import DenotTransformers._
import Denotations._
import Decorators._
import config.Printers.config
import scala.collection.mutable.{ListBuffer, ArrayBuffer}
import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform}
import dotty.tools.dotc.transform._
import Periods._
import typer.{FrontEnd, RefChecks}
import ast.tpd

trait Phases {
  self: Context =>

  import Phases._

  def phase: Phase = base.phases(period.firstPhaseId)

  def phasesStack: List[Phase] =
    if ((this eq NoContext) || !phase.exists) Nil
    else {
      val rest = outersIterator.dropWhile(_.phase == phase)
      phase :: (if (rest.hasNext) rest.next.phasesStack else Nil)
    }

  /** Execute `op` at given phase */
  def atPhase[T](phase: Phase)(op: Context => T): T =
    atPhase(phase.id)(op)

  def atNextPhase[T](op: Context => T): T = atPhase(phase.next)(op)

  def atPhaseNotLaterThan[T](limit: Phase)(op: Context => T): T =
    if (!limit.exists || phase <= limit) op(this) else atPhase(limit)(op)

  def isAfterTyper: Boolean = base.isAfterTyper(phase)
}

object Phases {

  trait PhasesBase {
    this: ContextBase =>

    // drop NoPhase at beginning
    def allPhases = (if (squashedPhases.nonEmpty) squashedPhases else phases).tail

    object NoPhase extends Phase {
      override def exists = false
      def phaseName = "<no phase>"
      def run(implicit ctx: Context): Unit = unsupported("run")
      def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = unsupported("transform")
    }

    object SomePhase extends Phase {
      def phaseName = "<some phase>"
      def run(implicit ctx: Context): Unit = unsupported("run")
    }

    /** A sentinel transformer object */
    class TerminalPhase extends DenotTransformer {
      def phaseName = "terminal"
      def run(implicit ctx: Context): Unit = unsupported("run")
      def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation =
        unsupported("transform")
      override def lastPhaseId(implicit ctx: Context) = id
    }

    def phasePlan = this.phasesPlan
    def setPhasePlan(phasess: List[List[Phase]]) = this.phasesPlan = phasess

    /** Squash TreeTransform's beloning to same sublist to a single TreeTransformer
      * Each TreeTransform gets own period,
      * whereas a combined TreeTransformer gets period equal to union of periods of it's TreeTransforms
      */
    def squashPhases(phasess: List[List[Phase]],
                             phasesToSkip: List[String], stopBeforePhases: List[String], stopAfterPhases: List[String], YCheckAfter: List[String]): List[Phase] = {
      val squashedPhases = ListBuffer[Phase]()
      var prevPhases: Set[Class[_ <: Phase]] = Set.empty
      val YCheckAll = YCheckAfter.contains("all")

      var stop = false
      val filteredPhases = phasess.map(_.filter { p =>
        val pstop = stop
        stop = stop | stopBeforePhases.contains(p.phaseName) | stopAfterPhases.contains(p.phaseName)
        !(pstop || stopBeforePhases.contains(p.phaseName) || phasesToSkip.contains(p.phaseName))
      })

      var i = 0

      while (i < filteredPhases.length) {
        if (filteredPhases(i).nonEmpty) { //could be empty due to filtering
          val filteredPhaseBlock = filteredPhases(i)
          val phaseToAdd =
            if (filteredPhaseBlock.length > 1) {
              val phasesInBlock: Set[String] = filteredPhaseBlock.map(_.phaseName).toSet
              for (phase <- filteredPhaseBlock) {
                phase match {
                  case p: MiniPhase =>
                    val unmetRequirements = p.runsAfterGroupsOf &~ prevPhases
                    assert(unmetRequirements.isEmpty,
                      s"${phase.phaseName} requires ${unmetRequirements.mkString(", ")} to be in different TreeTransformer")

                  case _ =>
                    assert(false, s"Only tree transforms can be squashed, ${phase.phaseName} can not be squashed")
                }
              }
              val block = new TreeTransformer {
                override def phaseName: String = miniPhases.map(_.phaseName).mkString("TreeTransform:{", ", ", "}")
                override def miniPhases: Array[MiniPhase] = filteredPhaseBlock.asInstanceOf[List[MiniPhase]].toArray
              }
              prevPhases ++= filteredPhaseBlock.map(_.getClazz)
              block
            } else { // block of a single phase, no squashing
              val phase = filteredPhaseBlock.head
              prevPhases += phase.getClazz
              phase
            }
          squashedPhases += phaseToAdd
          val shouldAddYCheck = YCheckAfter.containsPhase(phaseToAdd) || YCheckAll
          if (shouldAddYCheck) {
            val checker = new TreeChecker
            squashedPhases += checker
          }
        }

        i += 1
      }
      squashedPhases.toList
    }

    /** Use the following phases in the order they are given.
     *  The list should never contain NoPhase.
     *  if squashing is enabled, phases in same subgroup will be squashed to single phase.
     */
    def usePhases(phasess: List[Phase], squash: Boolean = true) = {

      val flatPhases = collection.mutable.ListBuffer[Phase]()

      phasess.foreach(p => p match {
        case t: TreeTransformer => flatPhases ++= t.miniPhases
        case _ => flatPhases += p
      })

      phases = (NoPhase :: flatPhases.toList ::: new TerminalPhase :: Nil).toArray
      var phasesAfter:Set[Class[_ <: Phase]] = Set.empty
      nextDenotTransformerId = new Array[Int](phases.length)
      denotTransformers = new Array[DenotTransformer](phases.length)

      var phaseId = 0
      def nextPhaseId = {
        phaseId += 1
        phaseId // starting from 1 as NoPhase is 0
      }

      def checkRequirements(p: Phase) = {
        val unmetPrecedeRequirements = p.runsAfter -- phasesAfter
        assert(unmetPrecedeRequirements.isEmpty,
          s"phase ${p} has unmet requirement: ${unmetPrecedeRequirements.mkString(", ")} should precede this phase")
        phasesAfter += p.getClazz

      }
      var i = 0

      while (i < phasess.length) {
        val phase = phasess(i)
        phase match {
          case t: TreeTransformer =>
            val miniPhases = t.miniPhases
            miniPhases.foreach{ phase =>
              checkRequirements(phase)
              phase.init(this, nextPhaseId)}
            t.init(this, miniPhases.head.id, miniPhases.last.id)
          case _ =>
            phase.init(this, nextPhaseId)
            checkRequirements(phase)
        }

        i += 1
      }

      phases.last.init(this, nextPhaseId) // init terminal phase

      i = phases.length
      var lastTransformerId = i
      while (i > 0) {
        i -= 1
        val phase = phases(i)
        phase match {
          case transformer: DenotTransformer =>
            lastTransformerId = i
            denotTransformers(i) = transformer
          case _ =>
        }
        nextDenotTransformerId(i) = lastTransformerId
      }

      if (squash) {
        this.squashedPhases = (NoPhase :: phasess).toArray
      } else {
        this.squashedPhases = this.phases
      }

      config.println(s"Phases = ${phases.deep}")
      config.println(s"nextDenotTransformerId = ${nextDenotTransformerId.deep}")
    }

    def phaseOfClass(pclass: Class[_]) = phases.find(pclass.isInstance).getOrElse(NoPhase)

    private val cachedPhases = collection.mutable.Set[PhaseCache]()
    private def cleanPhaseCache = cachedPhases.foreach(_.myPhase = NoPhase)

    /** A cache to compute the phase with given name, which
     *  stores the phase as soon as phaseNamed returns something
     *  different from NoPhase.
     */
    private class PhaseCache(pclass: Class[_ <: Phase]) {
      var myPhase: Phase = NoPhase
      def phase = {
        if (myPhase eq NoPhase) myPhase = phaseOfClass(pclass)
        myPhase
      }
      cachedPhases += this
    }

    private val typerCache = new PhaseCache(classOf[FrontEnd])
    private val picklerCache = new PhaseCache(classOf[Pickler])

    private val refChecksCache = new PhaseCache(classOf[RefChecks])
    private val elimRepeatedCache = new PhaseCache(classOf[ElimRepeated])
    private val extensionMethodsCache = new PhaseCache(classOf[ExtensionMethods])
    private val erasureCache = new PhaseCache(classOf[Erasure])
    private val elimErasedValueTypeCache = new PhaseCache(classOf[ElimErasedValueType])
    private val patmatCache = new PhaseCache(classOf[PatternMatcher])
    private val lambdaLiftCache = new PhaseCache(classOf[LambdaLift])
    private val flattenCache = new PhaseCache(classOf[Flatten])
    private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter])
    private val gettersCache = new PhaseCache(classOf[Getters])
    private val genBCodeCache = new PhaseCache(classOf[GenBCode])

    def typerPhase = typerCache.phase
    def picklerPhase = picklerCache.phase
    def refchecksPhase = refChecksCache.phase
    def elimRepeatedPhase = elimRepeatedCache.phase
    def extensionMethodsPhase = extensionMethodsCache.phase
    def erasurePhase = erasureCache.phase
    def elimErasedValueTypePhase = elimErasedValueTypeCache.phase
    def patmatPhase = patmatCache.phase
    def lambdaLiftPhase = lambdaLiftCache.phase
    def flattenPhase = flattenCache.phase
    def explicitOuterPhase = explicitOuterCache.phase
    def gettersPhase = gettersCache.phase
    def genBCodePhase = genBCodeCache.phase

    def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id
  }

  trait Phase extends DotClass {

    /** A name given to the `Phase` that can be used to debug the compiler. For
     *  instance, it is possible to print trees after a given phase using:
     *
     *  ```bash
     *  $ ./bin/dotc -Xprint:<phaseNameHere> sourceFile.scala
     *  ```
     */
    def phaseName: String

    /** List of names of phases that should precede this phase */
    def runsAfter: Set[Class[_ <: Phase]] = Set.empty

    def run(implicit ctx: Context): Unit

    def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] =
      units.map { unit =>
        val unitCtx = ctx.fresh.setPhase(this.start).setCompilationUnit(unit)
        run(unitCtx)
        unitCtx.compilationUnit
      }

    def description: String = phaseName

    /** Output should be checkable by TreeChecker */
    def isCheckable: Boolean = true

    /** Check what the phase achieves, to be called at any point after it is finished.
     */
    def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = ()

    /** If set, allow missing or superfluous arguments in applications
     *  and type applications.
     */
    def relaxedTyping: Boolean = false

    /** Is this phase the standard typerphase? True for FrontEnd, but
     *  not for other first phases (such as FromTasty). The predicate
     *  is tested in some places that perform checks and corrections. It's
     *  different from isAfterTyper (and cheaper to test).
     */
    def isTyper = false

    def exists: Boolean = true

    private var myPeriod: Period = Periods.InvalidPeriod
    private var myBase: ContextBase = null
    private var myErasedTypes = false
    private var myFlatClasses = false
    private var myRefChecked = false
    private var mySymbolicRefs = false
    private var myLabelsReordered = false


    /** The sequence position of this phase in the given context where 0
     * is reserved for NoPhase and the first real phase is at position 1.
     * -1 if the phase is not installed in the context.
     */
    def id = myPeriod.firstPhaseId

    def period = myPeriod
    def start = myPeriod.firstPhaseId
    def end = myPeriod.lastPhaseId

    final def erasedTypes = myErasedTypes   // Phase is after erasure
    final def flatClasses = myFlatClasses   // Phase is after flatten
    final def refChecked = myRefChecked     // Phase is after RefChecks
    final def symbolicRefs = mySymbolicRefs // Phase is after ResolveSuper, newly generated TermRefs should be symbolic
    final def labelsReordered = myLabelsReordered // Phase is after LabelDefs, labels are flattened and owner chains don't mirror this

    protected[Phases] def init(base: ContextBase, start: Int, end:Int): Unit = {
      if (start >= FirstPhaseId)
        assert(myPeriod == Periods.InvalidPeriod, s"phase $this has already been used once; cannot be reused")
      myBase = base
      myPeriod = Period(NoRunId, start, end)
      myErasedTypes  = prev.getClass == classOf[Erasure]      || prev.erasedTypes
      myFlatClasses  = prev.getClass == classOf[Flatten]      || prev.flatClasses
      myRefChecked   = prev.getClass == classOf[RefChecks]    || prev.refChecked
      mySymbolicRefs = prev.getClass == classOf[ResolveSuper] || prev.symbolicRefs
      myLabelsReordered = prev.getClass == classOf[LabelDefs] || prev.labelsReordered
    }

    protected[Phases] def init(base: ContextBase, id: Int): Unit = init(base, id, id)

    final def <=(that: Phase) =
      exists && id <= that.id

    final def prev: Phase =
      if (id > FirstPhaseId) myBase.phases(start - 1) else myBase.NoPhase

    final def next: Phase =
      if (hasNext) myBase.phases(end + 1) else myBase.NoPhase

    final def hasNext = start >= FirstPhaseId && end + 1 < myBase.phases.length

    final def iterator =
      Iterator.iterate(this)(_.next) takeWhile (_.hasNext)

    override def toString = phaseName
  }

  trait NeedsCompanions {
    def isCompanionNeeded(cls: ClassSymbol)(implicit ctx: Context): Boolean
  }

  /** Replace all instances of `oldPhaseClass` in `current` phases
   *  by the result of `newPhases` applied to the old phase.
   */
  def replace(oldPhaseClass: Class[_ <: Phase], newPhases: Phase => List[Phase], current: List[List[Phase]]): List[List[Phase]] =
    current.map(_.flatMap(phase =>
      if (oldPhaseClass.isInstance(phase)) newPhases(phase) else phase :: Nil))

  /** Dotty deviation: getClass yields Class[_], instead of [Class <: <type of receiver>].
   *  We can get back the old behavior using this decorator. We should also use the same
   *  trick for standard getClass.
   */
  private implicit class getClassDeco[T](val x: T) extends AnyVal {
    def getClazz: Class[_ <: T] = x.getClass.asInstanceOf[Class[_ <: T]]
  }
}