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