Skip to content

Commit 0f5746f

Browse files
authored
Merge pull request scala#10352 from som-snytt/issue/12757-glblub-loop
More tailrec in handling long seq
2 parents 0842f23 + b9a4518 commit 0f5746f

File tree

7 files changed

+125
-46
lines changed

7 files changed

+125
-46
lines changed

src/partest/scala/tools/partest/nest/Runner.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) {
249249
// We'll let the checkfile diffing report this failure
250250
Files.write(log.toPath, stackTraceString(t).getBytes(Charset.defaultCharset()), CREATE, APPEND)
251251
case t: Throwable =>
252-
Files.write(log.toPath, t.getMessage.getBytes(Charset.defaultCharset()), CREATE, APPEND)
252+
val data = (if (t.getMessage != null) t.getMessage else t.getClass.getName).getBytes(Charset.defaultCharset())
253+
Files.write(log.toPath, data, CREATE, APPEND)
253254
throw t
254255
}
255256
}

src/reflect/scala/reflect/internal/Printers.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ trait Printers extends api.Printers { self: SymbolTable =>
109109
out.write(indentString, 0, indentMargin)
110110
}
111111

112-
def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit): Unit =
112+
@tailrec
113+
final def printSeq[A](ls: List[A])(printelem: A => Unit)(printsep: => Unit): Unit =
113114
ls match {
114115
case List() =>
115116
case List(x) => printelem(x)

src/reflect/scala/reflect/internal/tpe/GlbLubs.scala

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ package reflect
1515
package internal
1616
package tpe
1717

18-
import scala.collection.mutable
18+
import scala.collection.mutable, mutable.ListBuffer
1919
import scala.annotation.tailrec
2020
import Variance._
2121

@@ -101,20 +101,20 @@ private[internal] trait GlbLubs {
101101

102102
def headOf(ix: Int) = baseTypeSeqs(ix).rawElem(ices(ix))
103103

104-
val pretypes: mutable.ListBuffer[Type] = mutable.ListBuffer.empty[Type]
104+
val pretypes: ListBuffer[Type] = ListBuffer.empty[Type]
105105

106106
var isFinished = false
107-
while (! isFinished && ices(0) < baseTypeSeqs(0).length){
107+
while (!isFinished && ices(0) < baseTypeSeqs(0).length) {
108108
lubListDepth = lubListDepth.incr
109109
// Step 1: run through the List with these variables:
110110
// 1) Is there any empty list? Are they equal or are we taking the smallest?
111111
// isFinished: tsBts.exists(typeListIsEmpty)
112112
// Is the frontier made up of types with the same symbol?
113-
var isUniformFrontier = true
113+
var isUniformFrontier = true
114114
var sym = headOf(0).typeSymbol
115115
// var tsYs = tsBts
116116
var ix = 0
117-
while (! isFinished && ix < baseTypeSeqs.length){
117+
while (!isFinished && ix < baseTypeSeqs.length) {
118118
if (ices(ix) == baseTypeSeqs(ix).length)
119119
isFinished = true
120120
else {
@@ -130,7 +130,7 @@ private[internal] trait GlbLubs {
130130
// the invariant holds, i.e., the one that conveys most information regarding subtyping. Before
131131
// merging, strip targs that refer to bound tparams (when we're computing the lub of type
132132
// constructors.) Also filter out all types that are a subtype of some other type.
133-
if (! isFinished){
133+
if (!isFinished) {
134134
// ts0 is the 1-dimensional frontier of symbols cutting through 2-dimensional tsBts.
135135
// Invariant: all symbols "under" (closer to the first row) the frontier
136136
// are smaller (according to _.isLess) than the ones "on and beyond" the frontier
@@ -145,7 +145,7 @@ private[internal] trait GlbLubs {
145145
}
146146

147147
if (isUniformFrontier) {
148-
val ts1 = elimSub(ts0, depth) map elimHigherOrderTypeParam
148+
val ts1 = elimSub(ts0, depth).map(elimHigherOrderTypeParam)
149149
mergePrefixAndArgs(ts1, Covariant, depth) match {
150150
case NoType =>
151151
case tp => pretypes += tp
@@ -165,11 +165,12 @@ private[internal] trait GlbLubs {
165165
jx += 1
166166
}
167167
if (printLubs) {
168-
val str = baseTypeSeqs.zipWithIndex.map({ case (tps, idx) =>
169-
tps.toList.drop(ices(idx)).map(" " + _ + "\n").mkString(" (" + idx + ")\n", "", "\n")
170-
}).mkString("")
171-
172-
println("Frontier(\n" + str + ")")
168+
println {
169+
baseTypeSeqs.zipWithIndex.map { case (tps, idx) =>
170+
tps.toList.drop(ices(idx)).map(" " + _).mkString(" (" + idx + ")\n", "\n", "\n")
171+
}
172+
.mkString("Frontier(\n", "", ")")
173+
}
173174
printLubMatrixAux(lubListDepth)
174175
}
175176
}
@@ -198,36 +199,57 @@ private[internal] trait GlbLubs {
198199

199200
/** From a list of types, retain only maximal types as determined by the partial order `po`. */
200201
private def maxTypes(ts: List[Type])(po: (Type, Type) => Boolean): List[Type] = {
201-
def loop(ts: List[Type]): List[Type] = ts match {
202+
def stacked(ts: List[Type]): List[Type] = ts match {
202203
case t :: ts1 =>
203-
val ts2 = loop(ts1.filterNot(po(_, t)))
204+
val ts2 = stacked(ts1.filterNot(po(_, t)))
204205
if (ts2.exists(po(t, _))) ts2 else t :: ts2
205206
case Nil => Nil
206207
}
207208

208-
// The order here matters because type variables and
209-
// wildcards can act both as subtypes and supertypes.
210-
val (ts2, ts1) = partitionConserve(ts) { tp =>
211-
isWildCardOrNonGroundTypeVarCollector.collect(tp).isDefined
209+
// loop thru tails, filtering for survivors of po test with the current element, which is saved for later culling
210+
@tailrec
211+
def loop(survivors: List[Type], toCull: List[Type]): List[Type] = survivors match {
212+
case h :: rest =>
213+
loop(rest.filterNot(po(_, h)), h :: toCull)
214+
case _ =>
215+
// unwind the stack of saved elements, accumulating a result containing elements surviving po (in swapped order)
216+
def sieve(res: List[Type], remaining: List[Type]): List[Type] = remaining match {
217+
case h :: tail =>
218+
val res1 = if (res.exists(po(h, _))) res else h :: res
219+
sieve(res1, tail)
220+
case _ => res
221+
}
222+
toCull match {
223+
case _ :: Nil => toCull
224+
case _ => sieve(Nil, toCull)
225+
}
212226
}
213227

214-
loop(ts1 ::: ts2)
228+
// The order here matters because type variables and wildcards can act both as subtypes and supertypes.
229+
val sorted = {
230+
val (wilds, ts1) = partitionConserve(ts)(isWildCardOrNonGroundTypeVarCollector.collect(_).isDefined)
231+
ts1 ::: wilds
232+
}
233+
if (sorted.lengthCompare(5) > 0) loop(sorted, Nil)
234+
else stacked(sorted)
215235
}
216236

217237
/** Eliminate from list of types all elements which are a supertype
218-
* of some other element of the list. */
238+
* of some other element of the list. */
219239
private def elimSuper(ts: List[Type]): List[Type] =
220-
maxTypes(ts)((t1, t2) => t2 <:< t1)
240+
if (ts.lengthCompare(1) <= 0) ts
241+
else maxTypes(ts)((t1, t2) => t2 <:< t1)
221242

222243
/** Eliminate from list of types all elements which are a subtype
223-
* of some other element of the list. */
224-
@tailrec private def elimSub(ts: List[Type], depth: Depth): List[Type] = {
225-
val ts1 = maxTypes(ts)(isSubType(_, _, depth.decr))
226-
if (ts1.lengthCompare(1) <= 0) ts1 else {
227-
val ts2 = ts1.mapConserve(t => elimAnonymousClass(t.dealiasWiden))
228-
if (ts1 eq ts2) ts1 else elimSub(ts2, depth)
244+
* of some other element of the list. */
245+
@tailrec private def elimSub(ts: List[Type], depth: Depth): List[Type] =
246+
if (ts.lengthCompare(1) <= 0) ts else {
247+
val ts1 = maxTypes(ts)(isSubType(_, _, depth.decr))
248+
if (ts1.lengthCompare(1) <= 0) ts1 else {
249+
val ts2 = ts1.mapConserve(t => elimAnonymousClass(t.dealiasWiden))
250+
if (ts1 eq ts2) ts1 else elimSub(ts2, depth)
251+
}
229252
}
230-
}
231253

232254
/** Does this set of types have the same weak lub as
233255
* it does regular lub? This is exposed so lub callers
@@ -491,7 +513,7 @@ private[internal] trait GlbLubs {
491513
val (ts, tparams) = stripExistentialsAndTypeVars(ts0)
492514
val glbOwner = commonOwner(ts)
493515
val ts1 = {
494-
val res = mutable.ListBuffer.empty[Type]
516+
val res = ListBuffer.empty[Type]
495517
def loop(ty: Type): Unit = ty match {
496518
case RefinedType(ps, _) => ps.foreach(loop)
497519
case _ => res += ty
@@ -508,7 +530,7 @@ private[internal] trait GlbLubs {
508530
def glbsym(proto: Symbol): Symbol = {
509531
val prototp = glbThisType.memberInfo(proto)
510532
val symtypes: List[Type] = {
511-
val res = mutable.ListBuffer.empty[Type]
533+
val res = ListBuffer.empty[Type]
512534
ts foreach { t =>
513535
t.nonPrivateMember(proto.name).alternatives foreach { alt =>
514536
val mi = glbThisType.memberInfo(alt)

src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ private[internal] trait TypeConstraints {
2424
import definitions._
2525

2626
/** A log of type variable with their original constraints. Used in order
27-
* to undo constraints in the case of isSubType/isSameType failure.
28-
*/
27+
* to undo constraints in the case of isSubType/isSameType failure.
28+
*/
2929
private lazy val _undoLog = new UndoLog
3030
def undoLog = _undoLog
3131

@@ -54,9 +54,9 @@ private[internal] trait TypeConstraints {
5454
}
5555

5656
/** No sync necessary, because record should only
57-
* be called from within an undo or undoUnless block,
58-
* which is already synchronized.
59-
*/
57+
* be called from within an undo or undoUnless block,
58+
* which is already synchronized.
59+
*/
6060
private[reflect] def record(tv: TypeVar) = {
6161
log ::= UndoPair(tv, tv.constr.cloneInternal)
6262
}
@@ -96,11 +96,11 @@ private[internal] trait TypeConstraints {
9696
*/
9797

9898
/** Guard these lists against AnyClass and NothingClass appearing,
99-
* else loBounds.isEmpty will have different results for an empty
100-
* constraint and one with Nothing as a lower bound. [Actually
101-
* guarding addLoBound/addHiBound somehow broke raw types so it
102-
* only guards against being created with them.]
103-
*/
99+
* else loBounds.isEmpty will have different results for an empty
100+
* constraint and one with Nothing as a lower bound. [Actually
101+
* guarding addLoBound/addHiBound somehow broke raw types so it
102+
* only guards against being created with them.]
103+
*/
104104
private[this] var lobounds = lo0 filterNot (_.isNothing)
105105
private[this] var hibounds = hi0 filterNot (_.isAny)
106106
private[this] var numlo = numlo0
@@ -124,15 +124,21 @@ private[internal] trait TypeConstraints {
124124
// See pos/t6367 and pos/t6499 for the competing test cases.
125125
val mustConsider = tp.typeSymbol match {
126126
case NothingClass => true
127-
case _ => !(lobounds contains tp)
127+
case _ => !lobounds.contains(tp)
128128
}
129129
if (mustConsider) {
130+
def justTwoStrings: Boolean = (
131+
tp.typeSymbol == StringClass && tp.isInstanceOf[ConstantType] &&
132+
lobounds.lengthCompare(1) == 0 && lobounds.head.typeSymbol == StringClass
133+
)
130134
if (isNumericBound && isNumericValueType(tp)) {
131135
if (numlo == NoType || isNumericSubType(numlo, tp))
132136
numlo = tp
133137
else if (!isNumericSubType(tp, numlo))
134138
numlo = numericLoBound
135139
}
140+
else if (justTwoStrings)
141+
lobounds = tp.widen :: Nil // don't accumulate strings; we know they are not exactly the same bc mustConsider
136142
else lobounds ::= tp
137143
}
138144
}
@@ -222,7 +228,7 @@ private[internal] trait TypeConstraints {
222228

223229
@inline def toBound(hi: Boolean, tparam: Symbol) = if (hi) tparam.info.upperBound else tparam.info.lowerBound
224230

225-
def solveOne(tvar: TypeVar, isContravariant: Boolean): Unit = {
231+
def solveOne(tvar: TypeVar, isContravariant: Boolean): Unit =
226232
if (tvar.constr.inst == NoType) {
227233
tvar.constr.inst = null // mark tvar as being solved
228234

@@ -252,7 +258,6 @@ private[internal] trait TypeConstraints {
252258
}
253259
}
254260

255-
256261
if (!(otherTypeVarBeingSolved || containsSymbol(bound, tparam))) {
257262
val boundSym = bound.typeSymbol
258263
if (up) {
@@ -284,7 +289,6 @@ private[internal] trait TypeConstraints {
284289
// debuglog(s"$tvar setInst $newInst")
285290
tvar setInst newInst
286291
}
287-
}
288292

289293
// println("solving "+tvars+"/"+tparams+"/"+(tparams map (_.info)))
290294
foreachWithIndex(tvars)((tvar, i) => solveOne(tvar, areContravariant(i)))

test/files/run/t12757.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
import scala.tools.partest.DirectTest
3+
4+
object Test extends DirectTest {
5+
def header = """|object Test extends App {
6+
| val myStrings: List[String] = List(""".stripMargin.linesIterator
7+
def footer = """| )
8+
| println(myStrings.mkString(","))
9+
|}""".stripMargin.linesIterator
10+
def values = Iterator.tabulate(4000)(i => s" \"$i\",")
11+
def code = (header ++ values ++ footer).mkString("\n")
12+
13+
override def extraSettings: String = "-usejavacp -J-Xms256k"
14+
15+
def show() = assert(compile())
16+
}

test/files/run/t12757b.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
import scala.tools.partest.DirectTest
3+
4+
object Test extends DirectTest {
5+
def header = """|object Test extends App {
6+
| val myInts: List[Int] = List(""".stripMargin.linesIterator
7+
def footer = """| )
8+
| println(myInts.mkString(","))
9+
|}""".stripMargin.linesIterator
10+
def values = Iterator.tabulate(4000)(i => s" $i,")
11+
def code = (header ++ values ++ footer).mkString("\n")
12+
13+
override def extraSettings: String = "-usejavacp -J-Xms256k"
14+
15+
def show() = assert(compile())
16+
}

test/files/run/t12757c.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
import scala.tools.partest.DirectTest
3+
4+
object Test extends DirectTest {
5+
def header = """|object Test extends App {
6+
| val myStrings = List(
7+
| 42,
8+
| Test,
9+
|""".stripMargin.linesIterator
10+
def footer = """| )
11+
| println(myStrings.mkString(","))
12+
|}""".stripMargin.linesIterator
13+
def values = Iterator.tabulate(4000)(i => s" \"$i\",")
14+
def code = (header ++ values ++ footer).mkString("\n")
15+
16+
override def extraSettings: String = "-usejavacp -J-Xms256k"
17+
18+
def show() = assert(compile())
19+
}

0 commit comments

Comments
 (0)