Skip to content

Commit 0e09603

Browse files
committed
handle generic tuples of different arity
1 parent 8f55301 commit 0e09603

File tree

2 files changed

+34
-16
lines changed

2 files changed

+34
-16
lines changed

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -281,21 +281,34 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
281281

282282
private def productMirror(mirroredType: Type, formal: Type, span: Span)(using Context): TreeWithErrors =
283283

284-
var isSafeGenericTuple = Option.empty[List[Type]]
284+
var isSafeGenericTuple = Option.empty[(Symbol, List[Type])]
285+
286+
/** do all parts match the class symbol? Or can we extract a generic tuple type out? */
287+
def acceptable(tp: Type, cls: Symbol): Boolean =
288+
var genericTupleParts = List.empty[(Symbol, List[Type])]
289+
290+
def acceptableGenericTuple(tp: AppliedType): Boolean =
291+
val tupleArgs = tp.tupleElementTypes
292+
val arity = tupleArgs.size
293+
val isOk = arity <= Definitions.MaxTupleArity
294+
if isOk then
295+
genericTupleParts ::= {
296+
val cls = defn.TupleType(arity).nn.classSymbol
297+
(cls, tupleArgs)
298+
}
299+
isOk
285300

286-
def illegalGenericTuple(tp: AppliedType): Boolean =
287-
val tupleArgs = tp.tupleElementTypes
288-
val isTooLarge = tupleArgs.length > Definitions.MaxTupleArity
289-
isSafeGenericTuple = Option.when(!isTooLarge)(tupleArgs)
290-
isTooLarge
301+
def inner(tp: Type, cls: Symbol): Boolean = tp match
302+
case tp: HKTypeLambda if tp.resultType.isInstanceOf[HKTypeLambda] => false
303+
case tp @ AppliedType(cons: TypeRef, _) if cons.isRef(defn.PairClass) => acceptableGenericTuple(tp)
304+
case tp: TypeProxy => inner(tp.underlying, cls)
305+
case OrType(tp1, tp2) => inner(tp1, cls) && inner(tp2, cls)
306+
case _ => tp.classSymbol eq cls
291307

292-
/** do all parts match the class symbol? */
293-
def acceptable(tp: Type, cls: Symbol): Boolean = tp match
294-
case tp: HKTypeLambda if tp.resultType.isInstanceOf[HKTypeLambda] => false
295-
case tp @ AppliedType(cons: TypeRef, _) if cons.isRef(defn.PairClass) && illegalGenericTuple(tp) => false
296-
case tp: TypeProxy => acceptable(tp.underlying, cls)
297-
case OrType(tp1, tp2) => acceptable(tp1, cls) && acceptable(tp2, cls)
298-
case _ => tp.classSymbol eq cls
308+
val classPartsMatch = inner(tp, cls)
309+
classPartsMatch && genericTupleParts.map((cls, _) => cls).distinct.sizeIs <= 1 &&
310+
{ isSafeGenericTuple = genericTupleParts.headOption ; true }
311+
end acceptable
299312

300313
/** for a case class, if it will have an anonymous mirror,
301314
* check that its constructor can be accessed
@@ -314,10 +327,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
314327
cls.is(Scala2x) || cls.linkedClass.is(Case)
315328

316329
def makeProductMirror(cls: Symbol): TreeWithErrors =
317-
val mirroredClass = isSafeGenericTuple.fold(cls)(tps => defn.TupleType(tps.size).nn.classSymbol)
330+
val mirroredClass = isSafeGenericTuple.fold(cls)((cls, _) => cls)
318331
val accessors = mirroredClass.caseAccessors.filterNot(_.isAllOf(PrivateLocal))
319332
val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString)))
320-
val nestedPairs = isSafeGenericTuple.map(TypeOps.nestedPairs).getOrElse {
333+
val nestedPairs = isSafeGenericTuple.map((_, tps) => TypeOps.nestedPairs(tps)).getOrElse {
321334
TypeOps.nestedPairs(accessors.map(mirroredType.resultType.memberInfo(_).widenExpr))
322335
}
323336
val (monoType, elemsType) = mirroredType match
@@ -334,7 +347,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
334347
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(elemsLabels))
335348
val mirrorRef =
336349
if genAnonyousMirror(mirroredClass) then
337-
anonymousMirror(monoType, ExtendsProductMirror, isSafeGenericTuple.map(_.size), span)
350+
anonymousMirror(monoType, ExtendsProductMirror, isSafeGenericTuple.map(_(1).size), span)
338351
else companionPath(mirroredType, span)
339352
withNoErrors(mirrorRef.cast(mirrorType))
340353
end makeProductMirror

tests/neg/i14127a.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import scala.deriving.Mirror
2+
3+
// mixing arities is not supported
4+
5+
val mT23 = summon[Mirror.Of[(Int *: Int *: EmptyTuple) | (Int *: Int *: Int *: EmptyTuple)]] // error

0 commit comments

Comments
 (0)