Skip to content

Commit 28eef15

Browse files
authored
Merge pull request scala#10361 from lrytz/t12760
Custom LinkedHashSet subclass for match analysis
2 parents dfe438a + 2a76547 commit 28eef15

File tree

2 files changed

+41
-16
lines changed

2 files changed

+41
-16
lines changed

src/compiler/scala/tools/nsc/transform/patmat/Logic.scala

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
package scala
1414
package tools.nsc.transform.patmat
1515

16-
import scala.collection.mutable
1716
import scala.collection.immutable.ArraySeq
18-
import scala.collection.IterableOps
17+
import scala.collection.{IterableOps, mutable}
1918
import scala.reflect.internal.util.Collections._
2019
import scala.reflect.internal.util.HashSet
20+
import scala.tools.nsc.transform.patmat.Logic.LogicLinkedHashSet
2121

2222
trait Logic extends Debugging {
2323
import global._
@@ -114,22 +114,21 @@ trait Logic extends Debugging {
114114
def implications: List[(Sym, List[Sym], List[Sym])]
115115
}
116116

117-
// The error message of t7020 assumes the ops are ordered implicitly
118-
// However, ListSet is slow (concatenate cost?), so use
119-
// scala.collection.mutable.LinkedHashSet (which grantees "the order in which elements were inserted into the set")
117+
// Using LogicLinkedHashSet (a custom mutable.LinkedHashSet subclass) to ensure deterministic exhaustivity
118+
// messages. immutable.ListSet was too slow (concatenate cost? scala/bug#12499).
120119

121120
// would be nice to statically check whether a prop is equational or pure,
122121
// but that requires typing relations like And(x: Tx, y: Ty) : (if(Tx == PureProp && Ty == PureProp) PureProp else Prop)
123-
final case class And(ops: mutable.LinkedHashSet[Prop]) extends Prop
122+
final case class And(ops: LogicLinkedHashSet[Prop]) extends Prop
124123
object And {
125124
def apply(ps: Prop*) = create(ps)
126-
def create(ps: Iterable[Prop]) = new And(ps.to(mutable.LinkedHashSet))
125+
def create(ps: Iterable[Prop]) = new And(ps.to(LogicLinkedHashSet))
127126
}
128127

129-
final case class Or(ops: mutable.LinkedHashSet[Prop]) extends Prop
128+
final case class Or(ops: LogicLinkedHashSet[Prop]) extends Prop
130129
object Or {
131130
def apply(ps: Prop*) = create(ps)
132-
def create(ps: Iterable[Prop]) = new Or(ps.to(mutable.LinkedHashSet))
131+
def create(ps: Iterable[Prop]) = new Or(ps.to(LogicLinkedHashSet))
133132
}
134133

135134
final case class Not(a: Prop) extends Prop
@@ -286,7 +285,7 @@ trait Logic extends Debugging {
286285
def simplifyAnd(ps: Iterable[Prop]): Prop = {
287286
// recurse for nested And (pulls all Ands up)
288287
// build up Set in order to remove duplicates
289-
val props = mutable.LinkedHashSet.empty[Prop]
288+
val props = LogicLinkedHashSet.empty[Prop]
290289
for (prop <- ps) {
291290
simplifyProp(prop) match {
292291
case True => // ignore `True`
@@ -302,7 +301,7 @@ trait Logic extends Debugging {
302301
def simplifyOr(ps: Iterable[Prop]): Prop = {
303302
// recurse for nested Or (pulls all Ors up)
304303
// build up Set in order to remove duplicates
305-
val props = mutable.LinkedHashSet.empty[Prop]
304+
val props = LogicLinkedHashSet.empty[Prop]
306305
for (prop <- ps) {
307306
simplifyProp(prop) match {
308307
case False => // ignore `False`
@@ -343,15 +342,15 @@ trait Logic extends Debugging {
343342
}
344343

345344
def gatherVariables(p: Prop): collection.Set[Var] = {
346-
val vars = new mutable.LinkedHashSet[Var]()
345+
val vars = new LogicLinkedHashSet[Var]()
347346
(new PropTraverser {
348347
override def applyVar(v: Var) = vars += v
349348
})(p)
350349
vars
351350
}
352351

353352
def gatherSymbols(p: Prop): collection.Set[Sym] = {
354-
val syms = new mutable.LinkedHashSet[Sym]()
353+
val syms = new LogicLinkedHashSet[Sym]()
355354
(new PropTraverser {
356355
override def applySymbol(s: Sym) = syms += s
357356
})(p)
@@ -409,7 +408,7 @@ trait Logic extends Debugging {
409408
def removeVarEq(props: List[Prop], modelNull: Boolean = false): (Prop, List[Prop]) = {
410409
val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.patmatAnaVarEq) else null
411410

412-
val vars = new mutable.LinkedHashSet[Var]
411+
val vars = new LogicLinkedHashSet[Var]
413412

414413
object gatherEqualities extends PropTraverser {
415414
override def apply(p: Prop) = p match {
@@ -516,6 +515,31 @@ trait Logic extends Debugging {
516515
}
517516
}
518517

518+
object Logic {
519+
import scala.annotation.nowarn
520+
import scala.collection.mutable.{Growable, GrowableBuilder, SetOps}
521+
import scala.collection.{IterableFactory, IterableFactoryDefaults, StrictOptimizedIterableOps}
522+
523+
// Local subclass because we can't override `addAll` in the collections (bin compat), see PR scala/scala#10361
524+
@nowarn("msg=inheritance from class LinkedHashSet")
525+
class LogicLinkedHashSet[A] extends mutable.LinkedHashSet[A]
526+
with SetOps[A, LogicLinkedHashSet, LogicLinkedHashSet[A]]
527+
with StrictOptimizedIterableOps[A, LogicLinkedHashSet, LogicLinkedHashSet[A]]
528+
with IterableFactoryDefaults[A, LogicLinkedHashSet] {
529+
override def iterableFactory: IterableFactory[LogicLinkedHashSet] = LogicLinkedHashSet
530+
override def addAll(xs: IterableOnce[A]): this.type = {
531+
sizeHint(xs.knownSize)
532+
super.addAll(xs)
533+
}
534+
}
535+
536+
object LogicLinkedHashSet extends IterableFactory[LogicLinkedHashSet] {
537+
override def from[A](source: IterableOnce[A]): LogicLinkedHashSet[A] = Growable.from(empty[A], source)
538+
override def empty[A]: LogicLinkedHashSet[A] = new LogicLinkedHashSet[A]
539+
override def newBuilder[A]: mutable.Builder[A, LogicLinkedHashSet[A]] = new GrowableBuilder(empty[A])
540+
}
541+
}
542+
519543
trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
520544
trait TreesAndTypesDomain extends PropositionalLogic with CheckableTreeAndTypeAnalysis {
521545
type Type = global.Type
@@ -733,8 +757,8 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
733757
}
734758

735759

736-
import global.{ConstantType, SingletonType, Literal, Ident, singleType, TypeBounds, NoSymbol}
737760
import global.definitions._
761+
import global.{ConstantType, Ident, Literal, NoSymbol, SingletonType, TypeBounds, singleType}
738762

739763

740764
// all our variables range over types

test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.junit.Test
55

66
import scala.collection.mutable
77
import scala.reflect.internal.util.Position
8+
import scala.tools.nsc.transform.patmat.Logic.LogicLinkedHashSet
89
import scala.tools.nsc.{Global, Settings}
910

1011
object TestSolver extends Logic with Solving {
@@ -579,7 +580,7 @@ class SolvingTest {
579580
def pairWiseEncoding(ops: List[Sym]) = {
580581
And(ops.combinations(2).collect {
581582
case a :: b :: Nil => Or(Not(a), Not(b)): Prop
582-
}.to(mutable.LinkedHashSet))
583+
}.to(LogicLinkedHashSet))
583584
}
584585

585586
@Test

0 commit comments

Comments
 (0)