Skip to content

Commit 7b7c055

Browse files
authored
Allow case classes with up to 254 parameters (#16501)
Fixes #16500
2 parents a4a1cbc + a48ba99 commit 7b7c055

File tree

5 files changed

+113
-25
lines changed

5 files changed

+113
-25
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,17 @@ object Decorators {
212212
/** Union on lists seen as sets */
213213
def setUnion (ys: List[T]): List[T] = xs ::: ys.filterNot(xs contains _)
214214

215+
/** Reduce left with `op` as long as list `xs` is not longer than `seqLimit`.
216+
* Otherwise, split list in two half, reduce each, and combine with `op`.
217+
*/
218+
def reduceBalanced(op: (T, T) => T, seqLimit: Int = 100): T =
219+
val len = xs.length
220+
if len > seqLimit then
221+
val (leading, trailing) = xs.splitAt(len / 2)
222+
op(leading.reduceBalanced(op, seqLimit), trailing.reduceBalanced(op, seqLimit))
223+
else
224+
xs.reduceLeft(op)
225+
215226
extension [T, U](xss: List[List[T]])
216227
def nestedMap(f: T => U): List[List[U]] = xss match
217228
case xs :: xss1 => xs.map(f) :: xss1.nestedMap(f)

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

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,23 @@ end RecursionOverflow
9696
*/
9797
// Beware: Since this object is only used when handling a StackOverflow, this code
9898
// cannot consume significant amounts of stack.
99-
object handleRecursive {
99+
object handleRecursive:
100+
inline def underlyingStackOverflowOrNull(exc: Throwable): Throwable | Null =
101+
var e: Throwable | Null = exc
102+
while e != null && !e.isInstanceOf[StackOverflowError] do e = e.getCause
103+
e
104+
100105
def apply(op: String, details: => String, exc: Throwable, weight: Int = 1)(using Context): Nothing =
101-
if (ctx.settings.YnoDecodeStacktraces.value)
106+
if ctx.settings.YnoDecodeStacktraces.value then
102107
throw exc
103-
else
104-
exc match {
105-
case _: RecursionOverflow =>
106-
throw new RecursionOverflow(op, details, exc, weight)
107-
case _ =>
108-
var e: Throwable | Null = exc
109-
while (e != null && !e.isInstanceOf[StackOverflowError]) e = e.getCause
110-
if (e != null) throw new RecursionOverflow(op, details, e, weight)
111-
else throw exc
112-
}
113-
}
108+
else exc match
109+
case _: RecursionOverflow =>
110+
throw new RecursionOverflow(op, details, exc, weight)
111+
case _ =>
112+
val so = underlyingStackOverflowOrNull(exc)
113+
if so != null then throw new RecursionOverflow(op, details, so, weight)
114+
else throw exc
115+
end handleRecursive
114116

115117
/**
116118
* This TypeError signals that completing denot encountered a cycle: it asked for denot.info (or similar),

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@ import collection.mutable
2020
import reporting.{Profile, NoProfile}
2121
import dotty.tools.tasty.TastyFormat.ASTsSection
2222

23+
object TreePickler:
24+
class StackSizeExceeded(val mdef: tpd.MemberDef) extends Exception
2325

2426
class TreePickler(pickler: TastyPickler) {
2527
val buf: TreeBuffer = new TreeBuffer
2628
pickler.newSection(ASTsSection, buf)
2729
import buf._
2830
import pickler.nameBuffer.nameIndex
2931
import tpd._
32+
import TreePickler.*
3033

3134
private val symRefs = Symbols.MutableSymbolMap[Addr](256)
3235
private val forwardSymRefs = Symbols.MutableSymbolMap[List[Addr]]()
@@ -53,7 +56,7 @@ class TreePickler(pickler: TastyPickler) {
5356
def docString(tree: untpd.MemberDef): Option[Comment] =
5457
Option(docStrings.lookup(tree))
5558

56-
private def withLength(op: => Unit) = {
59+
private inline def withLength(inline op: Unit) = {
5760
val lengthAddr = reserveRef(relative = true)
5861
op
5962
fillRef(lengthAddr, currentAddr, relative = true)
@@ -328,16 +331,24 @@ class TreePickler(pickler: TastyPickler) {
328331
registerDef(sym)
329332
writeByte(tag)
330333
val addr = currentAddr
331-
withLength {
332-
pickleName(sym.name)
333-
pickleParams
334-
tpt match {
335-
case _: Template | _: Hole => pickleTree(tpt)
336-
case _ if tpt.isType => pickleTpt(tpt)
334+
try
335+
withLength {
336+
pickleName(sym.name)
337+
pickleParams
338+
tpt match {
339+
case _: Template | _: Hole => pickleTree(tpt)
340+
case _ if tpt.isType => pickleTpt(tpt)
341+
}
342+
pickleTreeUnlessEmpty(rhs)
343+
pickleModifiers(sym, mdef)
337344
}
338-
pickleTreeUnlessEmpty(rhs)
339-
pickleModifiers(sym, mdef)
340-
}
345+
catch
346+
case ex: Throwable =>
347+
if !ctx.settings.YnoDecodeStacktraces.value
348+
&& handleRecursive.underlyingStackOverflowOrNull(ex) != null then
349+
throw StackSizeExceeded(mdef)
350+
else
351+
throw ex
341352
if sym.is(Method) && sym.owner.isClass then
342353
profile.recordMethodSize(sym, currentAddr.index - addr.index, mdef.span)
343354
for
@@ -784,7 +795,17 @@ class TreePickler(pickler: TastyPickler) {
784795

785796
def pickle(trees: List[Tree])(using Context): Unit = {
786797
profile = Profile.current
787-
trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree))
798+
for tree <- trees do
799+
try
800+
if !tree.isEmpty then pickleTree(tree)
801+
catch case ex: StackSizeExceeded =>
802+
report.error(
803+
em"""Recursion limit exceeded while pickling ${ex.mdef}
804+
|in ${ex.mdef.symbol.showLocated}.
805+
|You could try to increase the stacksize using the -Xss JVM option.
806+
|For the unprocessed stack trace, compile with -Yno-decode-stacktraces.""",
807+
ex.mdef.srcPos)
808+
788809
def missing = forwardSymRefs.keysIterator
789810
.map(sym => i"${sym.showLocated} (line ${sym.srcPos.line}) #${sym.id}")
790811
.toList

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
266266
val comparisons = sortedAccessors.map { accessor =>
267267
This(clazz).withSpan(ctx.owner.span.focus).select(accessor).equal(ref(thatAsClazz).select(accessor)) }
268268
var rhs = // this.x == this$0.x && this.y == x$0.y && that.canEqual(this)
269-
if comparisons.isEmpty then Literal(Constant(true)) else comparisons.reduceLeft(_ and _)
269+
if comparisons.isEmpty then Literal(Constant(true)) else comparisons.reduceBalanced(_ and _)
270270
val canEqualMeth = existingDef(defn.Product_canEqual, clazz)
271271
if !clazz.is(Final) || canEqualMeth.exists && !canEqualMeth.is(Synthetic) then
272272
rhs = rhs.and(

tests/pos/i16500.scala

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
type I = Int
2+
case class BigClass254(
3+
udef00:I, udef01:I, udef02:I, udef03:I, udef04:I, udef05:I, udef06:I, udef07:I,
4+
udef08:I, udef09:I, udef0a:I, udef0b:I, udef0c:I, udef0d:I, udef0e:I, udef0f:I,
5+
udef10:I, udef11:I, udef12:I, udef13:I, udef14:I, udef15:I, udef16:I, udef17:I,
6+
udef18:I, udef19:I, udef1a:I, udef1b:I, udef1c:I, udef1d:I, udef1e:I, udef1f:I,
7+
udef20:I, udef21:I, udef22:I, udef23:I, udef24:I, udef25:I, udef26:I, udef27:I,
8+
udef28:I, udef29:I, udef2a:I, udef2b:I, udef2c:I, udef2d:I, udef2e:I, udef2f:I,
9+
udef30:I, udef31:I, udef32:I, udef33:I, udef34:I, udef35:I, udef36:I, udef37:I,
10+
udef38:I, udef39:I, udef3a:I, udef3b:I, udef3c:I, udef3d:I, udef3e:I, udef3f:I,
11+
udef40:I, udef41:I, udef42:I, udef43:I, udef44:I, udef45:I, udef46:I, udef47:I,
12+
udef48:I, udef49:I, udef4a:I, udef4b:I, udef4c:I, udef4d:I, udef4e:I, udef4f:I,
13+
udef50:I, udef51:I, udef52:I, udef53:I, udef54:I, udef55:I, udef56:I, udef57:I,
14+
udef58:I, udef59:I, udef5a:I, udef5b:I, udef5c:I, udef5d:I, udef5e:I, udef5f:I,
15+
udef60:I, udef61:I, udef62:I, udef63:I, udef64:I, udef65:I, udef66:I, udef67:I,
16+
udef68:I, udef69:I, udef6a:I, udef6b:I, udef6c:I, udef6d:I, udef6e:I, udef6f:I,
17+
udef70:I, udef71:I, udef72:I, udef73:I, udef74:I, udef75:I, udef76:I, udef77:I,
18+
udef78:I, udef79:I, udef7a:I, udef7b:I, udef7c:I, udef7d:I, udef7e:I, udef7f:I,
19+
udef80:I, udef81:I, udef82:I, udef83:I, udef84:I, udef85:I, udef86:I, udef87:I,
20+
udef88:I, udef89:I, udef8a:I, udef8b:I, udef8c:I, udef8d:I, udef8e:I, udef8f:I,
21+
udef90:I, udef91:I, udef92:I, udef93:I, udef94:I, udef95:I, udef96:I, udef97:I,
22+
udef98:I, udef99:I, udef9a:I, udef9b:I, udef9c:I, udef9d:I, udef9e:I, udef9f:I,
23+
udefa0:I, udefa1:I, udefa2:I, udefa3:I, udefa4:I, udefa5:I, udefa6:I, udefa7:I,
24+
udefa8:I, udefa9:I, udefaa:I, udefab:I, udefac:I, udefad:I, udefae:I, udefaf:I,
25+
udefb0:I, udefb1:I, udefb2:I, udefb3:I, udefb4:I, udefb5:I, udefb6:I, udefb7:I,
26+
udefb8:I, udefb9:I, udefba:I, udefbb:I, udefbc:I, udefbd:I, udefbe:I, udefbf:I,
27+
udefc0:I, udefc1:I, udefc2:I, udefc3:I, udefc4:I, udefc5:I, udefc6:I, udefc7:I,
28+
udefc8:I, udefc9:I, udefca:I, udefcb:I, udefcc:I, udefcd:I, udefce:I, udefcf:I,
29+
udefd0:I, udefd1:I, udefd2:I, udefd3:I, udefd4:I, udefd5:I, udefd6:I, udefd7:I,
30+
udefd8:I, udefd9:I, udefda:I, udefdb:I, udefdc:I, udefdd:I, udefde:I, udefdf:I,
31+
udefe0:I, udefe1:I, udefe2:I, udefe3:I, udefe4:I, udefe5:I, udefe6:I, udefe7:I,
32+
udefe8:I, udefe9:I, udefea:I, udefeb:I, udefec:I, udefed:I, udefee:I, udefef:I,
33+
udeff0:I, udeff1:I, udeff2:I, udeff3:I, udeff4:I, udeff5:I, udeff6:I, udeff7:I,
34+
udeff8:I, udeff9:I, udeffa:I, udeffb:I, udeffc:I, udeffd:I,
35+
)
36+
@main def Test =
37+
BigClass254(
38+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
49+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54+
)

0 commit comments

Comments
 (0)