@@ -32,6 +32,8 @@ trait Deriving { this: Typer =>
3232 /** A buffer for synthesized symbols */
3333 private var synthetics = new mutable.ListBuffer [Symbol ]
3434
35+ private var derivesGeneric = false
36+
3537 /** the children of `cls` ordered by textual occurrence */
3638 lazy val children : List [Symbol ] = cls.children
3739
@@ -159,33 +161,67 @@ trait Deriving { this: Typer =>
159161 *
160162 * implicit def derived$D(implicit ev_1: D[T_1], ..., ev_n: D[T_n]): D[C[Ts]] = D.derived
161163 *
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
164171 * the underlying class name. This allows one to disambiguate derivations of type classes
165172 * that have the same name but different prefixes through selective aliasing.
166173 */
167174 private def processDerivedInstance (derived : untpd.Tree ): Unit = {
168175 val originalType = typedAheadType(derived, AnyTypeConstructorProto ).tpe
169176 val underlyingType = underlyingClassRef(originalType)
170177 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
172180 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]
177206 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
180220 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))
183223 addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos, reportErrors = true )
184224 }
185- else
186- ctx.error(
187- i " derived class $derivedType should have one type paramater but has $nparams" ,
188- derived.sourcePos)
189225 }
190226
191227 /** Add value corresponding to `val genericClass = new GenericClass(...)`
@@ -210,14 +246,33 @@ trait Deriving { this: Typer =>
210246 addDerivedInstance(defn.GenericType .name, genericCompleter, codePos, reportErrors = false )
211247 }
212248
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+
213269 /** 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 .
216272 */
217273 def enterDerived (derived : List [untpd.Tree ]) = {
218274 derived.foreach(processDerivedInstance(_))
219- addGeneric()
220- addGenericClass()
275+ maybeAddGeneric()
221276 }
222277
223278 private def tupleElems (tp : Type ): List [Type ] = tp match {
0 commit comments