@@ -83,23 +83,112 @@ trait Deriving { this: Typer =>
83
83
* that have the same name but different prefixes through selective aliasing.
84
84
*/
85
85
private def processDerivedInstance (derived : untpd.Tree ): Unit = {
86
- val originalType = typedAheadType(derived, AnyTypeConstructorProto ).tpe
87
- val underlyingType = underlyingClassRef(originalType)
88
- val derivedType = checkClassType(underlyingType, derived.sourcePos, traitReq = false , stablePrefixReq = true )
89
- val typeClass = derivedType.classSymbol
90
- val nparams = typeClass.typeParams.length
91
-
92
- lazy val clsTpe = cls.typeRef.EtaExpand (cls.typeParams)
93
- if (nparams == 1 && clsTpe.hasSameKindAs(typeClass.typeParams.head.info)) {
94
- // A "natural" type class instance ... the kind of the data type
95
- // matches the kind of the unique type class type parameter
96
-
97
- val resultType = derivedType.appliedTo(clsTpe)
98
- val instanceInfo = ExprType (resultType)
99
- addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
100
- } else if (typeClass == defn.EqlClass ) {
101
- // Special case derives semantics for the Eql type class
86
+ val originalTypeClassType = typedAheadType(derived, AnyTypeConstructorProto ).tpe
87
+ val typeClassType = checkClassType(underlyingClassRef(originalTypeClassType), derived.sourcePos, traitReq = false , stablePrefixReq = true )
88
+ val typeClass = typeClassType.classSymbol
89
+
90
+ def sameParamKinds (xs : List [ParamInfo ], ys : List [ParamInfo ]): Boolean =
91
+ xs.corresponds(ys)((x, y) => x.paramInfo.hasSameKindAs(y.paramInfo))
92
+
93
+ def cannotBeUnified =
94
+ ctx.error(i " ${cls.name} cannot be unified with the type argument of ${typeClass.name}" , derived.sourcePos)
95
+
96
+ def addInstance (derivedParams : List [TypeSymbol ], evidenceParamInfos : List [List [Type ]], instanceTypes : List [Type ]): Unit = {
97
+ val resultType = typeClassType.appliedTo(instanceTypes)
98
+ val methodOrExpr =
99
+ if (evidenceParamInfos.isEmpty) ExprType (resultType)
100
+ else ImplicitMethodType (evidenceParamInfos.map(typeClassType.appliedTo), resultType)
101
+ val derivedInfo = if (derivedParams.isEmpty) methodOrExpr else PolyType .fromParams(derivedParams, methodOrExpr)
102
+ addDerivedInstance(originalTypeClassType.typeSymbol.name, derivedInfo, derived.sourcePos)
103
+ }
104
+
105
+ val typeClassParams = typeClass.typeParams
106
+ val typeClassArity = typeClassParams.length
107
+ if (typeClassArity == 1 ) {
108
+ // Primary case: single parameter type classes
109
+ //
110
+ // (a) ADT and type class parameters overlap on the right and have the
111
+ // same kinds at the overlap.
112
+ //
113
+ // Examples:
114
+ //
115
+ // Type class: TC[F[T, U]]
116
+ //
117
+ // ADT: C[A, B, C, D] (C, D have same kinds as T, U)
118
+ //
119
+ // given derived$TC[a, b]: TC[[t, u] =>> C[a, b, t, u]]
120
+ //
121
+ // ADT: C[A, B, C] (B, C have same kinds at T, U)
122
+ //
123
+ // given derived$TC [a]: TC[[t, u] =>> C[a, t, u]]
124
+ //
125
+ // ADT: C[A, B] (A, B have same kinds at T, U)
126
+ //
127
+ // given derived$TC : TC[ C ] // a "natural" instance
128
+ //
129
+ // ADT: C[A] (A has same kind as U)
130
+ //
131
+ // given derived$TC : TC[[t, u] =>> C[ u]]
132
+ //
133
+ // (b) The type class and all ADT type parameters are of kind *
134
+ //
135
+ // In this case the ADT has at least one type parameter of kind *,
136
+ // otherwise it would already have been covered as a "natural" case
137
+ // for a type class of the form F[_].
138
+ //
139
+ // The derived instance has a type parameter and a given for
140
+ // each of the type parameters of the ADT,
141
+ //
142
+ // Example:
143
+ //
144
+ // Type class: TC[T]
145
+ //
146
+ // ADT: C[A, B, C]
147
+ //
148
+ // given derived$TC[a, b, c] given TC[a], TC[b], TC[c]: TC[a, b, c]
149
+ //
150
+ // This, like the derivation for Eql, is a special case of the
151
+ // earlier more general multi-parameter type class model for which
152
+ // the heuristic is typically a good one.
102
153
154
+ val typeClassParamType = typeClassParams.head.info
155
+ val typeClassParamInfos = typeClassParamType.typeParams
156
+ val instanceArity = typeClassParamInfos.length
157
+ val clsType = cls.typeRef
158
+ val clsParams = cls.typeParams
159
+ val clsParamInfos = clsType.typeParams
160
+ val clsArity = clsParamInfos.length
161
+ val alignedClsParamInfos = clsParamInfos.takeRight(instanceArity)
162
+ val alignedTypeClassParamInfos = typeClassParamInfos.take(alignedClsParamInfos.length)
163
+
164
+ if ((instanceArity == clsArity || instanceArity > 0 ) && sameParamKinds(alignedClsParamInfos, alignedTypeClassParamInfos)) {
165
+ // case (a) ... see description above
166
+ val derivedParams = clsParams.dropRight(instanceArity)
167
+ val instanceType =
168
+ if (instanceArity == clsArity) clsType.EtaExpand (clsParams)
169
+ else {
170
+ val derivedParamTypes = derivedParams.map(_.typeRef)
171
+
172
+ HKTypeLambda (typeClassParamInfos.map(_.paramName))(
173
+ tl => typeClassParamInfos.map(_.paramInfo.bounds),
174
+ tl => clsType.appliedTo(derivedParamTypes ++ tl.paramRefs.takeRight(clsArity)))
175
+ }
176
+
177
+ addInstance(derivedParams, Nil , List (instanceType))
178
+ } else if (instanceArity == 0 && ! clsParams.exists(_.info.isLambdaSub)) {
179
+ // case (b) ... see description above
180
+ val instanceType = clsType.appliedTo(clsParams.map(_.typeRef))
181
+ val evidenceParamInfos = clsParams.map(param => List (param.typeRef))
182
+ addInstance(clsParams, evidenceParamInfos, List (instanceType))
183
+ } else
184
+ cannotBeUnified
185
+ } else if (typeClass == defn.EqlClass ) {
186
+ // Special case: derives semantics for the Eql type class
187
+ //
188
+ // This has been extracted from the earlier more general multi-parameter
189
+ // type class model. Modulo the assumptions below, the implied semantics
190
+ // are reasonable defaults.
191
+ //
103
192
// Assumptions:
104
193
// 1. Type params of the deriving class correspond to all and only
105
194
// elements of the deriving class which are relevant to equality (but:
@@ -129,7 +218,7 @@ trait Deriving { this: Typer =>
129
218
// U_L U_R
130
219
// V_L V_R
131
220
val clsParamss : List [List [TypeSymbol ]] = cls.typeParams.map { tparam =>
132
- typeClass.typeParams .map(tcparam =>
221
+ typeClassParams .map(tcparam =>
133
222
tparam.copy(name = s " ${tparam.name}_ $$ _ ${tcparam.name}" .toTypeName)
134
223
.asInstanceOf [TypeSymbol ])
135
224
}
@@ -144,36 +233,20 @@ trait Deriving { this: Typer =>
144
233
// Eql[T_L, T_R], Eql[U_L, U_R], Eql[V_L, V_R]
145
234
val evidenceParamInfos =
146
235
for (row <- firstKindedParamss)
147
- yield derivedType.appliedTo( row.map(_.typeRef) )
236
+ yield row.map(_.typeRef)
148
237
149
238
// The class instances in the result type. Running example:
150
239
// A[T_L, U_L, V_L], A[T_R, U_R, V_R]
151
- val resultInstances =
152
- for (n <- List .range(0 , nparams ))
240
+ val instanceTypes =
241
+ for (n <- List .range(0 , typeClassArity ))
153
242
yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef))
154
243
155
244
// Eql[A[T_L, U_L, V_L], A[T_R, U_R, V_R]]
156
- val resultType = derivedType.appliedTo(resultInstances)
157
-
158
- val clsParams : List [TypeSymbol ] = clsParamss.flatten
159
- val instanceInfo =
160
- if (clsParams.isEmpty) ExprType (resultType)
161
- else PolyType .fromParams(clsParams, ImplicitMethodType (evidenceParamInfos, resultType))
162
- addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
163
- } else if (nparams == 1 && ! typeClass.typeParams.head.info.isLambdaSub && ! cls.typeParams.exists(_.info.isLambdaSub)) {
164
- val clsParams : List [TypeSymbol ] = cls.typeParams
165
- val evidenceParamInfos = clsParams.map(param => derivedType.appliedTo(param.typeRef))
166
- val resultInstance = cls.typeRef.appliedTo(clsParams.map(_.typeRef))
167
- val resultType = derivedType.appliedTo(resultInstance)
168
- val instanceInfo =
169
- if (clsParams.isEmpty) ExprType (resultType)
170
- else PolyType .fromParams(clsParams, ImplicitMethodType (evidenceParamInfos, resultType))
171
- addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
172
- } else if (nparams == 0 ) {
245
+ addInstance(clsParamss.flatten, evidenceParamInfos, instanceTypes)
246
+ } else if (typeClassArity == 0 )
173
247
ctx.error(i " type ${typeClass.name} in derives clause of ${cls.name} has no type parameters " , derived.sourcePos)
174
- } else {
175
- ctx.error(i " ${cls.name} cannot be unified with the type argument of ${typeClass.name}" , derived.sourcePos)
176
- }
248
+ else
249
+ cannotBeUnified
177
250
}
178
251
179
252
/** Create symbols for derived instances and infrastructure,
0 commit comments