Skip to content

Commit c53bdae

Browse files
Extend ProductN for N <= 22, Product above that
1 parent 8545df6 commit c53bdae

File tree

2 files changed

+69
-14
lines changed

2 files changed

+69
-14
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -333,27 +333,24 @@ object desugar {
333333
lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef)
334334

335335
// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
336-
// def productArity = N
337-
// def productElement(i: Int): Any = i match { ... }
338336
// def _1 = this.p1
339337
// ...
340338
// def _N = this.pN
341339
// def copy(p1: T1 = p1: @uncheckedVariance, ...,
342340
// pN: TN = pN: @uncheckedVariance)(moreParams) =
343341
// new C[...](p1, ..., pN)(moreParams)
344342
//
343+
// Above arity 22 we also synthesize:
344+
// def productArity = N
345+
// def productElement(i: Int): Any = i match { ... }
346+
//
345347
// Note: copy default parameters need @uncheckedVariance; see
346348
// neg/t1843-variances.scala for a test case. The test would give
347349
// two errors without @uncheckedVariance, one of them spurious.
348350
val caseClassMeths = {
349351
def syntheticProperty(name: TermName, rhs: Tree) =
350352
DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic)
351-
// The override here is less than ideal: user defined productArity / productElement
352-
// methods would be silently ignored. This is necessary to compile `scala.TupleN`.
353-
// The long term solution is to remove `ProductN` entirely from stdlib.
354-
def productArity =
355-
DefDef(nme.productArity, Nil, Nil, TypeTree(), Literal(Constant(arity)))
356-
.withMods(Modifiers(Synthetic | Override))
353+
def productArity = syntheticProperty(nme.productArity, Literal(Constant(arity)))
357354
def productElement = {
358355
val param = makeSyntheticParameter(tpt = ref(defn.IntType))
359356
// case N => _${N + 1}
@@ -366,7 +363,7 @@ object desugar {
366363
val defaultCase = CaseDef(untpd.Ident(nme.WILDCARD), EmptyTree, error)
367364
val body = Match(refOfDef(param), (cases :+ defaultCase).toList)
368365
DefDef(nme.productElement, Nil, List(List(param)), TypeTree(defn.AnyType), body)
369-
.withMods(Modifiers(Synthetic | Override))
366+
.withMods(synthetic)
370367
}
371368
def productElemMeths = {
372369
val caseParams = constrVparamss.head.toArray
@@ -396,18 +393,31 @@ object desugar {
396393
}
397394
}
398395

396+
// Above MaxTupleArity we extend Product instead of ProductN, in this
397+
// case we need to synthesise productElement & productArity.
398+
def largeProductMeths =
399+
if (arity > Definitions.MaxTupleArity) List(productElement, productArity)
400+
else Nil
401+
399402
if (isCaseClass)
400-
productElement :: productArity :: copyMeths ::: productElemMeths.toList
401-
else if (isCaseObject)
402-
productElement :: productArity :: Nil
403+
largeProductMeths ::: copyMeths ::: productElemMeths.toList
403404
else Nil
404405
}
405406

406407
def anyRef = ref(defn.AnyRefAlias.typeRef)
408+
def productConstr(n: Int) = {
409+
val tycon = scalaDot((tpnme.Product.toString + n).toTypeName)
410+
val targs = constrVparamss.head map (_.tpt)
411+
if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs)
412+
}
413+
def product =
414+
if (arity > Definitions.MaxTupleArity) scalaDot(nme.Product.toTypeName)
415+
else productConstr(arity)
407416

408-
// Case classes and case objects get NameBasedPattern and Product parents
417+
// Case classes and case objects get NameBasedPattern and Product/ProductN parents
409418
val parents1: List[Tree] =
410-
if (mods.is(Case)) parents :+ scalaDot(nme.Product.toTypeName) :+ scalaDot(nme.NameBasedPattern.toTypeName)
419+
if (mods.is(Case))
420+
parents :+ product :+ scalaDot(nme.NameBasedPattern.toTypeName)
411421
else parents
412422

413423
// The thicket which is the desugared version of the companion object

tests/run/1938.scala

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
case class Large(
2+
e1: Int,
3+
e2: Int,
4+
e3: Int,
5+
e4: Int,
6+
e5: Int,
7+
e6: Int,
8+
e7: Int,
9+
e8: Int,
10+
e9: Int,
11+
e10: Int,
12+
e11: Int,
13+
e12: Int,
14+
e13: Int,
15+
e14: Int,
16+
e15: Int,
17+
e16: Int,
18+
e17: Int,
19+
e18: Int,
20+
e19: Int,
21+
e20: Int,
22+
e21: Int,
23+
e22: Int,
24+
e23: Int
25+
)
26+
27+
object Main {
28+
def main(args: Array[String]): Unit = {
29+
val l = Large(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
30+
31+
assert(l.productArity == 23)
32+
33+
assert(l.productElement(0) == 1)
34+
assert(l.productElement(1) == 2)
35+
assert(l.productElement(21) == 22)
36+
assert(l.productElement(22) == 23)
37+
38+
try {
39+
l.productElement(23)
40+
???
41+
} catch {
42+
case e: IndexOutOfBoundsException => assert(e.getMessage == "23")
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)