aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala636
1 files changed, 636 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
new file mode 100644
index 000000000..72c7a8e51
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
@@ -0,0 +1,636 @@
+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[PolyType, Array[T]]
+
+ /** The type of `OrderingConstraint#boundsMap` */
+ type ParamBounds = ArrayValuedMap[Type]
+
+ /** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */
+ type ParamOrdering = ArrayValuedMap[List[PolyParam]]
+
+ /** 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: PolyType): Array[T]
+ def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[T])(implicit ctx: Context): OrderingConstraint
+ def initial: T
+
+ def apply(c: OrderingConstraint, poly: PolyType, 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: PolyType, 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: PolyParam, entry: T)(implicit ctx: Context): OrderingConstraint =
+ update(prev, current, param.binder, param.paramNum, entry)
+
+ def map(prev: OrderingConstraint, current: OrderingConstraint,
+ poly: PolyType, 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: PolyParam, 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: PolyType): Array[Type] =
+ c.boundsMap(poly)
+ def updateEntries(c: OrderingConstraint, poly: PolyType, 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[PolyParam]] {
+ def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] =
+ c.lowerMap(poly)
+ def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint =
+ newConstraint(c.boundsMap, c.lowerMap.updated(poly, entries), c.upperMap)
+ def initial = Nil
+ }
+
+ val upperLens = new ConstraintLens[List[PolyParam]] {
+ def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] =
+ c.upperMap(poly)
+ def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(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 PolyType to arrays.
+ * Each array contains twice the number of entries as there a type parameters
+ * in the PolyType. The first half of the array contains the type bounds that constrain the
+ * polytype'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 PolyTypes to arrays. Each array entry corresponds
+ * to a parameter P of the polytype; it contains all constrained parameters
+ * Q that are known to be smaller than P, i.e. Q <: P.
+ * @param upperMap a map from PolyTypes to arrays. Each array entry corresponds
+ * to a parameter P of the polytype; 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: PolyParam): Type = {
+ val entries = boundsMap(param.binder)
+ if (entries == null) NoType
+ else entries(param.paramNum)
+ }
+
+// ----------- Contains tests --------------------------------------------------
+
+ def contains(pt: PolyType): Boolean = boundsMap(pt) != null
+
+ def contains(param: PolyParam): 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: PolyParam): List[PolyParam] = lowerLens(this, param.binder, param.paramNum)
+ def upper(param: PolyParam): List[PolyParam] = upperLens(this, param.binder, param.paramNum)
+
+ def minLower(param: PolyParam): List[PolyParam] = {
+ val all = lower(param)
+ all.filterNot(p => all.exists(isLess(p, _)))
+ }
+
+ def minUpper(param: PolyParam): List[PolyParam] = {
+ val all = upper(param)
+ all.filterNot(p => all.exists(isLess(_, p)))
+ }
+
+ def exclusiveLower(param: PolyParam, butNot: PolyParam): List[PolyParam] =
+ lower(param).filterNot(isLess(_, butNot))
+
+ def exclusiveUpper(param: PolyParam, butNot: PolyParam): List[PolyParam] =
+ upper(param).filterNot(isLess(butNot, _))
+
+// ---------- Info related to PolyParams -------------------------------------------
+
+ def isLess(param1: PolyParam, param2: PolyParam): Boolean =
+ upper(param1).contains(param2)
+
+ def nonParamBounds(param: PolyParam): TypeBounds =
+ entry(param).asInstanceOf[TypeBounds]
+
+ def fullLowerBound(param: PolyParam)(implicit ctx: Context): Type =
+ (nonParamBounds(param).lo /: minLower(param))(_ | _)
+
+ def fullUpperBound(param: PolyParam)(implicit ctx: Context): Type =
+ (nonParamBounds(param).hi /: minUpper(param))(_ & _)
+
+ def fullBounds(param: PolyParam)(implicit ctx: Context): TypeBounds =
+ nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param))
+
+ def typeVarOfParam(param: PolyParam): Type = {
+ val entries = boundsMap(param.binder)
+ if (entries == null) NoType
+ else {
+ val tvar = typeVar(entries, param.paramNum)
+ if (tvar != null) tvar else NoType
+ }
+ }
+
+// ---------- Adding PolyTypes --------------------------------------------------
+
+ /** 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[PolyParam] = tp match {
+ case param: PolyParam 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[PolyParam],
+ isUpper: Boolean)(implicit ctx: Context): Type = tp match {
+ case param: PolyParam 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[PolyParam],
+ isUpper: Boolean)(implicit ctx: Context): Type =
+ stripParams(tp, paramBuf, isUpper)
+ .orElse(if (isUpper) defn.AnyType else defn.NothingType)
+
+ def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This = {
+ assert(!contains(poly))
+ val nparams = poly.paramNames.length
+ val entries1 = new Array[Type](nparams * 2)
+ poly.paramBounds.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: PolyType)(implicit ctx: Context): This = {
+ var current = this
+ val loBuf, hiBuf = new mutable.ListBuffer[PolyParam]
+ var i = 0
+ while (i < poly.paramNames.length) {
+ val param = PolyParam(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: PolyParam, param2: PolyParam)(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: PolyParam, param2: PolyParam)(implicit ctx: Context): This =
+ order(this, param1, param2)
+
+ def updateEntry(current: This, param: PolyParam, 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: PolyParam, tp: Type)(implicit ctx: Context): This =
+ updateEntry(this, param, tp)
+
+ def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This = {
+ val p1Bounds = (nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1)
+ updateEntry(p1, p1Bounds).replace(p2, p1)
+ }
+
+ def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This = {
+ val oldBounds @ TypeBounds(lo, hi) = nonParamBounds(param)
+ val newBounds =
+ if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
+ else oldBounds.derivedTypeBounds(lo | bound, hi)
+ updateEntry(param, newBounds)
+ }
+
+// ---------- 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: PolyParam, 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[PolyParam]) =
+ ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx)
+
+ def replaceParam(tp: Type, atPoly: PolyType, 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: PolyParam 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: PolyType)(implicit ctx: Context): This = {
+ def removeFromOrdering(po: ParamOrdering) = {
+ def removeFromBoundss(key: PolyType, bndss: Array[List[PolyParam]]): Array[List[PolyParam]] = {
+ 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: PolyType): 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 domainPolys: List[PolyType] = boundsMap.keys
+
+ def domainParams: List[PolyParam] =
+ for {
+ (poly, entries) <- boundsMap.toList
+ n <- 0 until paramCount(entries)
+ if entries(n).exists
+ } yield PolyParam(poly, n)
+
+ def forallParams(p: PolyParam => Boolean): Boolean = {
+ boundsMap.foreachBinding { (poly, entries) =>
+ for (i <- 0 until paramCount(entries))
+ if (isBounds(entries(i)) && !p(PolyParam(poly, i))) return false
+ }
+ true
+ }
+
+ def foreachParam(p: (PolyType, 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[PolyParam], ps2: List[PolyParam]) =
+ (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 isFreePolyParam(tp: Type) = tp match {
+ case PolyParam(binder: PolyType, _) => !contains(binder)
+ case _ => false
+ }
+ def checkClosedType(tp: Type, where: String) =
+ if (tp != null)
+ assert(!tp.existsPart(isFreePolyParam), 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: PolyParam)(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(domainPolys 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 = " + domainPolys.mkString("\n")
+ val boundsText =
+ " bounds = " + {
+ val assocs =
+ for (param <- domainParams)
+ yield
+ param.binder.paramNames(param.paramNum) + ": " + entryText(entry(param))
+ assocs.mkString("\n")
+ }
+ constrainedText + "\n" + boundsText
+ }
+}