Skip to content

Commit c0ff8ad

Browse files
Generate synthetic productElement/productArity methods above 22
1 parent 0ad5956 commit c0ff8ad

File tree

2 files changed

+56
-28
lines changed

2 files changed

+56
-28
lines changed

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

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -352,26 +352,45 @@ object desugar {
352352
lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef)
353353

354354
// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
355-
// def isDefined = true
356-
// def productArity = N
357355
// def _1 = this.p1
358356
// ...
359357
// def _N = this.pN
360358
// def copy(p1: T1 = p1: @uncheckedVariance, ...,
361359
// pN: TN = pN: @uncheckedVariance)(moreParams) =
362360
// new C[...](p1, ..., pN)(moreParams)
363361
//
362+
// Above arity 22 we also synthesize:
363+
// def productArity = N
364+
// def productElement(i: Int): Any = i match { ... }
365+
//
364366
// Note: copy default parameters need @uncheckedVariance; see
365367
// neg/t1843-variances.scala for a test case. The test would give
366368
// 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 = {
371388
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 = {
375394
def isRepeated(tree: Tree): Boolean = tree match {
376395
case PostfixOp(_, Ident(nme.raw.STAR)) => true
377396
case ByNameTypeTree(tree1) => isRepeated(tree1)
@@ -381,38 +400,46 @@ object desugar {
381400
case ValDef(_, tpt, _) => isRepeated(tpt)
382401
case _ => false
383402
})
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+
}
400414
}
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
401424
else Nil
425+
}
402426

403427
def anyRef = ref(defn.AnyRefAlias.typeRef)
404428
def productConstr(n: Int) = {
405429
val tycon = scalaDot((tpnme.Product.toString + n).toTypeName)
406430
val targs = constrVparamss.head map (_.tpt)
407431
if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs)
408432
}
433+
def product =
434+
if (arity > Definitions.MaxTupleArity) scalaDot(nme.Product.toTypeName)
435+
else productConstr(arity)
409436

410-
// Case classes and case objects get a ProductN parent
437+
// Case classes and case objects get Product/ProductN parents
411438
var parents1 = parents
412439
if (isEnumCase && parents.isEmpty)
413440
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?
416443
if (isEnum)
417444
parents1 = parents1 :+ ref(defn.EnumType)
418445

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ class Definitions {
477477

478478
lazy val JavaCloneableClass = ctx.requiredClass("java.lang.Cloneable")
479479
lazy val NullPointerExceptionClass = ctx.requiredClass("java.lang.NullPointerException")
480+
lazy val IndexOutOfBoundsException = ctx.requiredClass("java.lang.IndexOutOfBoundsException")
480481
lazy val ClassClass = ctx.requiredClass("java.lang.Class")
481482
lazy val BoxedNumberClass = ctx.requiredClass("java.lang.Number")
482483
lazy val ThrowableClass = ctx.requiredClass("java.lang.Throwable")

0 commit comments

Comments
 (0)