@@ -353,26 +353,45 @@ object desugar {
353
353
lazy val creatorExpr = New (classTypeRef, constrVparamss nestedMap refOfDef)
354
354
355
355
// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
356
- // def isDefined = true
357
- // def productArity = N
358
356
// def _1 = this.p1
359
357
// ...
360
358
// def _N = this.pN
361
359
// def copy(p1: T1 = p1: @uncheckedVariance, ...,
362
360
// pN: TN = pN: @uncheckedVariance)(moreParams) =
363
361
// new C[...](p1, ..., pN)(moreParams)
364
362
//
363
+ // Above arity 22 we also synthesize:
364
+ // def productArity = N
365
+ // def productElement(i: Int): Any = i match { ... }
366
+ //
365
367
// Note: copy default parameters need @uncheckedVariance; see
366
368
// neg/t1843-variances.scala for a test case. The test would give
367
369
// two errors without @uncheckedVariance, one of them spurious.
368
- val caseClassMeths =
369
- if (isCaseClass) {
370
- def syntheticProperty (name : TermName , rhs : Tree ) =
371
- DefDef (name, Nil , Nil , TypeTree (), rhs).withMods(synthetic)
370
+ val caseClassMeths = {
371
+ def syntheticProperty (name : TermName , rhs : Tree ) =
372
+ DefDef (name, Nil , Nil , TypeTree (), rhs).withMods(synthetic)
373
+ def productArity = syntheticProperty(nme.productArity, Literal (Constant (arity)))
374
+ def productElement = {
375
+ val param = makeSyntheticParameter(tpt = ref(defn.IntType ))
376
+ // case N => _${N + 1}
377
+ val cases = 0 .until(arity).map { i =>
378
+ CaseDef (Literal (Constant (i)), EmptyTree , Select (This (EmptyTypeIdent ), nme.selectorName(i)))
379
+ }
380
+ val ioob = ref(defn.IndexOutOfBoundsException .typeRef)
381
+ val error = Throw (New (ioob, List (List (Select (refOfDef(param), nme.toString_)))))
382
+ // case _ => throw new IndexOutOfBoundsException(i.toString)
383
+ val defaultCase = CaseDef (untpd.Ident (nme.WILDCARD ), EmptyTree , error)
384
+ val body = Match (refOfDef(param), (cases :+ defaultCase).toList)
385
+ DefDef (nme.productElement, Nil , List (List (param)), TypeTree (defn.AnyType ), body)
386
+ .withMods(synthetic)
387
+ }
388
+ def productElemMeths = {
372
389
val caseParams = constrVparamss.head.toArray
373
- val productElemMeths =
374
- for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name)
375
- yield syntheticProperty(nme.selectorName(i), Select (This (EmptyTypeIdent ), caseParams(i).name))
390
+ for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name)
391
+ yield syntheticProperty(nme.selectorName(i), Select (This (EmptyTypeIdent ), caseParams(i).name))
392
+ }
393
+ def enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind .Class )._1 :: Nil else Nil
394
+ def copyMeths = {
376
395
def isRepeated (tree : Tree ): Boolean = tree match {
377
396
case PostfixOp (_, Ident (nme.raw.STAR )) => true
378
397
case ByNameTypeTree (tree1) => isRepeated(tree1)
@@ -382,38 +401,46 @@ object desugar {
382
401
case ValDef (_, tpt, _) => isRepeated(tpt)
383
402
case _ => false
384
403
})
385
-
386
- val copyMeths =
387
- if (mods.is(Abstract ) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued
388
- else {
389
- def copyDefault (vparam : ValDef ) =
390
- makeAnnotated(" scala.annotation.unchecked.uncheckedVariance" , refOfDef(vparam))
391
- val copyFirstParams = derivedVparamss.head.map(vparam =>
392
- cpy.ValDef (vparam)(rhs = copyDefault(vparam)))
393
- val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
394
- cpy.ValDef (vparam)(rhs = EmptyTree ))
395
- DefDef (nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree (), creatorExpr)
396
- .withMods(synthetic) :: Nil
397
- }
398
-
399
- val enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind .Class )._1 :: Nil else Nil
400
- copyMeths ::: enumTagMeths ::: productElemMeths.toList
404
+ if (mods.is(Abstract ) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued
405
+ else {
406
+ def copyDefault (vparam : ValDef ) =
407
+ makeAnnotated(" scala.annotation.unchecked.uncheckedVariance" , refOfDef(vparam))
408
+ val copyFirstParams = derivedVparamss.head.map(vparam =>
409
+ cpy.ValDef (vparam)(rhs = copyDefault(vparam)))
410
+ val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
411
+ cpy.ValDef (vparam)(rhs = EmptyTree ))
412
+ DefDef (nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree (), creatorExpr)
413
+ .withMods(synthetic) :: Nil
414
+ }
401
415
}
416
+
417
+ // Above MaxTupleArity we extend Product instead of ProductN, in this
418
+ // case we need to synthesise productElement & productArity.
419
+ def largeProductMeths =
420
+ if (arity > Definitions .MaxTupleArity ) productElement :: productArity :: Nil
421
+ else Nil
422
+
423
+ if (isCaseClass)
424
+ largeProductMeths ::: copyMeths ::: enumTagMeths ::: productElemMeths.toList
402
425
else Nil
426
+ }
403
427
404
428
def anyRef = ref(defn.AnyRefAlias .typeRef)
405
429
def productConstr (n : Int ) = {
406
430
val tycon = scalaDot((str.Product + n).toTypeName)
407
431
val targs = constrVparamss.head map (_.tpt)
408
432
if (targs.isEmpty) tycon else AppliedTypeTree (tycon, targs)
409
433
}
434
+ def product =
435
+ if (arity > Definitions .MaxTupleArity ) scalaDot(nme.Product .toTypeName)
436
+ else productConstr(arity)
410
437
411
- // Case classes and case objects get a ProductN parent
438
+ // Case classes and case objects get Product/ ProductN parents
412
439
var parents1 = parents
413
440
if (isEnumCase && parents.isEmpty)
414
441
parents1 = enumClassTypeRef :: Nil
415
- if (mods.is(Case ) && arity <= Definitions . MaxTupleArity )
416
- parents1 = parents1 :+ productConstr(arity) // TODO: This also adds Product0 to caes objects. Do we want that?
442
+ if (mods.is(Case ))
443
+ parents1 = parents1 :+ product // TODO: This also adds Product0 to case objects. Do we want that?
417
444
if (isEnum)
418
445
parents1 = parents1 :+ ref(defn.EnumType )
419
446
0 commit comments