@@ -161,33 +161,67 @@ trait Deriving { this: Typer =>
161161 *
162162 * implicit def derived$D(implicit ev_1: D[T_1], ..., ev_n: D[T_n]): D[C[Ts]] = D.derived
163163 *
164- * See test run/typeclass-derivation2 for examples that spell out what would be generated.
165- * Note that the name of the derived method containd the name in the derives clause, not
164+ * See the body of this method for who 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
166171 * the underlying class name. This allows one to disambiguate derivations of type classes
167172 * that have the same name but different prefixes through selective aliasing.
168173 */
169174 private def processDerivedInstance (derived : untpd.Tree ): Unit = {
170175 val originalType = typedAheadType(derived, AnyTypeConstructorProto ).tpe
171176 val underlyingType = underlyingClassRef(originalType)
172177 val derivedType = checkClassType(underlyingType, derived.sourcePos, traitReq = false , stablePrefixReq = true )
173- val nparams = derivedType.classSymbol.typeParams.length
178+ val typeClass = derivedType.classSymbol
179+ val nparams = typeClass.typeParams.length
174180 if (derivedType.isRef(defn.GenericClass ))
175181 derivesGeneric = true
176- else if (nparams == 1 ) {
177- val typeClass = derivedType.classSymbol
178- val firstKindedParams = cls.typeParams.filterNot(_.info.isLambdaSub)
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]
179206 val evidenceParamInfos =
180- for (param <- firstKindedParams) yield derivedType.appliedTo(param.typeRef)
181- 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
182220 val instanceInfo =
183- if (cls.typeParams .isEmpty) ExprType (resultType)
184- else PolyType .fromParams(cls.typeParams , ImplicitMethodType (evidenceParamInfos, resultType))
221+ if (clsParams .isEmpty) ExprType (resultType)
222+ else PolyType .fromParams(clsParams , ImplicitMethodType (evidenceParamInfos, resultType))
185223 addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos, reportErrors = true )
186224 }
187- else
188- ctx.error(
189- i " derived class $derivedType should have one type paramater but has $nparams" ,
190- derived.sourcePos)
191225 }
192226
193227 /** Add value corresponding to `val genericClass = new GenericClass(...)`
0 commit comments