Skip to content

Commit cdf5b1e

Browse files
authored
Merge pull request #6331 from dotty-staging/opt-interpolate
Optimize interpolateTypeVars
2 parents 4d56be3 + 4819f27 commit cdf5b1e

File tree

2 files changed

+160
-61
lines changed

2 files changed

+160
-61
lines changed

compiler/src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -410,57 +410,60 @@ trait Inferencing { this: Typer =>
410410
// anymore if they've been garbage-collected, so we can't use
411411
// `state.ownedVars.size > locked.size` as an early check to avoid computing
412412
// `qualifying`.
413-
val qualifying = state.ownedVars -- locked
414-
415-
if (!qualifying.isEmpty) {
416-
typr.println(i"interpolate $tree: ${tree.tpe.widen} in $state, owned vars = ${state.ownedVars.toList}%, %, previous = ${locked.toList}%, % / ${state.constraint}")
417-
val resultAlreadyConstrained =
418-
tree.isInstanceOf[Apply] || tree.tpe.isInstanceOf[MethodOrPoly]
419-
if (!resultAlreadyConstrained)
420-
constrainResult(tree.symbol, tree.tpe, pt)
421-
// This is needed because it could establish singleton type upper bounds. See i2998.scala.
422-
423-
val tp = tree.tpe.widen
424-
val vs = variances(tp)
425-
426-
// Avoid interpolating variables occurring in tree's type if typerstate has unreported errors.
427-
// Reason: The errors might reflect unsatisfiable constraints. In that
428-
// case interpolating without taking account the constraints risks producing
429-
// nonsensical types that then in turn produce incomprehensible errors.
430-
// An example is in neg/i1240.scala. Without the condition in the next code line
431-
// we get for
432-
//
433-
// val y: List[List[String]] = List(List(1))
434-
//
435-
// i1430.scala:5: error: type mismatch:
436-
// found : Int(1)
437-
// required: Nothing
438-
// val y: List[List[String]] = List(List(1))
439-
// ^
440-
// With the condition, we get the much more sensical:
441-
//
442-
// i1430.scala:5: error: type mismatch:
443-
// found : Int(1)
444-
// required: String
445-
// val y: List[List[String]] = List(List(1))
446-
val hasUnreportedErrors = state.reporter.hasUnreportedErrors
447-
def constraint = state.constraint
448-
for (tvar <- qualifying)
449-
if (!tvar.isInstantiated && state.constraint.contains(tvar)) {
450-
// Needs to be checked again, since previous interpolations could already have
451-
// instantiated `tvar` through unification.
452-
val v = vs(tvar)
453-
if (v == null) {
454-
typr.println(i"interpolate non-occurring $tvar in $state in $tree: $tp, fromBelow = ${tvar.hasLowerBound}, $constraint")
455-
tvar.instantiate(fromBelow = tvar.hasLowerBound)
456-
}
457-
else if (!hasUnreportedErrors)
458-
if (v.intValue != 0) {
459-
typr.println(i"interpolate $tvar in $state in $tree: $tp, fromBelow = ${v.intValue == 1}, $constraint")
460-
tvar.instantiate(fromBelow = v.intValue == 1)
413+
414+
val ownedVars = state.ownedVars
415+
if ((ownedVars ne locked) && !ownedVars.isEmpty) {
416+
val qualifying = ownedVars -- locked
417+
if (!qualifying.isEmpty) {
418+
typr.println(i"interpolate $tree: ${tree.tpe.widen} in $state, owned vars = ${state.ownedVars.toList}%, %, previous = ${locked.toList}%, % / ${state.constraint}")
419+
val resultAlreadyConstrained =
420+
tree.isInstanceOf[Apply] || tree.tpe.isInstanceOf[MethodOrPoly]
421+
if (!resultAlreadyConstrained)
422+
constrainResult(tree.symbol, tree.tpe, pt)
423+
// This is needed because it could establish singleton type upper bounds. See i2998.scala.
424+
425+
val tp = tree.tpe.widen
426+
val vs = variances(tp)
427+
428+
// Avoid interpolating variables occurring in tree's type if typerstate has unreported errors.
429+
// Reason: The errors might reflect unsatisfiable constraints. In that
430+
// case interpolating without taking account the constraints risks producing
431+
// nonsensical types that then in turn produce incomprehensible errors.
432+
// An example is in neg/i1240.scala. Without the condition in the next code line
433+
// we get for
434+
//
435+
// val y: List[List[String]] = List(List(1))
436+
//
437+
// i1430.scala:5: error: type mismatch:
438+
// found : Int(1)
439+
// required: Nothing
440+
// val y: List[List[String]] = List(List(1))
441+
// ^
442+
// With the condition, we get the much more sensical:
443+
//
444+
// i1430.scala:5: error: type mismatch:
445+
// found : Int(1)
446+
// required: String
447+
// val y: List[List[String]] = List(List(1))
448+
val hasUnreportedErrors = state.reporter.hasUnreportedErrors
449+
def constraint = state.constraint
450+
for (tvar <- qualifying)
451+
if (!tvar.isInstantiated && state.constraint.contains(tvar)) {
452+
// Needs to be checked again, since previous interpolations could already have
453+
// instantiated `tvar` through unification.
454+
val v = vs(tvar)
455+
if (v == null) {
456+
typr.println(i"interpolate non-occurring $tvar in $state in $tree: $tp, fromBelow = ${tvar.hasLowerBound}, $constraint")
457+
tvar.instantiate(fromBelow = tvar.hasLowerBound)
461458
}
462-
else typr.println(i"no interpolation for nonvariant $tvar in $state")
463-
}
459+
else if (!hasUnreportedErrors)
460+
if (v.intValue != 0) {
461+
typr.println(i"interpolate $tvar in $state in $tree: $tp, fromBelow = ${v.intValue == 1}, $constraint")
462+
tvar.instantiate(fromBelow = v.intValue == 1)
463+
}
464+
else typr.println(i"no interpolation for nonvariant $tvar in $state")
465+
}
466+
}
464467
}
465468
tree
466469
}

compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala

Lines changed: 107 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
package dotty.tools.dotc.util
22

3-
import collection.mutable.ListBuffer
3+
import collection.mutable
44

55
/** A simple linked set with `eq` as the comparison, optimized for small sets.
66
* It has linear complexity for `contains`, `+`, and `-`.
77
*/
88
abstract class SimpleIdentitySet[+Elem <: AnyRef] {
99
def size: Int
10-
def isEmpty: Boolean = size == 0
10+
final def isEmpty: Boolean = size == 0
1111
def + [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[E]
1212
def - [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[Elem]
1313
def contains[E >: Elem <: AnyRef](x: E): Boolean
1414
def foreach(f: Elem => Unit): Unit
15-
def toList: List[Elem] = {
16-
val buf = new ListBuffer[Elem]
17-
foreach(buf += _)
18-
buf.toList
19-
}
15+
def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean
16+
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A
17+
def toList: List[Elem]
2018
def ++ [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] =
21-
((this: SimpleIdentitySet[E]) /: that.toList)(_ + _)
22-
def -- [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[Elem] =
23-
(this /: that.toList)(_ - _)
19+
if (this.size == 0) that
20+
else if (that.size == 0) this
21+
else ((this: SimpleIdentitySet[E]) /: that)(_ + _)
22+
def -- [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] =
23+
if (that.size == 0) this
24+
else
25+
((SimpleIdentitySet.empty: SimpleIdentitySet[E]) /: this) { (s, x) =>
26+
if (that.contains(x)) s else s + x
27+
}
2428
override def toString: String = toList.mkString("(", ", ", ")")
2529
}
2630

@@ -33,6 +37,9 @@ object SimpleIdentitySet {
3337
this
3438
def contains[E <: AnyRef](x: E): Boolean = false
3539
def foreach(f: Nothing => Unit): Unit = ()
40+
def exists[E <: AnyRef](p: E => Boolean): Boolean = false
41+
def /: [A, E <: AnyRef](z: A)(f: (A, E) => A): A = z
42+
def toList = Nil
3643
}
3744

3845
private class Set1[+Elem <: AnyRef](x0: AnyRef) extends SimpleIdentitySet[Elem] {
@@ -43,6 +50,11 @@ object SimpleIdentitySet {
4350
if (x `eq` x0) empty else this
4451
def contains[E >: Elem <: AnyRef](x: E): Boolean = x `eq` x0
4552
def foreach(f: Elem => Unit): Unit = f(x0.asInstanceOf[Elem])
53+
def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean =
54+
p(x0.asInstanceOf[E])
55+
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A =
56+
f(z, x0.asInstanceOf[E])
57+
def toList = x0.asInstanceOf[Elem] :: Nil
4658
}
4759

4860
private class Set2[+Elem <: AnyRef](x0: AnyRef, x1: AnyRef) extends SimpleIdentitySet[Elem] {
@@ -55,6 +67,11 @@ object SimpleIdentitySet {
5567
else this
5668
def contains[E >: Elem <: AnyRef](x: E): Boolean = (x `eq` x0) || (x `eq` x1)
5769
def foreach(f: Elem => Unit): Unit = { f(x0.asInstanceOf[Elem]); f(x1.asInstanceOf[Elem]) }
70+
def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean =
71+
p(x0.asInstanceOf[E]) || p(x1.asInstanceOf[E])
72+
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A =
73+
f(f(z, x0.asInstanceOf[E]), x1.asInstanceOf[E])
74+
def toList = x0.asInstanceOf[Elem] :: x1.asInstanceOf[Elem] :: Nil
5875
}
5976

6077
private class Set3[+Elem <: AnyRef](x0: AnyRef, x1: AnyRef, x2: AnyRef) extends SimpleIdentitySet[Elem] {
@@ -78,9 +95,14 @@ object SimpleIdentitySet {
7895
def foreach(f: Elem => Unit): Unit = {
7996
f(x0.asInstanceOf[Elem]); f(x1.asInstanceOf[Elem]); f(x2.asInstanceOf[Elem])
8097
}
98+
def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean =
99+
p(x0.asInstanceOf[E]) || p(x1.asInstanceOf[E]) || p(x2.asInstanceOf[E])
100+
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A =
101+
f(f(f(z, x0.asInstanceOf[E]), x1.asInstanceOf[E]), x2.asInstanceOf[E])
102+
def toList = x0.asInstanceOf[Elem] :: x1.asInstanceOf[Elem] :: x2.asInstanceOf[Elem] :: Nil
81103
}
82104

83-
private class SetN[+Elem <: AnyRef](xs: Array[AnyRef]) extends SimpleIdentitySet[Elem] {
105+
private class SetN[+Elem <: AnyRef](val xs: Array[AnyRef]) extends SimpleIdentitySet[Elem] {
84106
def size = xs.length
85107
def + [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[E] =
86108
if (contains(x)) this
@@ -115,5 +137,79 @@ object SimpleIdentitySet {
115137
var i = 0
116138
while (i < size) { f(xs(i).asInstanceOf[Elem]); i += 1 }
117139
}
140+
def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean =
141+
xs.asInstanceOf[Array[E]].exists(p)
142+
def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A =
143+
(z /: xs.asInstanceOf[Array[E]])(f)
144+
def toList: List[Elem] = {
145+
val buf = new mutable.ListBuffer[Elem]
146+
foreach(buf += _)
147+
buf.toList
148+
}
149+
override def ++ [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] =
150+
that match {
151+
case that: SetN[_] =>
152+
var toAdd: mutable.ArrayBuffer[AnyRef] = null
153+
var i = 0
154+
val limit = that.xs.length
155+
while (i < limit) {
156+
val elem = that.xs(i)
157+
if (!contains(elem)) {
158+
if (toAdd == null) toAdd = new mutable.ArrayBuffer
159+
toAdd += elem
160+
}
161+
i += 1
162+
}
163+
if (toAdd == null) this
164+
else {
165+
val numAdded = toAdd.size
166+
val xs1 = new Array[AnyRef](size + numAdded)
167+
System.arraycopy(xs, 0, xs1, 0, size)
168+
var i = 0
169+
while (i < numAdded) {
170+
xs1(i + size) = toAdd(i)
171+
i += 1
172+
}
173+
new SetN[E](xs1)
174+
}
175+
case _ => super.++(that)
176+
}
177+
override def -- [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] =
178+
that match {
179+
case that: SetN[_] =>
180+
// both sets are large, optimize assuming they are similar
181+
// by starting from empty set and adding elements
182+
var toAdd: mutable.ArrayBuffer[AnyRef] = null
183+
val thisSize = this.size
184+
val thatSize = that.size
185+
val thatElems = that.xs
186+
var i = 0
187+
var searchStart = 0
188+
while (i < thisSize) {
189+
val elem = this.xs(i)
190+
var j = searchStart // search thatElems in round robin fashion, starting one after latest hit
191+
var missing = false
192+
while (!missing && (elem ne thatElems(j))) {
193+
j += 1
194+
if (j == thatSize) j = 0
195+
missing = j == searchStart
196+
}
197+
if (missing) {
198+
if (toAdd == null) toAdd = new mutable.ArrayBuffer
199+
toAdd += elem
200+
}
201+
else searchStart = (j + 1) % thatSize
202+
i += 1
203+
}
204+
if (toAdd == null) empty
205+
else toAdd.size match {
206+
case 1 => new Set1[E](toAdd(0))
207+
case 2 => new Set2[E](toAdd(0), toAdd(1))
208+
case 3 => new Set3[E](toAdd(0), toAdd(1), toAdd(2))
209+
case _ => new SetN[E](toAdd.toArray)
210+
}
211+
case _ => // this set is large, that set is small: reduce from above using `-`
212+
((this: SimpleIdentitySet[E]) /: that)(_ - _)
213+
}
118214
}
119215
}

0 commit comments

Comments
 (0)