Skip to content

Commit 474e79a

Browse files
authored
Merge pull request #4447 from dotty-staging/fix/constraint-simplification
More efficient garbage collection of type variables
2 parents 40258da + eeb5965 commit 474e79a

File tree

5 files changed

+169
-25
lines changed

5 files changed

+169
-25
lines changed

compiler/src/dotty/tools/dotc/core/Constraint.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,13 @@ abstract class Constraint extends Showable {
135135
*/
136136
def uninstVars: collection.Seq[TypeVar]
137137

138-
/** The weakest constraint that subsumes both this constraint and `other` */
139-
def & (other: Constraint)(implicit ctx: Context): Constraint
138+
/** The weakest constraint that subsumes both this constraint and `other`.
139+
*
140+
* @param otherHasErrors If true, handle incompatible constraints by
141+
* returning an approximate constraint, instead of
142+
* failing with an exception
143+
*/
144+
def & (other: Constraint, otherHasErrors: Boolean)(implicit ctx: Context): Constraint
140145

141146
/** Check that no constrained parameter contains itself as a bound */
142147
def checkNonCyclic()(implicit ctx: Context): Unit

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,29 +63,38 @@ trait ConstraintHandling {
6363
}
6464
if (Config.checkConstraintsSeparated)
6565
assert(!occursIn(bound), s"$param occurs in $bound")
66-
val newBound = narrowedBound(param, bound, isUpper)
67-
val c1 = constraint.updateEntry(param, newBound)
68-
(c1 eq constraint) || {
69-
constraint = c1
70-
val TypeBounds(lo, hi) = constraint.entry(param)
71-
isSubType(lo, hi)
66+
67+
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
68+
val equalBounds = isUpper && (lo eq bound) || !isUpper && (bound eq hi)
69+
if (equalBounds && !bound.existsPart(_.isInstanceOf[WildcardType])) {
70+
// The narrowed bounds are equal and do not contain wildcards,
71+
// so we can remove `param` from the constraint.
72+
// (Handling wildcards requires choosing a bound, but we don't know which
73+
// bound to choose here, this is handled in `ConstraintHandling#approximation`)
74+
constraint = constraint.replace(param, bound)
75+
true
76+
}
77+
else {
78+
// Narrow one of the bounds of type parameter `param`
79+
// If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
80+
// that `param >: bound`.
81+
val narrowedBounds = {
82+
val saved = homogenizeArgs
83+
homogenizeArgs = Config.alignArgsInAnd
84+
try
85+
if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
86+
else oldBounds.derivedTypeBounds(lo | bound, hi)
87+
finally homogenizeArgs = saved
88+
}
89+
val c1 = constraint.updateEntry(param, narrowedBounds)
90+
(c1 eq constraint) || {
91+
constraint = c1
92+
val TypeBounds(lo, hi) = constraint.entry(param)
93+
isSubType(lo, hi)
94+
}
7295
}
7396
}
7497

75-
/** Narrow one of the bounds of type parameter `param`
76-
* If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
77-
* that `param >: bound`.
78-
*/
79-
def narrowedBound(param: TypeParamRef, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = {
80-
val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
81-
val saved = homogenizeArgs
82-
homogenizeArgs = Config.alignArgsInAnd
83-
try
84-
if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
85-
else oldBounds.derivedTypeBounds(lo | bound, hi)
86-
finally homogenizeArgs = saved
87-
}
88-
8998
private def location(implicit ctx: Context) = "" // i"in ${ctx.typerState.stateChainStr}" // use for debugging
9099

91100
protected def addUpperBound(param: TypeParamRef, bound: Type): Boolean = {

compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
501501
}
502502
}
503503

504-
def & (other: Constraint)(implicit ctx: Context) = {
504+
def & (other: Constraint, otherHasErrors: Boolean)(implicit ctx: Context) = {
505505
def merge[T](m1: ArrayValuedMap[T], m2: ArrayValuedMap[T], join: (T, T) => T): ArrayValuedMap[T] = {
506506
var merged = m1
507507
def mergeArrays(xs1: Array[T], xs2: Array[T]) = {
@@ -528,7 +528,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
528528
case (_, e2: TypeBounds) if e2 contains e1 => e1
529529
case (tv1: TypeVar, tv2: TypeVar) if tv1.instanceOpt eq tv2.instanceOpt => e1
530530
case _ =>
531-
throw new AssertionError(i"cannot merge $this with $other, mergeEntries($e1, $e2) failed")
531+
if (otherHasErrors)
532+
e1
533+
else
534+
throw new AssertionError(i"cannot merge $this with $other, mergeEntries($e1, $e2) failed")
532535
}
533536

534537
val that = other.asInstanceOf[OrderingConstraint]

compiler/src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class TyperState(previous: TyperState /* | Null */) {
157157
assert(isCommittable)
158158
targetState.constraint =
159159
if (targetState.constraint eq previousConstraint) constraint
160-
else targetState.constraint & constraint
160+
else targetState.constraint & (constraint, otherHasErrors = reporter.errorsReported)
161161
constraint foreachTypeVar { tvar =>
162162
if (tvar.owningState.get eq this) tvar.owningState = new WeakReference(targetState)
163163
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Adapted from https://github.com/scala/compiler-benchmark/blob/master/corpus/induction/latest/inductive-implicits-bench.scala
2+
3+
// With polymorphic implicit pruning:
4+
// set resolvers in compilation += "pr-scala snapshots" at "https://scala-ci.typesafe.com/artifactory/scala-pr-validation-snapshots/"
5+
// set scalaVersion in compilation := "2.13.0-pre-765b3ed-SNAPSHOT"
6+
//
7+
// Without polymorphic implicit pruning:
8+
// set resolvers in compilation += "scala-integration" at "https://scala-ci.typesafe.com/artifactory/scala-integration/"
9+
// set scalaVersion in compilation := "2.13.0-pre-1c56f0a"
10+
//
11+
// Then:
12+
// cold -psource=induction -jvmArgs -Xss4M -jvmArgs -Xmx2G
13+
//
14+
// Nb. this is *very* slow without the pruning (> 400s).
15+
// With the pruning: 10-20s on reasonable hardware.
16+
17+
package shapeless {
18+
sealed trait HList extends Product with Serializable
19+
20+
final case class ::[+H, +T <: HList](head : H, tail : T) extends HList {
21+
def ::[HH](h : HH) : HH :: H :: T = shapeless.::(h, this)
22+
23+
override def toString = head match {
24+
case _: ::[_, _] => "("+head.toString+") :: "+tail.toString
25+
case _ => head.toString+" :: "+tail.toString
26+
}
27+
}
28+
29+
sealed trait HNil extends HList {
30+
def ::[H](h : H) = shapeless.::(h, this)
31+
override def toString = "HNil"
32+
}
33+
34+
case object HNil extends HNil
35+
36+
//@annotation.inductive
37+
trait Selector[L <: HList, U] {
38+
def apply(l: L): U
39+
}
40+
41+
object Selector {
42+
def apply[L <: HList, U](implicit selector: Selector[L, U]): Selector[L, U] = selector
43+
44+
implicit def inHead[H, T <: HList]: Selector[H :: T, H] =
45+
new Selector[H :: T, H] {
46+
def apply(l : H :: T) = l.head
47+
}
48+
49+
implicit def inTail[H, T <: HList, U]
50+
(implicit st : Selector[T, U]): Selector[H :: T, U] =
51+
new Selector[H :: T, U] {
52+
def apply(l : H :: T) = st(l.tail)
53+
}
54+
}
55+
}
56+
57+
import shapeless._
58+
59+
object Test extends App {
60+
val sel = Selector[L, Boolean]
61+
62+
type L =
63+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
64+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
65+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
66+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
67+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
68+
//
69+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
70+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
71+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
72+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
73+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
74+
//
75+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
76+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
77+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
78+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
79+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
80+
/*
81+
//
82+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
83+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
84+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
85+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
86+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
87+
//
88+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
89+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
90+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
91+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
92+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
93+
//
94+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
95+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
96+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
97+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
98+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
99+
//
100+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
101+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
102+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
103+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
104+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
105+
//
106+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
107+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
108+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
109+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
110+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
111+
//
112+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
113+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
114+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
115+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
116+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
117+
//
118+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
119+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
120+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
121+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
122+
Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int ::
123+
//
124+
*/
125+
Boolean ::
126+
HNil
127+
}

0 commit comments

Comments
 (0)