From 90a9067ba4f650beb0014d23bf97b88b0fb99d16 Mon Sep 17 00:00:00 2001 From: Emil Ejbyfeldt Date: Fri, 13 Jun 2025 21:03:59 +0200 Subject: [PATCH] Support Mirror for generic tuples arity > 22 --- .../dotty/tools/dotc/typer/Synthesizer.scala | 22 +++++++++++-------- tests/{neg => pos}/i14127.scala | 0 tests/pos/i15398.scala | 9 ++++++++ 3 files changed, 22 insertions(+), 9 deletions(-) rename tests/{neg => pos}/i14127.scala (100%) create mode 100644 tests/pos/i15398.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 4f596776d497..58f21121f629 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -432,13 +432,24 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): makeProductMirror(typeElems, elemLabels, tpnme.NamedTuple, mirrorRef) end makeNamedTupleProductMirror + def makeTupleProductMirror(tps: List[Type]): TreeWithErrors = + val arity = tps.size + if arity < Definitions.MaxTupleArity then + val tupleCls = defn.TupleType(arity).nn.classSymbol + makeClassProductMirror(tupleCls.owner.reachableThisType, tupleCls, Some(tps)) + else + val elemLabels = (for i <- 1 to arity yield ConstantType(Constant(s"_$i"))).toList + val mirrorRef: Type => Tree = _ => newTupleMirror(arity) + makeProductMirror(tps, elemLabels, s"Tuple$arity".toTermName, mirrorRef) + end makeTupleProductMirror + def makeClassProductMirror(pre: Type, cls: Symbol, tps: Option[List[Type]]) = val accessors = cls.caseAccessors val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString))) val typeElems = tps.getOrElse(accessors.map(mirroredType.resultType.memberInfo(_).widenExpr)) val mirrorRef = (monoType: Type) => if cls.useCompanionAsProductMirror then companionPath(pre, cls, span) - else if defn.isTupleClass(cls) then newTupleMirror(typeElems.size) // TODO: cls == defn.PairClass when > 22 + else if defn.isTupleClass(cls) then newTupleMirror(typeElems.size) else anonymousMirror(monoType, MirrorImpl.OfProduct(pre), span) makeProductMirror(typeElems, elemLabels, cls.name, mirrorRef) end makeClassProductMirror @@ -478,14 +489,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): } withNoErrors(singletonPath.cast(mirrorType).withSpan(span)) case MirrorSource.GenericTuple(tps) => - val maxArity = Definitions.MaxTupleArity - val arity = tps.size - if tps.size <= maxArity then - val tupleCls = defn.TupleType(arity).nn.classSymbol - makeClassProductMirror(tupleCls.owner.reachableThisType, tupleCls, Some(tps)) - else - val reason = s"it reduces to a tuple with arity $arity, expected arity <= $maxArity" - withErrors(i"${defn.PairClass} is not a generic product because $reason") + makeTupleProductMirror(tps) case MirrorSource.NamedTuple(nameTypePairs) => makeNamedTupleProductMirror(nameTypePairs) case MirrorSource.ClassSymbol(pre, cls) => diff --git a/tests/neg/i14127.scala b/tests/pos/i14127.scala similarity index 100% rename from tests/neg/i14127.scala rename to tests/pos/i14127.scala diff --git a/tests/pos/i15398.scala b/tests/pos/i15398.scala new file mode 100644 index 000000000000..15ab57ff6661 --- /dev/null +++ b/tests/pos/i15398.scala @@ -0,0 +1,9 @@ +object i15398 { + type Tuple23 = (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) + + summon[Tuple.Size[Tuple23] =:= 23] + val m = summon[scala.deriving.Mirror.Of[Tuple23]] + summon[m.MirroredLabel =:= "Tuple23"] + summon[m.MirroredElemTypes =:= Tuple23] + summon[m.MirroredElemLabels =:= ("_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9", "_10", "_11", "_12", "_13", "_14", "_15", "_16", "_17", "_18", "_19", "_20", "_21", "_22", "_23")] +}