@@ -32,6 +32,8 @@ trait Deriving { this: Typer =>
32
32
/** A buffer for synthesized symbols */
33
33
private var synthetics = new mutable.ListBuffer [Symbol ]
34
34
35
+ private var derivesGeneric = false
36
+
35
37
/** the children of `cls` ordered by textual occurrence */
36
38
lazy val children : List [Symbol ] = cls.children
37
39
@@ -159,33 +161,67 @@ trait Deriving { this: Typer =>
159
161
*
160
162
* implicit def derived$D(implicit ev_1: D[T_1], ..., ev_n: D[T_n]): D[C[Ts]] = D.derived
161
163
*
162
- * See test run/typeclass-derivation2 for examples that spell out what would be generated.
163
- * Note that the name of the derived method containd the name in the derives clause, not
164
+ * See the body of this method for how to generalize this to typeclasses with more
165
+ * or less than one type parameter.
166
+ *
167
+ * See test run/typeclass-derivation2 and run/derive-multi
168
+ * for examples that spell out what would be generated.
169
+ *
170
+ * Note that the name of the derived method contains the name in the derives clause, not
164
171
* the underlying class name. This allows one to disambiguate derivations of type classes
165
172
* that have the same name but different prefixes through selective aliasing.
166
173
*/
167
174
private def processDerivedInstance (derived : untpd.Tree ): Unit = {
168
175
val originalType = typedAheadType(derived, AnyTypeConstructorProto ).tpe
169
176
val underlyingType = underlyingClassRef(originalType)
170
177
val derivedType = checkClassType(underlyingType, derived.sourcePos, traitReq = false , stablePrefixReq = true )
171
- val nparams = derivedType.classSymbol.typeParams.length
178
+ val typeClass = derivedType.classSymbol
179
+ val nparams = typeClass.typeParams.length
172
180
if (derivedType.isRef(defn.GenericClass ))
173
- () // do nothing, a Generic instance will be created anyway by `addGeneric`
174
- else if (nparams == 1 ) {
175
- val typeClass = derivedType.classSymbol
176
- val firstKindedParams = cls.typeParams.filterNot(_.info.isLambdaSub)
181
+ derivesGeneric = true
182
+ else {
183
+ // A matrix of all parameter combinations of current class parameters
184
+ // and derived typeclass parameters.
185
+ // Rows: parameters of current class
186
+ // Columns: parameters of typeclass
187
+
188
+ // Running example: typeclass: class TC[X, Y, Z], deriving class: class A[T, U]
189
+ // clsParamss =
190
+ // T_X T_Y T_Z
191
+ // U_X U_Y U_Z
192
+ val clsParamss : List [List [TypeSymbol ]] = cls.typeParams.map { tparam =>
193
+ if (nparams == 0 ) Nil
194
+ else if (nparams == 1 ) tparam :: Nil
195
+ else typeClass.typeParams.map(tcparam =>
196
+ tparam.copy(name = s " ${tparam.name}_ ${tcparam.name}" .toTypeName)
197
+ .asInstanceOf [TypeSymbol ])
198
+ }
199
+ val firstKindedParamss = clsParamss.filter {
200
+ case param :: _ => ! param.info.isLambdaSub
201
+ case nil => false
202
+ }
203
+
204
+ // The types of the required evidence parameters. In the running example:
205
+ // TC[T_X, T_Y, T_Z], TC[U_X, U_Y, U_Z]
177
206
val evidenceParamInfos =
178
- for (param <- firstKindedParams) yield derivedType.appliedTo(param.typeRef)
179
- val resultType = derivedType.appliedTo(cls.appliedRef)
207
+ for (row <- firstKindedParamss)
208
+ yield derivedType.appliedTo(row.map(_.typeRef))
209
+
210
+ // The class instances in the result type. Running example:
211
+ // A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z]
212
+ val resultInstances =
213
+ for (n <- List .range(0 , nparams))
214
+ yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef))
215
+
216
+ // TC[A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z]]
217
+ val resultType = derivedType.appliedTo(resultInstances)
218
+
219
+ val clsParams : List [TypeSymbol ] = clsParamss.flatten
180
220
val instanceInfo =
181
- if (cls.typeParams .isEmpty) ExprType (resultType)
182
- else PolyType .fromParams(cls.typeParams , ImplicitMethodType (evidenceParamInfos, resultType))
221
+ if (clsParams .isEmpty) ExprType (resultType)
222
+ else PolyType .fromParams(clsParams , ImplicitMethodType (evidenceParamInfos, resultType))
183
223
addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos, reportErrors = true )
184
224
}
185
- else
186
- ctx.error(
187
- i " derived class $derivedType should have one type paramater but has $nparams" ,
188
- derived.sourcePos)
189
225
}
190
226
191
227
/** Add value corresponding to `val genericClass = new GenericClass(...)`
@@ -210,14 +246,33 @@ trait Deriving { this: Typer =>
210
246
addDerivedInstance(defn.GenericType .name, genericCompleter, codePos, reportErrors = false )
211
247
}
212
248
249
+ /** If any of the instances has a companion with a `derived` member
250
+ * that refers to `scala.reflect.Generic`, add an implied instance
251
+ * of `Generic`. Note: this is just an optimization to avoid possible
252
+ * code duplication. Generic instances are created on the fly if they
253
+ * are missing from the companion.
254
+ */
255
+ private def maybeAddGeneric (): Unit = {
256
+ val genericCls = defn.GenericClass
257
+ def refersToGeneric (sym : Symbol ): Boolean = {
258
+ val companion = sym.info.finalResultType.classSymbol.companionModule
259
+ val derivd = companion.info.member(nme.derived)
260
+ derivd.hasAltWith(sd => sd.info.existsPart(p => p.typeSymbol == genericCls))
261
+ }
262
+ if (derivesGeneric || synthetics.exists(refersToGeneric)) {
263
+ derive.println(i " add generic infrastructure for $cls" )
264
+ addGeneric()
265
+ addGenericClass()
266
+ }
267
+ }
268
+
213
269
/** Create symbols for derived instances and infrastructure,
214
- * append them to `synthetics` buffer,
215
- * and enter them into class scope .
270
+ * append them to `synthetics` buffer, and enter them into class scope.
271
+ * Also, add generic instances if needed .
216
272
*/
217
273
def enterDerived (derived : List [untpd.Tree ]) = {
218
274
derived.foreach(processDerivedInstance(_))
219
- addGeneric()
220
- addGenericClass()
275
+ maybeAddGeneric()
221
276
}
222
277
223
278
private def tupleElems (tp : Type ): List [Type ] = tp match {
0 commit comments