From da1f626e1035f703d2f370130edfbfdc72374e9f Mon Sep 17 00:00:00 2001 From: Emil Ejbyfeldt Date: Fri, 13 Jun 2025 21:03:59 +0200 Subject: [PATCH 1/2] 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 761a24e10474..5d1e6da61afa 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")] +} From 73d24042e208bc80051c7684d80929bde8609ff8 Mon Sep 17 00:00:00 2001 From: Emil Ejbyfeldt Date: Fri, 4 Jul 2025 16:48:55 +0200 Subject: [PATCH 2/2] PR comments --- compiler/src/dotty/tools/dotc/core/StdNames.scala | 1 + compiler/src/dotty/tools/dotc/typer/Synthesizer.scala | 4 ++-- tests/pos/i15398.scala | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 9352be725e2c..d88cbe80e56a 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -396,6 +396,7 @@ object StdNames { val This: N = "This" val ThisType: N = "ThisType" val Tuple2: N = "Tuple2" + val Tuple: N = "Tuple" val TYPE_ : N = "TYPE" val TypeApply: N = "TypeApply" val TypeRef: N = "TypeRef" diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 5d1e6da61afa..99a2f15c13f5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -434,13 +434,13 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): def makeTupleProductMirror(tps: List[Type]): TreeWithErrors = val arity = tps.size - if arity < Definitions.MaxTupleArity then + 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) + makeProductMirror(tps, elemLabels, tpnme.Tuple, mirrorRef) end makeTupleProductMirror def makeClassProductMirror(pre: Type, cls: Symbol, tps: Option[List[Type]]) = diff --git a/tests/pos/i15398.scala b/tests/pos/i15398.scala index 15ab57ff6661..a93738ba1c0d 100644 --- a/tests/pos/i15398.scala +++ b/tests/pos/i15398.scala @@ -3,7 +3,7 @@ object i15398 { summon[Tuple.Size[Tuple23] =:= 23] val m = summon[scala.deriving.Mirror.Of[Tuple23]] - summon[m.MirroredLabel =:= "Tuple23"] + summon[m.MirroredLabel =:= "Tuple"] 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")] }