@@ -17,20 +17,22 @@ import scala.language.postfixOps
17
17
18
18
/** Synthetic method implementations for case classes, case objects,
19
19
* and value classes.
20
+ *
20
21
* Selectively added to case classes/objects, unless a non-default
21
22
* implementation already exists:
22
23
* def equals(other: Any): Boolean
23
24
* def hashCode(): Int
24
25
* def canEqual(other: Any): Boolean
25
26
* def toString(): String
27
+ * def productElement(i: Int): Any
26
28
* def productArity: Int
27
29
* def productPrefix: String
30
+ *
28
31
* Special handling:
29
32
* protected def readResolve(): AnyRef
30
33
*
31
34
* Selectively added to value classes, unless a non-default
32
35
* implementation already exists:
33
- *
34
36
* def equals(other: Any): Boolean
35
37
* def hashCode(): Int
36
38
*/
@@ -44,14 +46,13 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
44
46
if (myValueSymbols.isEmpty) {
45
47
myValueSymbols = List (defn.Any_hashCode , defn.Any_equals )
46
48
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 )
48
50
}
49
51
50
52
def valueSymbols (implicit ctx : Context ) = { initSymbols; myValueSymbols }
51
53
def caseSymbols (implicit ctx : Context ) = { initSymbols; myCaseSymbols }
52
54
53
- /** The synthetic methods of the case or value class `clazz`.
54
- */
55
+ /** The synthetic methods of the case or value class `clazz`. */
55
56
def syntheticMethods (clazz : ClassSymbol )(implicit ctx : Context ): List [Tree ] = {
56
57
val clazzType = clazz.typeRef
57
58
lazy val accessors =
@@ -91,25 +92,68 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
91
92
case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head)
92
93
case nme.productArity => vrefss => Literal (Constant (accessors.length))
93
94
case nme.productPrefix => ownName
95
+ case nme.productElement => vrefss => productElementBody(accessors.length, vrefss.head.head)
94
96
}
95
97
ctx.log(s " adding $synthetic to $clazz at ${ctx.phase}" )
96
98
DefDef (synthetic, syntheticRHS(ctx.withOwner(synthetic)))
97
99
}
98
100
99
101
/** The class
100
102
*
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
+ * ```
102
144
*
103
- * gets the equals method:
145
+ * gets the ` equals` method:
104
146
*
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
+ * ```
111
155
*
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.
113
157
*/
114
158
def equalsBody (that : Tree )(implicit ctx : Context ): Tree = {
115
159
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) {
131
175
132
176
/** The class
133
177
*
178
+ * ```
134
179
* class C(x: T) extends AnyVal
180
+ * ```
135
181
*
136
- * gets the hashCode method:
182
+ * gets the ` hashCode` method:
137
183
*
138
- * def hashCode: Int = x.hashCode()
184
+ * ```
185
+ * def hashCode: Int = x.hashCode()
186
+ * ```
139
187
*/
140
188
def valueHashCodeBody (implicit ctx : Context ): Tree = {
141
189
assert(accessors.length == 1 )
@@ -144,17 +192,21 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
144
192
145
193
/** The class
146
194
*
147
- * package p
148
- * case class C(x: T, y: T)
195
+ * ```
196
+ * package p
197
+ * case class C(x: T, y: T)
198
+ * ```
149
199
*
150
- * gets the hashCode method:
200
+ * gets the ` hashCode` method:
151
201
*
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
+ * ```
158
210
*/
159
211
def caseHashCodeBody (implicit ctx : Context ): Tree = {
160
212
val acc = ctx.newSymbol(ctx.owner, " acc" .toTermName, Mutable | Synthetic , defn.IntType , coord = ctx.owner.pos)
@@ -165,7 +217,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
165
217
Block (accDef :: mixes, finish)
166
218
}
167
219
168
- /** The hashCode implementation for given symbol `sym`. */
220
+ /** The ` hashCode` implementation for given symbol `sym`. */
169
221
def hashImpl (sym : Symbol )(implicit ctx : Context ): Tree =
170
222
defn.scalaClassName(sym.info.finalResultType) match {
171
223
case tpnme.Unit | tpnme.Null => Literal (Constant (0 ))
@@ -180,11 +232,15 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
180
232
181
233
/** The class
182
234
*
183
- * case class C(...)
235
+ * ```
236
+ * case class C(...)
237
+ * ```
184
238
*
185
- * gets the canEqual method
239
+ * gets the ` canEqual` method
186
240
*
187
- * def canEqual(that: Any) = that.isInstanceOf[C]
241
+ * ```
242
+ * def canEqual(that: Any) = that.isInstanceOf[C]
243
+ * ```
188
244
*/
189
245
def canEqualBody (that : Tree ): Tree = that.isInstance(clazzType)
190
246
0 commit comments