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






                   
                                 
                  

                                   
                    
                         
 



                                                              

                                   
                              
                                                                             
                                                                   
 
                                     
                                        
 






                                                                                         


                                        


                                                                                        
                                                      
     
                                                                                                  
                                
                            

                                               

                 
 
                                                               
                                                                                    
     
                                                      
 


                                                                                       
                                                                     
                                                                   
 

                                                                                           
     
                                            
 



                                                                            
                                            
 

                                            
 


                                                                      

                                                                                                             
                                                                     


                                                                      

 





                                                                                               
 

                                                           

                                        
                                                                     
                                                                                  

                    
 




                                                                  
                                                          
                                                                           
 


                                                        



                                                                               




                                                           




                                                                                    
    
                                                                                                   









                                                                               
     
                                                  
                                    
                         


                                                                  


                                       
     
                                      

                    
                      

   
                                                    
                                                      
                                       

                                 
                                                        
                          

                                                           
         

       
                           
                                          
   
 


























                                                                                           
                                                     
                                  
                              
                                      





                                                          

   
                                                                          


                                                                                  
 
package dotty.tools
package dotc
package core

import Types._
import Flags._
import Contexts._
import util.{SimpleMap, DotClass}
import reporting._
import printing.{Showable, Printer}
import printing.Texts._
import config.Config
import collection.mutable

class TyperState(r: Reporter) extends DotClass with Showable {

  /** The current reporter */
  def reporter = r

  /** The current constraint set */
  def constraint: Constraint =
    new OrderingConstraint(SimpleMap.Empty, SimpleMap.Empty, SimpleMap.Empty)
  def constraint_=(c: Constraint)(implicit ctx: Context): Unit = {}

  /** The uninstantiated variables */
  def uninstVars = constraint.uninstVars

  /** The ephemeral flag is set as a side effect if an operation accesses
   *  the underlying type of a type variable. The reason we need this flag is
   *  that any such operation is not referentially transparent; it might logically change
   *  its value at the moment the type variable is instantiated. Caching code needs to
   *  check the ephemeral flag; If the flag is set during an operation, the result
   *  of that operation should not be cached.
   */
  def ephemeral: Boolean = false
  def ephemeral_=(x: Boolean): Unit = ()

  /** Gives for each instantiated type var that does not yet have its `inst` field
   *  set, the instance value stored in the constraint. Storing instances in constraints
   *  is done only in a temporary way for contexts that may be retracted
   *  without also retracting the type var as a whole.
   */
  def instType(tvar: TypeVar)(implicit ctx: Context): Type = constraint.entry(tvar.origin) match {
    case _: TypeBounds => NoType
    case tp: TypeParamRef =>
      var tvar1 = constraint.typeVarOfParam(tp)
      if (tvar1.exists) tvar1 else tp
    case tp => tp
  }

  /** A fresh typer state with the same constraint as this one.
   *  @param isCommittable  The constraint can be committed to an enclosing context.
   */
  def fresh(isCommittable: Boolean): TyperState = this

  /** A fresh type state with the same constraint as this one and the given reporter */
  def withReporter(reporter: Reporter) = new TyperState(reporter)

  /** Commit state so that it gets propagated to enclosing context */
  def commit()(implicit ctx: Context): Unit = unsupported("commit")

  /** The closest ancestor of this typer state (including possibly this typer state itself)
   *  which is not yet committed, or which does not have a parent.
   */
  def uncommittedAncestor: TyperState = this

  /** Make type variable instances permanent by assigning to `inst` field if
   *  type variable instantiation cannot be retracted anymore. Then, remove
   *  no-longer needed constraint entries.
   */
  def gc()(implicit ctx: Context): Unit = ()

  /** Is it allowed to commit this state? */
  def isCommittable: Boolean = false

  /** Can this state be transitively committed until the top-level? */
  def isGlobalCommittable: Boolean = false

  def tryWithFallback[T](op: => T)(fallback: => T)(implicit ctx: Context): T = unsupported("tryWithFallBack")

  override def toText(printer: Printer): Text = "ImmutableTyperState"

  /** A string showing the hashes of all nested mutable typerstates */
  def hashesStr: String = ""
}

class MutableTyperState(previous: TyperState, r: Reporter, override val isCommittable: Boolean)
extends TyperState(r) {

  private var myReporter = r

  override def reporter = myReporter

  private val previousConstraint = previous.constraint
  private var myConstraint: Constraint = previousConstraint

  override def constraint = myConstraint
  override def constraint_=(c: Constraint)(implicit ctx: Context) = {
    if (Config.debugCheckConstraintsClosed && isGlobalCommittable) c.checkClosed()
    myConstraint = c
  }

  private var myEphemeral: Boolean = previous.ephemeral

  override def ephemeral = myEphemeral
  override def ephemeral_=(x: Boolean): Unit = { myEphemeral = x }

  override def fresh(isCommittable: Boolean): TyperState =
    new MutableTyperState(this, new StoreReporter(reporter), isCommittable)

  override def withReporter(reporter: Reporter) =
    new MutableTyperState(this, reporter, isCommittable)

  override val isGlobalCommittable =
    isCommittable &&
    (!previous.isInstanceOf[MutableTyperState] || previous.isGlobalCommittable)

  private var isCommitted = false

  override def uncommittedAncestor: TyperState =
    if (isCommitted) previous.uncommittedAncestor else this

  /** Commit typer state so that its information is copied into current typer state
   *  In addition (1) the owning state of undetermined or temporarily instantiated
   *  type variables changes from this typer state to the current one. (2) Variables
   *  that were temporarily instantiated in the current typer state are permanently
   *  instantiated instead.
   *
   *  A note on merging: An interesting test case is isApplicableSafe.scala. It turns out that this
   *  requires a context merge using the new `&' operator. Sequence of actions:
   *  1) Typecheck argument in typerstate 1.
   *  2) Cache argument.
   *  3) Evolve same typer state (to typecheck other arguments, say)
   *     leading to a different constraint.
   *  4) Take typechecked argument in same state.
   *
   * It turns out that the merge is needed not just for
   * isApplicableSafe but also for (e.g. erased-lubs.scala) as well as
   * many parts of dotty itself.
   */
  override def commit()(implicit ctx: Context) = {
    val targetState = ctx.typerState
    assert(isCommittable)
    targetState.constraint =
      if (targetState.constraint eq previousConstraint) constraint
      else targetState.constraint & constraint
    constraint foreachTypeVar { tvar =>
      if (tvar.owningState eq this)
        tvar.owningState = targetState
    }
    targetState.ephemeral |= ephemeral
    targetState.gc()
    reporter.flush()
    isCommitted = true
  }

  override def gc()(implicit ctx: Context): Unit = {
    val toCollect = new mutable.ListBuffer[TypeLambda]
    constraint foreachTypeVar { tvar =>
      if (!tvar.inst.exists) {
        val inst = instType(tvar)
        if (inst.exists && (tvar.owningState eq this)) {
          tvar.inst = inst
          val lam = tvar.origin.binder
          if (constraint.isRemovable(lam)) toCollect += lam
        }
      }
    }
    for (poly <- toCollect)
      constraint = constraint.remove(poly)
  }

  /** Try operation `op`; if it produces errors, execute `fallback` with constraint and
   *  reporter as they were before `op` was executed. This is similar to `typer/tryEither`,
   *  but with one important difference: Any type variable instantiations produced by `op`
   *  are persisted even if `op` fails. This is normally not what one wants and therefore
   *  it is recommended to use
   *
   *      tryEither { implicit ctx => op } { (_, _) => fallBack }
   *
   *  instead of
   *
   *      ctx.tryWithFallback(op)(fallBack)
   *
   *  `tryWithFallback` is only used when an implicit parameter search fails
   *  and the whole expression is subsequently retype-checked with a Wildcard
   *  expected type (so as to allow an implicit conversion on the result and
   *  avoid over-constraining the implicit parameter search). In this case,
   *  the only type variables that might be falsely instantiated by `op` but
   *  not by `fallBack` are type variables in the typed expression itself, and
   *  these will be thrown away and new ones will be created on re-typing.
   *  So `tryWithFallback` is safe. It is also necessary because without it
   *  we do not propagate enough instantiation information into the implicit search
   *  and this might lead to a missing parameter type error. This is exhibited
   *  at several places in the test suite (for instance in `pos_typers`).
   *  Overall, this is rather ugly, but despite trying for 2 days I have not
   *  found a better solution.
   */
  override def tryWithFallback[T](op: => T)(fallback: => T)(implicit ctx: Context): T = {
    val storeReporter = new StoreReporter(myReporter)
    val savedReporter = myReporter
    myReporter = storeReporter
    val savedConstraint = myConstraint
    val result = try op finally myReporter = savedReporter
    if (!storeReporter.hasErrors) result
    else {
      myConstraint = savedConstraint
      fallback
    }
  }

  override def toText(printer: Printer): Text = constraint.toText(printer)

  override def hashesStr: String = hashCode.toString + " -> " + previous.hashesStr

}