Skip to content

Commit 25b8b6e

Browse files
Synthesize productElement in SyntheticMethods instead of Desugaring
The implementation needed changes since Desugaring uses `untpd._` while SyntheticMethods uses `tpd._`. `productArity` is also removed from Desugaring, it's already implement in SyntheticMethods, so no changes there.
1 parent f327b3e commit 25b8b6e

File tree

3 files changed

+42
-26
lines changed

3 files changed

+42
-26
lines changed

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

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ object desugar {
2727

2828
/** Names of methods that are added unconditionally to case classes */
2929
def isDesugaredCaseClassMethodName(name: Name)(implicit ctx: Context): Boolean =
30-
name == nme.copy ||
31-
name == nme.productArity ||
32-
name.isSelectorName
30+
name == nme.copy || name.isSelectorName
3331

3432
// ----- DerivedTypeTrees -----------------------------------
3533

@@ -361,31 +359,12 @@ object desugar {
361359
// pN: TN = pN: @uncheckedVariance)(moreParams) =
362360
// new C[...](p1, ..., pN)(moreParams)
363361
//
364-
// To add to both case classes and objects
365-
// def productArity = N
366-
// def productElement(i: Int): Any = i match { ... }
367-
//
368362
// Note: copy default parameters need @uncheckedVariance; see
369363
// neg/t1843-variances.scala for a test case. The test would give
370364
// two errors without @uncheckedVariance, one of them spurious.
371365
val caseClassMeths = {
372366
def syntheticProperty(name: TermName, rhs: Tree) =
373367
DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic)
374-
def productArity = syntheticProperty(nme.productArity, Literal(Constant(arity)))
375-
def productElement = {
376-
val param = makeSyntheticParameter(tpt = ref(defn.IntType))
377-
// case N => _${N + 1}
378-
val cases = 0.until(arity).map { i =>
379-
CaseDef(Literal(Constant(i)), EmptyTree, Select(This(EmptyTypeIdent), nme.selectorName(i)))
380-
}
381-
val ioob = ref(defn.IndexOutOfBoundsException.typeRef)
382-
val error = Throw(New(ioob, List(List(Select(refOfDef(param), nme.toString_)))))
383-
// case _ => throw new IndexOutOfBoundsException(i.toString)
384-
val defaultCase = CaseDef(untpd.Ident(nme.WILDCARD), EmptyTree, error)
385-
val body = Match(refOfDef(param), (cases :+ defaultCase).toList)
386-
DefDef(nme.productElement, Nil, List(List(param)), TypeTree(defn.AnyType), body)
387-
.withMods(synthetic)
388-
}
389368
def productElemMeths = {
390369
val caseParams = constrVparamss.head.toArray
391370
for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name)
@@ -416,9 +395,7 @@ object desugar {
416395
}
417396

418397
if (isCaseClass)
419-
productElement :: productArity :: copyMeths ::: enumTagMeths ::: productElemMeths.toList
420-
else if (isCaseObject)
421-
productArity :: productElement :: Nil
398+
copyMeths ::: enumTagMeths ::: productElemMeths.toList
422399
else Nil
423400
}
424401

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,8 @@ class Definitions {
525525
def Product_canEqual(implicit ctx: Context) = Product_canEqualR.symbol
526526
lazy val Product_productArityR = ProductClass.requiredMethodRef(nme.productArity)
527527
def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol
528+
lazy val Product_productElementR = ProductClass.requiredMethodRef(nme.productElement)
529+
def Product_productElement(implicit ctx: Context) = Product_productElementR.symbol
528530
lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix)
529531
def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol
530532
lazy val LanguageModuleRef = ctx.requiredModule("scala.language")

compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import scala.language.postfixOps
2323
* def hashCode(): Int
2424
* def canEqual(other: Any): Boolean
2525
* def toString(): String
26+
* def productElement(i: Int): Any
2627
* def productArity: Int
2728
* def productPrefix: String
2829
* Special handling:
@@ -44,7 +45,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
4445
if (myValueSymbols.isEmpty) {
4546
myValueSymbols = List(defn.Any_hashCode, defn.Any_equals)
4647
myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual,
47-
defn.Product_productArity, defn.Product_productPrefix)
48+
defn.Product_productArity, defn.Product_productPrefix, defn.Product_productElement)
4849
}
4950

5051
def valueSymbols(implicit ctx: Context) = { initSymbols; myValueSymbols }
@@ -91,11 +92,47 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
9192
case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head)
9293
case nme.productArity => vrefss => Literal(Constant(accessors.length))
9394
case nme.productPrefix => ownName
95+
case nme.productElement => vrefss => productElementBody(accessors.length, vrefss.head.head)
9496
}
9597
ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}")
9698
DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic)))
9799
}
98100

101+
/** The class
102+
*
103+
* ```
104+
* case class C(x: T, y: T)
105+
* ```
106+
*
107+
* gets the `productElement` method:
108+
*
109+
* ```
110+
* def productElement(index: Int): Any = index match {
111+
* case 0 => this._1
112+
* case 1 => this._2
113+
* case _ => throw new IndexOutOfBoundsException(index.toString)
114+
* }
115+
* ```
116+
*/
117+
def productElementBody(arity: Int, index: Tree)(implicit ctx: Context): Tree = {
118+
val ioob = defn.IndexOutOfBoundsException.typeRef
119+
// That's not ioob.typeSymbol.primaryConstructor, this is the other one
120+
// that takes a String argument.
121+
val constructor = ioob.typeSymbol.info.decls.toList.tail.head.asTerm
122+
val stringIndex = Apply(Select(index, nme.toString_), Nil)
123+
val error = Throw(New(ioob, constructor, List(stringIndex)))
124+
125+
// case _ => throw new IndexOutOfBoundsException(i.toString)
126+
val defaultCase = CaseDef(Underscore(defn.IntType), EmptyTree, error)
127+
128+
// case N => _${N + 1}
129+
val cases = 0.until(arity).map { i =>
130+
CaseDef(Literal(Constant(i)), EmptyTree, Select(This(clazz), nme.selectorName(i)))
131+
}
132+
133+
Match(index, (cases :+ defaultCase).toList)
134+
}
135+
99136
/** The class
100137
*
101138
* case class C(x: T, y: U)

0 commit comments

Comments
 (0)