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









                                                   

                                  
                         


                           
                                                          
 
                                                   
                                         
 
                                                                                 
                                                         
 






                                                                                                                                                     
 

                                                                                     

                                                                                                                            
                  
 
                                                                    


                                          
 




                                                                                   
                                                                     
                                                                                            


                                                   
                    













                                                            

                                                                     
                                                                                   
                                                                

                                                                  
                                                                                           

                                                                   
                                                                  
                                                                                    



                                                         
                                                                       
                       
                                                                                                                                 


                                                                               
 
                                                          
                                                                                     
                      
                                                                                                                                               



                                                                               
                                                          
                                                                                     
                      
                                                                                                                                               






                                                                               
                                                                            
                                
                                                     
                                                                                           
                                                                                                           
                                                                                            

                                                                                             

                                                                                      
  
                                                                                  
                                                                                            
                                                                    
                                                                                  
                                                                                            
                                                                    
   

                                                              
                                                                                   
 
                                
 
                                                                               
 







                                                                                            
                                          



                                         
 

                                                                                
                                                               
 
                                                
                                         
                                                        









                                                                                  
 
                                                                                
 

                                                                                                    
 
                                                           


                                                
 
                                                           


                                                
 
                                                                                     
                                             
 
                                                                                     
                                             
 
                                                                                       
 
                                                                   

                                  
                                                       
                                         
 
                                                                        

                                                        
                                                                        
                                                        
 
                                                                          

                                                                                         
                                                   





                                                 
   
 
                                                                                   
 
                                                                       
    


                                                            

                                                                                  







                                                                     
 







                                                                                 
                                                                                           






                                                                                  
    


                                                                              
                                                                               
                                    
    
                                                                              
     
                                                                               
                                                                 
                                                  










                                                      

   
                                                               
                                                                         
                                                                              
     
                                                                                  
                                                      

                                                              
 
                                                                                  


                                               
                                            


                                                                                   
 



                                                                           
                                                                     
                      
                                                           

                                        
                                       


                                                                
                                                                             








                                                          
 
                                                                                  
 

                                                                               
     
                                                                                                             










                                                                                      
                                                                                        
                               
 
                                                                                                


                                                              
                                                       
                                              
                                                      




                                              
 
                                                                               
                                
 
                                                                                


                                                                               
 

                                                                                   




                                                                                        
    

                                                                                  
    

                         
    
                                                      
    
                         
    

                                                                               
    
                     

                                                                                 

                                                                                
     
                                                                                           
                                             
                                  
          
                                             

                              
 
                                               
                                                                 
 
                                                                                   
                                           
 






                                                                                                  
 
                                                                      
                                                                                







                                                                                          
                                                       
           
 
                                                                                                 

                                           
       

                   
                                                                                








                                                                            
                                                             
                                                 
                                                                                                             

                                                                
       
                                                   


                                                                                                   
 
                                              
                               




                                                                    
       
                                         



                                                                                  
                                                      
 
                                        


                                         
                          
                                 
 
                                                           

                                                 
                                                                           


        
 
                                                        













                                                         














                                                                                                        
                                                                       








                                                                         




                                                                         












                                                                                
                                                             
                                                 
                                                                   



                                                  
                                                                                                         




                                                                                                    





















                                                                                             
 
                                                                                
                                                                                 
 



                                                                          
                            
                          
               






                                                          
                                                                                 














                                                                                
               





                                                                                            






                                                

                                                            








                                                                                    
 
package dotty.tools
package dotc
package core

import Types._, Contexts._, Symbols._, Decorators._
import util.SimpleMap
import collection.mutable
import printing.{Printer, Showable}
import printing.Texts._
import config.Config
import collection.immutable.BitSet
import reflect.ClassTag
import annotation.tailrec

object OrderingConstraint {

  type ArrayValuedMap[T] = SimpleMap[TypeLambda, Array[T]]

  /** The type of `OrderingConstraint#boundsMap` */
  type ParamBounds = ArrayValuedMap[Type]

  /** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */
  type ParamOrdering = ArrayValuedMap[List[TypeParamRef]]

  /** A new constraint with given maps */
  private def newConstraint(boundsMap: ParamBounds, lowerMap: ParamOrdering, upperMap: ParamOrdering)(implicit ctx: Context) : OrderingConstraint = {
    val result = new OrderingConstraint(boundsMap, lowerMap, upperMap)
    if (Config.checkConstraintsNonCyclic) result.checkNonCyclic()
    ctx.runInfo.recordConstraintSize(result, result.boundsMap.size)
    result
  }

  /** A lens for updating a single entry array in one of the three constraint maps */
  abstract class ConstraintLens[T <: AnyRef: ClassTag] {
    def entries(c: OrderingConstraint, poly: TypeLambda): Array[T]
    def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[T])(implicit ctx: Context): OrderingConstraint
    def initial: T

    def apply(c: OrderingConstraint, poly: TypeLambda, idx: Int) = {
      val es = entries(c, poly)
      if (es == null) initial else es(idx)
    }

    /** The `current` constraint but with the entry for `param` updated to `entry`.
     *  `current` is used linearly. If it is different from `prev` it is
     *  known to be dead after the call. Hence it is OK to update destructively
     *  parts of `current` which are not shared by `prev`.
     */
    def update(prev: OrderingConstraint, current: OrderingConstraint,
        poly: TypeLambda, idx: Int, entry: T)(implicit ctx: Context): OrderingConstraint = {
      var es = entries(current, poly)
      if (es != null && (es(idx) eq entry)) current
      else {
        val result =
          if (es == null) {
            es = Array.fill(poly.paramNames.length)(initial)
            updateEntries(current, poly, es)
          }
          else if (es ne entries(prev, poly))
            current // can re-use existing entries array.
          else {
            es = es.clone
            updateEntries(current, poly, es)
          }
        es(idx) = entry
        result
      }
    }

    def update(prev: OrderingConstraint, current: OrderingConstraint,
        param: TypeParamRef, entry: T)(implicit ctx: Context): OrderingConstraint =
      update(prev, current, param.binder, param.paramNum, entry)

    def map(prev: OrderingConstraint, current: OrderingConstraint,
        poly: TypeLambda, idx: Int, f: T => T)(implicit ctx: Context): OrderingConstraint =
     update(prev, current, poly, idx, f(apply(current, poly, idx)))

    def map(prev: OrderingConstraint, current: OrderingConstraint,
        param: TypeParamRef, f: T => T)(implicit ctx: Context): OrderingConstraint =
      map(prev, current, param.binder, param.paramNum, f)
  }

  val boundsLens = new ConstraintLens[Type] {
    def entries(c: OrderingConstraint, poly: TypeLambda): Array[Type] =
      c.boundsMap(poly)
    def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[Type])(implicit ctx: Context): OrderingConstraint =
      newConstraint(c.boundsMap.updated(poly, entries), c.lowerMap, c.upperMap)
    def initial = NoType
  }

  val lowerLens = new ConstraintLens[List[TypeParamRef]] {
    def entries(c: OrderingConstraint, poly: TypeLambda): Array[List[TypeParamRef]] =
      c.lowerMap(poly)
    def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[List[TypeParamRef]])(implicit ctx: Context): OrderingConstraint =
      newConstraint(c.boundsMap, c.lowerMap.updated(poly, entries), c.upperMap)
    def initial = Nil
  }

  val upperLens = new ConstraintLens[List[TypeParamRef]] {
    def entries(c: OrderingConstraint, poly: TypeLambda): Array[List[TypeParamRef]] =
      c.upperMap(poly)
    def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[List[TypeParamRef]])(implicit ctx: Context): OrderingConstraint =
      newConstraint(c.boundsMap, c.lowerMap, c.upperMap.updated(poly, entries))
    def initial = Nil
  }
}

import OrderingConstraint._

/** Constraint over undetermined type parameters that keeps separate maps to
 *  reflect parameter orderings.
 *  @param boundsMap a map from TypeLambda to arrays.
 *               Each array contains twice the number of entries as there a type parameters
 *               in the TypeLambda. The first half of the array contains the type bounds that constrain the
 *               lambda's type parameters. The second half might contain type variables that
 *               track the corresponding parameters, or is left empty (filled with nulls).
 *               An instantiated type parameter is represented by having its instance type in
 *               the corresponding array entry. The dual use of arrays for poly params
 *               and typevars is to save space and hopefully gain some speed.
 *
 *  @param lowerMap a map from TypeLambdas to arrays. Each array entry corresponds
 *               to a parameter P of the type lambda; it contains all constrained parameters
 *               Q that are known to be smaller than P, i.e. Q <: P.
 *  @param upperMap a map from TypeLambdas to arrays. Each array entry corresponds
 *               to a parameter P of the type lambda; it contains all constrained parameters
 *               Q that are known to be greater than P, i.e. P <: Q.
 */
class OrderingConstraint(private val boundsMap: ParamBounds,
                         private val lowerMap : ParamOrdering,
                         private val upperMap : ParamOrdering) extends Constraint {

  type This = OrderingConstraint

// ----------- Basic indices --------------------------------------------------

  /** The number of type parameters in the given entry array */
  private def paramCount(entries: Array[Type]) = entries.length >> 1

  /** The type variable corresponding to parameter numbered `n`, null if none was created */
  private def typeVar(entries: Array[Type], n: Int): Type =
    entries(paramCount(entries) + n)

  /** The `boundsMap` entry corresponding to `param` */
  def entry(param: TypeParamRef): Type = {
    val entries = boundsMap(param.binder)
    if (entries == null) NoType
    else entries(param.paramNum)
  }

// ----------- Contains tests --------------------------------------------------

  def contains(pt: TypeLambda): Boolean = boundsMap(pt) != null

  def contains(param: TypeParamRef): Boolean = {
    val entries = boundsMap(param.binder)
    entries != null && isBounds(entries(param.paramNum))
  }

  def contains(tvar: TypeVar): Boolean = {
    val origin = tvar.origin
    val entries = boundsMap(origin.binder)
    val pnum = origin.paramNum
    entries != null && isBounds(entries(pnum)) && (typeVar(entries, pnum) eq tvar)
  }

  private def isBounds(tp: Type) = tp.isInstanceOf[TypeBounds]

// ---------- Dependency handling ----------------------------------------------

  def lower(param: TypeParamRef): List[TypeParamRef] = lowerLens(this, param.binder, param.paramNum)
  def upper(param: TypeParamRef): List[TypeParamRef] = upperLens(this, param.binder, param.paramNum)

  def minLower(param: TypeParamRef): List[TypeParamRef] = {
    val all = lower(param)
    all.filterNot(p => all.exists(isLess(p, _)))
  }

  def minUpper(param: TypeParamRef): List[TypeParamRef] = {
    val all = upper(param)
    all.filterNot(p => all.exists(isLess(_, p)))
  }

  def exclusiveLower(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] =
    lower(param).filterNot(isLess(_, butNot))

  def exclusiveUpper(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] =
    upper(param).filterNot(isLess(butNot, _))

// ---------- Info related to TypeParamRefs -------------------------------------------

  def isLess(param1: TypeParamRef, param2: TypeParamRef): Boolean =
    upper(param1).contains(param2)

  def nonParamBounds(param: TypeParamRef): TypeBounds =
    entry(param).asInstanceOf[TypeBounds]

  def fullLowerBound(param: TypeParamRef)(implicit ctx: Context): Type =
    (nonParamBounds(param).lo /: minLower(param))(_ | _)

  def fullUpperBound(param: TypeParamRef)(implicit ctx: Context): Type =
    (nonParamBounds(param).hi /: minUpper(param))(_ & _)

  def fullBounds(param: TypeParamRef)(implicit ctx: Context): TypeBounds =
    nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param))

  def typeVarOfParam(param: TypeParamRef): Type = {
    val entries = boundsMap(param.binder)
    if (entries == null) NoType
    else {
      val tvar = typeVar(entries, param.paramNum)
      if (tvar != null) tvar else NoType
    }
  }

// ---------- Adding TypeLambdas --------------------------------------------------

  /** The list of parameters P such that, for a fresh type parameter Q:
   *
   *    Q <: tp  implies  Q <: P      and isUpper = true, or
   *    tp <: Q  implies  P <: Q      and isUpper = false
   */
  def dependentParams(tp: Type, isUpper: Boolean): List[TypeParamRef] = tp match {
    case param: TypeParamRef if contains(param) =>
      param :: (if (isUpper) upper(param) else lower(param))
    case tp: AndOrType =>
      val ps1 = dependentParams(tp.tp1, isUpper)
      val ps2 = dependentParams(tp.tp2, isUpper)
      if (isUpper == tp.isAnd) ps1.union(ps2) else ps1.intersect(ps2)
    case _ =>
      Nil
  }

  /** The bound type `tp` without constrained parameters which are clearly
   *  dependent. A parameter in an upper bound is clearly dependent if it appears
   *  in a hole of a context H given by:
   *
   *      H = []
   *          H & T
   *          T & H
   *
   *  (the idea is that a parameter P in a H context is guaranteed to be a supertype of the
   *   bounded parameter.)
   *  Analogously, a parameter in a lower bound is clearly dependent if it appears
   *  in a hole of a context H given by:
   *
   *      L = []
   *          L | T
   *          T | L
   *
   *  "Clearly dependent" is not synonymous with "dependent" in the sense
   *  it is defined in `dependentParams`. Dependent parameters are handled
   *  in `updateEntry`. The idea of stripping off clearly dependent parameters
   *  and to handle them separately is for efficiency, so that type expressions
   *  used as bounds become smaller.
   *
   *  @param isUpper   If true, `bound` is an upper bound, else a lower bound.
   */
  private def stripParams(tp: Type, paramBuf: mutable.ListBuffer[TypeParamRef],
      isUpper: Boolean)(implicit ctx: Context): Type = tp match {
    case param: TypeParamRef if contains(param) =>
      if (!paramBuf.contains(param)) paramBuf += param
      NoType
    case tp: AndOrType if isUpper == tp.isAnd =>
      val tp1 = stripParams(tp.tp1, paramBuf, isUpper)
      val tp2 = stripParams(tp.tp2, paramBuf, isUpper)
      if (tp1.exists)
        if (tp2.exists) tp.derivedAndOrType(tp1, tp2)
        else tp1
      else tp2
    case _ =>
      tp
  }

  /** The bound type `tp` without clearly dependent parameters.
   *  A top or bottom type if type consists only of dependent parameters.
   *  @param isUpper   If true, `bound` is an upper bound, else a lower bound.
   */
  private def normalizedType(tp: Type, paramBuf: mutable.ListBuffer[TypeParamRef],
      isUpper: Boolean)(implicit ctx: Context): Type =
    stripParams(tp, paramBuf, isUpper)
      .orElse(if (isUpper) defn.AnyType else defn.NothingType)

  def add(poly: TypeLambda, tvars: List[TypeVar])(implicit ctx: Context): This = {
    assert(!contains(poly))
    val nparams = poly.paramNames.length
    val entries1 = new Array[Type](nparams * 2)
    poly.paramInfos.copyToArray(entries1, 0)
    tvars.copyToArray(entries1, nparams)
    newConstraint(boundsMap.updated(poly, entries1), lowerMap, upperMap).init(poly)
  }

  /** Split dependent parameters off the bounds for parameters in `poly`.
   *  Update all bounds to be normalized and update ordering to account for
   *  dependent parameters.
   */
  private def init(poly: TypeLambda)(implicit ctx: Context): This = {
    var current = this
    val loBuf, hiBuf = new mutable.ListBuffer[TypeParamRef]
    var i = 0
    while (i < poly.paramNames.length) {
      val param = TypeParamRef(poly, i)
      val bounds = nonParamBounds(param)
      val lo = normalizedType(bounds.lo, loBuf, isUpper = false)
      val hi = normalizedType(bounds.hi, hiBuf, isUpper = true)
      current = updateEntry(current, param, bounds.derivedTypeBounds(lo, hi))
      current = (current /: loBuf)(order(_, _, param))
      current = (current /: hiBuf)(order(_, param, _))
      loBuf.clear()
      hiBuf.clear()
      i += 1
    }
    if (Config.checkConstraintsNonCyclic) checkNonCyclic()
    current
  }

// ---------- Updates ------------------------------------------------------------

  /** Add the fact `param1 <: param2` to the constraint `current` and propagate
   *  `<:<` relationships between parameters ("edges") but not bounds.
   */
  private def order(current: This, param1: TypeParamRef, param2: TypeParamRef)(implicit ctx: Context): This =
    if (param1 == param2 || current.isLess(param1, param2)) this
    else {
      assert(contains(param1))
      assert(contains(param2))
      val newUpper = param2 :: exclusiveUpper(param2, param1)
      val newLower = param1 :: exclusiveLower(param1, param2)
      val current1 = (current /: newLower)(upperLens.map(this, _, _, newUpper ::: _))
      val current2 = (current1 /: newUpper)(lowerLens.map(this, _, _, newLower ::: _))
      current2
    }

  def addLess(param1: TypeParamRef, param2: TypeParamRef)(implicit ctx: Context): This =
    order(this, param1, param2)

  def updateEntry(current: This, param: TypeParamRef, tp: Type)(implicit ctx: Context): This = {
    var current1 = boundsLens.update(this, current, param, tp)
    tp match {
      case TypeBounds(lo, hi) =>
        for (p <- dependentParams(lo, isUpper = false))
          current1 = order(current1, p, param)
        for (p <- dependentParams(hi, isUpper = true))
          current1 = order(current1, param, p)
      case _ =>
    }
    current1
  }

  def updateEntry(param: TypeParamRef, tp: Type)(implicit ctx: Context): This =
    updateEntry(this, param, tp)

  def unify(p1: TypeParamRef, p2: TypeParamRef)(implicit ctx: Context): This = {
    val p1Bounds = (nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1)
    updateEntry(p1, p1Bounds).replace(p2, p1)
  }

// ---------- Removals ------------------------------------------------------------

  /** A new constraint which is derived from this constraint by removing
   *  the type parameter `param` from the domain and replacing all top-level occurrences
   *  of the parameter elsewhere in the constraint by type `tp`, or a conservative
   *  approximation of it if that is needed to avoid cycles.
   *  Occurrences nested inside a refinement or prefix are not affected.
   *
   *  The reason we need to substitute top-level occurrences of the parameter
   *  is to deal with situations like the following. Say we have in the constraint
   *
   *      P <: Q & String
   *      Q
   *
   *  and we replace Q with P. Then substitution gives
   *
   *      P <: P & String
   *
   *  this would be a cyclic constraint is therefore changed by `normalize` and
   *  `recombine` below to
   *
   *      P <: String
   *
   *  approximating the RHS occurrence of P with Any. Without the substitution we
   *  would not find out where we need to approximate. Occurrences of parameters
   *  that are not top-level are not affected.
   */
  def replace(param: TypeParamRef, tp: Type)(implicit ctx: Context): OrderingConstraint = {
    val replacement = tp.dealias.stripTypeVar
    if (param == replacement) this
    else {
      assert(replacement.isValueTypeOrLambda)
      val poly = param.binder
      val idx = param.paramNum

      def removeParam(ps: List[TypeParamRef]) =
        ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx)

      def replaceParam(tp: Type, atPoly: TypeLambda, atIdx: Int): Type = tp match {
        case bounds @ TypeBounds(lo, hi) =>

          def recombine(andor: AndOrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = {
            val tp1 = op(andor.tp1, isUpper)
            val tp2 = op(andor.tp2, isUpper)
            if ((tp1 eq andor.tp1) && (tp2 eq andor.tp2)) andor
            else if (andor.isAnd) tp1 & tp2
            else tp1 | tp2
          }

          def normalize(tp: Type, isUpper: Boolean): Type = tp match {
            case p: TypeParamRef if p.binder == atPoly && p.paramNum == atIdx =>
              if (isUpper) defn.AnyType else defn.NothingType
            case tp: AndOrType if isUpper == tp.isAnd => recombine(tp, normalize, isUpper)
            case _ => tp
          }

          def replaceIn(tp: Type, isUpper: Boolean): Type = tp match {
            case `param` => normalize(replacement, isUpper)
            case tp: AndOrType if isUpper == tp.isAnd => recombine(tp, replaceIn, isUpper)
            case _ => tp.substParam(param, replacement)
          }

          bounds.derivedTypeBounds(replaceIn(lo, isUpper = false), replaceIn(hi, isUpper = true))
        case _ =>
          tp.substParam(param, replacement)
      }

      var current =
        if (isRemovable(poly)) remove(poly) else updateEntry(param, replacement)
      current.foreachParam {(p, i) =>
        current = boundsLens.map(this, current, p, i, replaceParam(_, p, i))
        current = lowerLens.map(this, current, p, i, removeParam)
        current = upperLens.map(this, current, p, i, removeParam)
      }
      current
    }
  }

  def remove(pt: TypeLambda)(implicit ctx: Context): This = {
    def removeFromOrdering(po: ParamOrdering) = {
      def removeFromBoundss(key: TypeLambda, bndss: Array[List[TypeParamRef]]): Array[List[TypeParamRef]] = {
        val bndss1 = bndss.map(_.filterConserve(_.binder ne pt))
        if (bndss.corresponds(bndss1)(_ eq _)) bndss else bndss1
      }
      po.remove(pt).mapValuesNow(removeFromBoundss)
    }
    newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap))
  }

  def isRemovable(pt: TypeLambda): Boolean = {
    val entries = boundsMap(pt)
    @tailrec def allRemovable(last: Int): Boolean =
      if (last < 0) true
      else typeVar(entries, last) match {
        case tv: TypeVar => tv.inst.exists && allRemovable(last - 1)
        case _ => false
      }
    allRemovable(paramCount(entries) - 1)
  }

// ---------- Exploration --------------------------------------------------------

  def domainLambdas: List[TypeLambda] = boundsMap.keys

  def domainParams: List[TypeParamRef] =
    for {
      (poly, entries) <- boundsMap.toList
      n <- 0 until paramCount(entries)
      if entries(n).exists
    } yield TypeParamRef(poly, n)

  def forallParams(p: TypeParamRef => Boolean): Boolean = {
    boundsMap.foreachBinding { (poly, entries) =>
      for (i <- 0 until paramCount(entries))
        if (isBounds(entries(i)) && !p(TypeParamRef(poly, i))) return false
    }
    true
  }

  def foreachParam(p: (TypeLambda, Int) => Unit): Unit =
    boundsMap.foreachBinding { (poly, entries) =>
      0.until(poly.paramNames.length).foreach(p(poly, _))
    }

  def foreachTypeVar(op: TypeVar => Unit): Unit =
    boundsMap.foreachBinding { (poly, entries) =>
      for (i <- 0 until paramCount(entries)) {
        typeVar(entries, i) match {
          case tv: TypeVar if !tv.inst.exists => op(tv)
          case _ =>
        }
      }
    }

  def & (other: Constraint)(implicit ctx: Context) = {
    def merge[T](m1: ArrayValuedMap[T], m2: ArrayValuedMap[T], join: (T, T) => T): ArrayValuedMap[T] = {
      var merged = m1
      def mergeArrays(xs1: Array[T], xs2: Array[T]) = {
        val xs = xs1.clone
        for (i <- xs.indices) xs(i) = join(xs1(i), xs2(i))
        xs
      }
      m2.foreachBinding { (poly, xs2) =>
        merged = merged.updated(poly,
            if (m1.contains(poly)) mergeArrays(m1(poly), xs2) else xs2)
      }
      merged
    }

    def mergeParams(ps1: List[TypeParamRef], ps2: List[TypeParamRef]) =
      (ps1 /: ps2)((ps1, p2) => if (ps1.contains(p2)) ps1 else p2 :: ps1)

    def mergeEntries(e1: Type, e2: Type): Type = e1 match {
        case e1: TypeBounds =>
          e2 match {
            case e2: TypeBounds => e1 & e2
            case _ if e1 contains e2 => e2
            case _ => mergeError
          }
        case tv1: TypeVar =>
          e2 match {
            case tv2: TypeVar if tv1.instanceOpt eq tv2.instanceOpt => e1
            case _ => mergeError
          }
        case _ if e1 eq e2 => e1
        case _ => mergeError
    }

    def mergeError = throw new AssertionError(i"cannot merge $this with $other")

    val that = other.asInstanceOf[OrderingConstraint]
    new OrderingConstraint(
        merge(this.boundsMap, that.boundsMap, mergeEntries),
        merge(this.lowerMap, that.lowerMap, mergeParams),
        merge(this.upperMap, that.upperMap, mergeParams))
  }

  override def checkClosed()(implicit ctx: Context): Unit = {
    def isFreeTypeParamRef(tp: Type) = tp match {
      case TypeParamRef(binder: TypeLambda, _) => !contains(binder)
      case _ => false
    }
    def checkClosedType(tp: Type, where: String) =
      if (tp != null)
        assert(!tp.existsPart(isFreeTypeParamRef), i"unclosed constraint: $this refers to $tp in $where")
    boundsMap.foreachBinding((_, tps) => tps.foreach(checkClosedType(_, "bounds")))
    lowerMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "lower"))))
    upperMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "upper"))))
  }

  private var myUninstVars: mutable.ArrayBuffer[TypeVar] = _

  /** The uninstantiated typevars of this constraint */
  def uninstVars: collection.Seq[TypeVar] = {
    if (myUninstVars == null) {
      myUninstVars = new mutable.ArrayBuffer[TypeVar]
      boundsMap.foreachBinding { (poly, entries) =>
        for (i <- 0 until paramCount(entries)) {
          typeVar(entries, i) match {
            case tv: TypeVar if !tv.inst.exists && isBounds(entries(i)) => myUninstVars += tv
            case _ =>
          }
        }
      }
    }
    myUninstVars
  }

// ---------- Cyclic checking -------------------------------------------

  def checkNonCyclic()(implicit ctx: Context): Unit =
    domainParams.foreach(checkNonCyclic)

  private def checkNonCyclic(param: TypeParamRef)(implicit ctx: Context): Unit =
    assert(!isLess(param, param), i"cyclic constraint involving $param in $this")

// ---------- toText -----------------------------------------------------

  override def toText(printer: Printer): Text = {
    def entryText(tp: Type) = tp match {
      case tp: TypeBounds =>
        tp.toText(printer)
      case _ =>
        " := " ~ tp.toText(printer)
    }
    val indent = 3
    val header: Text = "Constraint("
    val uninstVarsText = " uninstVars = " ~
      Text(uninstVars map (_.toText(printer)), ", ") ~ ";"
    val constrainedText =
      " constrained types = " ~ Text(domainLambdas map (_.toText(printer)), ", ")
    val boundsText =
      " bounds = " ~ {
        val assocs =
          for (param <- domainParams)
          yield (" " * indent) ~ param.toText(printer) ~ entryText(entry(param))
        Text(assocs, "\n")
      }
    val orderingText =
      " ordering = " ~ {
        val deps =
          for {
            param <- domainParams
            ups = minUpper(param)
            if ups.nonEmpty
          }
          yield
            (" " * indent) ~ param.toText(printer) ~ " <: " ~
              Text(ups.map(_.toText(printer)), ", ")
        Text(deps, "\n")
      }
    Text.lines(List(header, uninstVarsText, constrainedText, boundsText, orderingText, ")"))
  }

  override def toString: String = {
    def entryText(tp: Type): String = tp match {
      case tp: TypeBounds => tp.toString
      case _ =>" := " + tp
    }
    val constrainedText =
      " constrained types = " + domainLambdas.mkString("\n")
    val boundsText = domainLambdas
      " bounds = " + {
        val assocs =
          for (param <- domainParams)
          yield
            param.binder.paramNames(param.paramNum) + ": " + entryText(entry(param))
        assocs.mkString("\n")
    }
    constrainedText + "\n" + boundsText
  }
}