Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow case classes with up to 254 parameters #16501

Merged
merged 2 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,17 @@ object Decorators {
/** Union on lists seen as sets */
def setUnion (ys: List[T]): List[T] = xs ::: ys.filterNot(xs contains _)

/** Reduce left with `op` as long as list `xs` is not longer than `seqLimit`.
* Otherwise, split list in two half, reduce each, and combine with `op`.
*/
def reduceBalanced(op: (T, T) => T, seqLimit: Int = 100): T =
val len = xs.length
if len > seqLimit then
val (leading, trailing) = xs.splitAt(len / 2)
op(leading.reduceBalanced(op, seqLimit), trailing.reduceBalanced(op, seqLimit))
else
xs.reduceLeft(op)

extension [T, U](xss: List[List[T]])
def nestedMap(f: T => U): List[List[U]] = xss match
case xs :: xss1 => xs.map(f) :: xss1.nestedMap(f)
Expand Down
28 changes: 15 additions & 13 deletions compiler/src/dotty/tools/dotc/core/TypeErrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,21 +96,23 @@ end RecursionOverflow
*/
// Beware: Since this object is only used when handling a StackOverflow, this code
// cannot consume significant amounts of stack.
object handleRecursive {
object handleRecursive:
inline def underlyingStackOverflowOrNull(exc: Throwable): Throwable | Null =
var e: Throwable | Null = exc
while e != null && !e.isInstanceOf[StackOverflowError] do e = e.getCause
e

def apply(op: String, details: => String, exc: Throwable, weight: Int = 1)(using Context): Nothing =
if (ctx.settings.YnoDecodeStacktraces.value)
if ctx.settings.YnoDecodeStacktraces.value then
throw exc
else
exc match {
case _: RecursionOverflow =>
throw new RecursionOverflow(op, details, exc, weight)
case _ =>
var e: Throwable | Null = exc
while (e != null && !e.isInstanceOf[StackOverflowError]) e = e.getCause
if (e != null) throw new RecursionOverflow(op, details, e, weight)
else throw exc
}
}
else exc match
case _: RecursionOverflow =>
throw new RecursionOverflow(op, details, exc, weight)
case _ =>
val so = underlyingStackOverflowOrNull(exc)
if so != null then throw new RecursionOverflow(op, details, so, weight)
else throw exc
end handleRecursive

/**
* This TypeError signals that completing denot encountered a cycle: it asked for denot.info (or similar),
Expand Down
43 changes: 32 additions & 11 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ import collection.mutable
import reporting.{Profile, NoProfile}
import dotty.tools.tasty.TastyFormat.ASTsSection

object TreePickler:
class StackSizeExceeded(val mdef: tpd.MemberDef) extends Exception

class TreePickler(pickler: TastyPickler) {
val buf: TreeBuffer = new TreeBuffer
pickler.newSection(ASTsSection, buf)
import buf._
import pickler.nameBuffer.nameIndex
import tpd._
import TreePickler.*

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

private def withLength(op: => Unit) = {
private inline def withLength(inline op: Unit) = {
val lengthAddr = reserveRef(relative = true)
op
fillRef(lengthAddr, currentAddr, relative = true)
Expand Down Expand Up @@ -328,16 +331,24 @@ class TreePickler(pickler: TastyPickler) {
registerDef(sym)
writeByte(tag)
val addr = currentAddr
withLength {
pickleName(sym.name)
pickleParams
tpt match {
case _: Template | _: Hole => pickleTree(tpt)
case _ if tpt.isType => pickleTpt(tpt)
try
withLength {
pickleName(sym.name)
pickleParams
tpt match {
case _: Template | _: Hole => pickleTree(tpt)
case _ if tpt.isType => pickleTpt(tpt)
}
pickleTreeUnlessEmpty(rhs)
pickleModifiers(sym, mdef)
}
pickleTreeUnlessEmpty(rhs)
pickleModifiers(sym, mdef)
}
catch
case ex: Throwable =>
if !ctx.settings.YnoDecodeStacktraces.value
&& handleRecursive.underlyingStackOverflowOrNull(ex) != null then
throw StackSizeExceeded(mdef)
else
throw ex
if sym.is(Method) && sym.owner.isClass then
profile.recordMethodSize(sym, currentAddr.index - addr.index, mdef.span)
for
Expand Down Expand Up @@ -784,7 +795,17 @@ class TreePickler(pickler: TastyPickler) {

def pickle(trees: List[Tree])(using Context): Unit = {
profile = Profile.current
trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree))
for tree <- trees do
try
if !tree.isEmpty then pickleTree(tree)
catch case ex: StackSizeExceeded =>
report.error(
em"""Recursion limit exceeded while pickling ${ex.mdef}
|in ${ex.mdef.symbol.showLocated}.
|You could try to increase the stacksize using the -Xss JVM option.
|For the unprocessed stack trace, compile with -Yno-decode-stacktraces.""",
ex.mdef.srcPos)

def missing = forwardSymRefs.keysIterator
.map(sym => i"${sym.showLocated} (line ${sym.srcPos.line}) #${sym.id}")
.toList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
val comparisons = sortedAccessors.map { accessor =>
This(clazz).withSpan(ctx.owner.span.focus).select(accessor).equal(ref(thatAsClazz).select(accessor)) }
var rhs = // this.x == this$0.x && this.y == x$0.y && that.canEqual(this)
if comparisons.isEmpty then Literal(Constant(true)) else comparisons.reduceLeft(_ and _)
if comparisons.isEmpty then Literal(Constant(true)) else comparisons.reduceBalanced(_ and _)
val canEqualMeth = existingDef(defn.Product_canEqual, clazz)
if !clazz.is(Final) || canEqualMeth.exists && !canEqualMeth.is(Synthetic) then
rhs = rhs.and(
Expand Down
54 changes: 54 additions & 0 deletions tests/pos/i16500.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
type I = Int
case class BigClass254(
udef00:I, udef01:I, udef02:I, udef03:I, udef04:I, udef05:I, udef06:I, udef07:I,
udef08:I, udef09:I, udef0a:I, udef0b:I, udef0c:I, udef0d:I, udef0e:I, udef0f:I,
udef10:I, udef11:I, udef12:I, udef13:I, udef14:I, udef15:I, udef16:I, udef17:I,
udef18:I, udef19:I, udef1a:I, udef1b:I, udef1c:I, udef1d:I, udef1e:I, udef1f:I,
udef20:I, udef21:I, udef22:I, udef23:I, udef24:I, udef25:I, udef26:I, udef27:I,
udef28:I, udef29:I, udef2a:I, udef2b:I, udef2c:I, udef2d:I, udef2e:I, udef2f:I,
udef30:I, udef31:I, udef32:I, udef33:I, udef34:I, udef35:I, udef36:I, udef37:I,
udef38:I, udef39:I, udef3a:I, udef3b:I, udef3c:I, udef3d:I, udef3e:I, udef3f:I,
udef40:I, udef41:I, udef42:I, udef43:I, udef44:I, udef45:I, udef46:I, udef47:I,
udef48:I, udef49:I, udef4a:I, udef4b:I, udef4c:I, udef4d:I, udef4e:I, udef4f:I,
udef50:I, udef51:I, udef52:I, udef53:I, udef54:I, udef55:I, udef56:I, udef57:I,
udef58:I, udef59:I, udef5a:I, udef5b:I, udef5c:I, udef5d:I, udef5e:I, udef5f:I,
udef60:I, udef61:I, udef62:I, udef63:I, udef64:I, udef65:I, udef66:I, udef67:I,
udef68:I, udef69:I, udef6a:I, udef6b:I, udef6c:I, udef6d:I, udef6e:I, udef6f:I,
udef70:I, udef71:I, udef72:I, udef73:I, udef74:I, udef75:I, udef76:I, udef77:I,
udef78:I, udef79:I, udef7a:I, udef7b:I, udef7c:I, udef7d:I, udef7e:I, udef7f:I,
udef80:I, udef81:I, udef82:I, udef83:I, udef84:I, udef85:I, udef86:I, udef87:I,
udef88:I, udef89:I, udef8a:I, udef8b:I, udef8c:I, udef8d:I, udef8e:I, udef8f:I,
udef90:I, udef91:I, udef92:I, udef93:I, udef94:I, udef95:I, udef96:I, udef97:I,
udef98:I, udef99:I, udef9a:I, udef9b:I, udef9c:I, udef9d:I, udef9e:I, udef9f:I,
udefa0:I, udefa1:I, udefa2:I, udefa3:I, udefa4:I, udefa5:I, udefa6:I, udefa7:I,
udefa8:I, udefa9:I, udefaa:I, udefab:I, udefac:I, udefad:I, udefae:I, udefaf:I,
udefb0:I, udefb1:I, udefb2:I, udefb3:I, udefb4:I, udefb5:I, udefb6:I, udefb7:I,
udefb8:I, udefb9:I, udefba:I, udefbb:I, udefbc:I, udefbd:I, udefbe:I, udefbf:I,
udefc0:I, udefc1:I, udefc2:I, udefc3:I, udefc4:I, udefc5:I, udefc6:I, udefc7:I,
udefc8:I, udefc9:I, udefca:I, udefcb:I, udefcc:I, udefcd:I, udefce:I, udefcf:I,
udefd0:I, udefd1:I, udefd2:I, udefd3:I, udefd4:I, udefd5:I, udefd6:I, udefd7:I,
udefd8:I, udefd9:I, udefda:I, udefdb:I, udefdc:I, udefdd:I, udefde:I, udefdf:I,
udefe0:I, udefe1:I, udefe2:I, udefe3:I, udefe4:I, udefe5:I, udefe6:I, udefe7:I,
udefe8:I, udefe9:I, udefea:I, udefeb:I, udefec:I, udefed:I, udefee:I, udefef:I,
udeff0:I, udeff1:I, udeff2:I, udeff3:I, udeff4:I, udeff5:I, udeff6:I, udeff7:I,
udeff8:I, udeff9:I, udeffa:I, udeffb:I, udeffc:I, udeffd:I,
)
@main def Test =
BigClass254(
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
)