@@ -17,20 +17,22 @@ import scala.language.postfixOps
1717
1818/** Synthetic method implementations for case classes, case objects,
1919 * and value classes.
20+ *
2021 * Selectively added to case classes/objects, unless a non-default
2122 * implementation already exists:
2223 * def equals(other: Any): Boolean
2324 * def hashCode(): Int
2425 * def canEqual(other: Any): Boolean
2526 * def toString(): String
27+ * def productElement(i: Int): Any
2628 * def productArity: Int
2729 * def productPrefix: String
30+ *
2831 * Special handling:
2932 * protected def readResolve(): AnyRef
3033 *
3134 * Selectively added to value classes, unless a non-default
3235 * implementation already exists:
33- *
3436 * def equals(other: Any): Boolean
3537 * def hashCode(): Int
3638 */
@@ -44,14 +46,13 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
4446 if (myValueSymbols.isEmpty) {
4547 myValueSymbols = List (defn.Any_hashCode , defn.Any_equals )
4648 myCaseSymbols = myValueSymbols ++ List (defn.Any_toString , defn.Product_canEqual ,
47- defn.Product_productArity , defn.Product_productPrefix )
49+ defn.Product_productArity , defn.Product_productPrefix , defn. Product_productElement )
4850 }
4951
5052 def valueSymbols (implicit ctx : Context ) = { initSymbols; myValueSymbols }
5153 def caseSymbols (implicit ctx : Context ) = { initSymbols; myCaseSymbols }
5254
53- /** The synthetic methods of the case or value class `clazz`.
54- */
55+ /** The synthetic methods of the case or value class `clazz`. */
5556 def syntheticMethods (clazz : ClassSymbol )(implicit ctx : Context ): List [Tree ] = {
5657 val clazzType = clazz.typeRef
5758 lazy val accessors =
@@ -91,25 +92,68 @@ 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
99101 /** The class
100102 *
101- * case class C(x: T, y: U)
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+ // Second constructor of ioob that takes a String argument
120+ def filterStringConstructor (s : Symbol ): Boolean = s.info match {
121+ case m : MethodType if s.isConstructor => m.paramInfos == List (defn.StringType )
122+ case _ => false
123+ }
124+ val constructor = ioob.typeSymbol.info.decls.find(filterStringConstructor _).asTerm
125+ val stringIndex = Apply (Select (index, nme.toString_), Nil )
126+ val error = Throw (New (ioob, constructor, List (stringIndex)))
127+
128+ // case _ => throw new IndexOutOfBoundsException(i.toString)
129+ val defaultCase = CaseDef (Underscore (defn.IntType ), EmptyTree , error)
130+
131+ // case N => _${N + 1}
132+ val cases = 0 .until(arity).map { i =>
133+ CaseDef (Literal (Constant (i)), EmptyTree , Select (This (clazz), nme.selectorName(i)))
134+ }
135+
136+ Match (index, (cases :+ defaultCase).toList)
137+ }
138+
139+ /** The class
140+ *
141+ * ```
142+ * case class C(x: T, y: U)
143+ * ```
102144 *
103- * gets the equals method:
145+ * gets the ` equals` method:
104146 *
105- * def equals(that: Any): Boolean =
106- * (this eq that) || {
107- * that match {
108- * case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y
109- * case _ => false
110- * }
147+ * ```
148+ * def equals(that: Any): Boolean =
149+ * (this eq that) || {
150+ * that match {
151+ * case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y
152+ * case _ => false
153+ * }
154+ * ```
111155 *
112- * If C is a value class the initial `eq` test is omitted.
156+ * If `C` is a value class the initial `eq` test is omitted.
113157 */
114158 def equalsBody (that : Tree )(implicit ctx : Context ): Tree = {
115159 val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic , clazzType, coord = ctx.owner.pos) // x$0
@@ -131,11 +175,15 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
131175
132176 /** The class
133177 *
178+ * ```
134179 * class C(x: T) extends AnyVal
180+ * ```
135181 *
136- * gets the hashCode method:
182+ * gets the ` hashCode` method:
137183 *
138- * def hashCode: Int = x.hashCode()
184+ * ```
185+ * def hashCode: Int = x.hashCode()
186+ * ```
139187 */
140188 def valueHashCodeBody (implicit ctx : Context ): Tree = {
141189 assert(accessors.length == 1 )
@@ -144,17 +192,21 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
144192
145193 /** The class
146194 *
147- * package p
148- * case class C(x: T, y: T)
195+ * ```
196+ * package p
197+ * case class C(x: T, y: T)
198+ * ```
149199 *
150- * gets the hashCode method:
200+ * gets the ` hashCode` method:
151201 *
152- * def hashCode: Int = {
153- * <synthetic> var acc: Int = "p.C".hashCode // constant folded
154- * acc = Statics.mix(acc, x);
155- * acc = Statics.mix(acc, Statics.this.anyHash(y));
156- * Statics.finalizeHash(acc, 2)
157- * }
202+ * ```
203+ * def hashCode: Int = {
204+ * <synthetic> var acc: Int = "p.C".hashCode // constant folded
205+ * acc = Statics.mix(acc, x);
206+ * acc = Statics.mix(acc, Statics.this.anyHash(y));
207+ * Statics.finalizeHash(acc, 2)
208+ * }
209+ * ```
158210 */
159211 def caseHashCodeBody (implicit ctx : Context ): Tree = {
160212 val acc = ctx.newSymbol(ctx.owner, " acc" .toTermName, Mutable | Synthetic , defn.IntType , coord = ctx.owner.pos)
@@ -165,7 +217,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
165217 Block (accDef :: mixes, finish)
166218 }
167219
168- /** The hashCode implementation for given symbol `sym`. */
220+ /** The ` hashCode` implementation for given symbol `sym`. */
169221 def hashImpl (sym : Symbol )(implicit ctx : Context ): Tree =
170222 defn.scalaClassName(sym.info.finalResultType) match {
171223 case tpnme.Unit | tpnme.Null => Literal (Constant (0 ))
@@ -180,11 +232,15 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
180232
181233 /** The class
182234 *
183- * case class C(...)
235+ * ```
236+ * case class C(...)
237+ * ```
184238 *
185- * gets the canEqual method
239+ * gets the ` canEqual` method
186240 *
187- * def canEqual(that: Any) = that.isInstanceOf[C]
241+ * ```
242+ * def canEqual(that: Any) = that.isInstanceOf[C]
243+ * ```
188244 */
189245 def canEqualBody (that : Tree ): Tree = that.isInstance(clazzType)
190246
0 commit comments