From 074202604a2f65c9ba1ce81680afc9d5f8d38f40 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Wed, 2 Jul 2025 15:14:39 +0000 Subject: [PATCH 1/3] Normalize tuple types when making seq literals for var args --- compiler/src/dotty/tools/dotc/core/TypeUtils.scala | 10 ++++++++++ compiler/src/dotty/tools/dotc/typer/Applications.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 7 ++++--- tests/run/i22345.scala | 2 ++ tests/run/i22345b.scala | 2 ++ 5 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 tests/run/i22345.scala create mode 100644 tests/run/i22345b.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index e31cd19b1135..8f462569e39b 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -115,6 +115,16 @@ class TypeUtils: case Some(types) => TypeOps.nestedPairs(types) case None => throw new AssertionError("not a tuple") + /** If this is a generic tuple type with arity <= MaxTupleArity, return the + * corresponding TupleN type, otherwise return this. + */ + def normalizedTupleType(using Context): Type = + if self.isGenericTuple then + self.tupleElementTypes match + case Some(elems) if elems.size <= Definitions.MaxTupleArity => defn.tupleType(elems) + case _ => self + else + self def refinedWith(name: Name, info: Type)(using Context) = RefinedType(self, name, info) /** Is this type a methodic type that takes at least one parameter? */ diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 4d39e3243fc0..d2fd7a928eb0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -841,7 +841,7 @@ trait Applications extends Compatibility { def makeVarArg(n: Int, elemFormal: Type): Unit = { val args = typedArgBuf.takeRight(n).toList typedArgBuf.dropRightInPlace(n) - val elemtpt = TypeTree(elemFormal) + val elemtpt = TypeTree(elemFormal.normalizedTupleType) typedArgBuf += seqToRepeated(SeqLiteral(args, elemtpt)) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index bf44ff659f22..cedc4a082744 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -738,10 +738,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Otherwise, map combinations of A *: B *: .... EmptyTuple with nesting levels <= 22 // to the Tuple class of the right arity and select from that one def trySmallGenericTuple(qual: Tree, withCast: Boolean) = - if qual.tpe.isSmallGenericTuple then + val tp = qual.tpe.widenTermRefExpr + val tpNormalized = tp.normalizedTupleType + if tp ne tpNormalized then if withCast then - val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil) - typedSelectWithAdapt(tree, pt, qual.cast(defn.tupleType(elems))) + typedSelectWithAdapt(tree, pt, qual.cast(tpNormalized)) else typedSelectWithAdapt(tree, pt, qual) else EmptyTree diff --git a/tests/run/i22345.scala b/tests/run/i22345.scala new file mode 100644 index 000000000000..86cc3a01930e --- /dev/null +++ b/tests/run/i22345.scala @@ -0,0 +1,2 @@ +@main def Test: Unit = + val a: Array[(Int, String)] = Array[Int *: String *: EmptyTuple]() diff --git a/tests/run/i22345b.scala b/tests/run/i22345b.scala new file mode 100644 index 000000000000..a331a66ea80a --- /dev/null +++ b/tests/run/i22345b.scala @@ -0,0 +1,2 @@ +@main def Test: Unit = + val a: Array[(Int, String)] = Array[Int *: String *: EmptyTuple]((1, "hello")) From 8b91ea87af84e272ca52e7cb2262023b616ebd96 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Tue, 8 Jul 2025 15:01:18 +0200 Subject: [PATCH 2/3] Normalize tuple types when making seq literals for var args [Cherry-picked 979c546c3d19dac3d22708c380a603a4c49a8cdd][modified] From ba5021a2ee8c0eb5203408db344c4453a6968f9f Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Wed, 2 Jul 2025 15:28:05 +0000 Subject: [PATCH 3/3] Normalize tuple types in synthetized classOf instances [Cherry-picked a324d12dd9ecb4b322c25e7f834f52e360c981de] --- compiler/src/dotty/tools/dotc/typer/Synthesizer.scala | 2 +- tests/run/i22345c.scala | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 tests/run/i22345c.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 2c85294a3a94..6a8acc01f257 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -51,7 +51,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): if defn.SpecialClassTagClasses.contains(sym) then classTagModul.select(sym.name.toTermName).withSpan(span) else - val ctype = escapeJavaArray(erasure(tp)) + val ctype = escapeJavaArray(erasure(tp.normalizedTupleType)) if ctype.exists then classTagModul.select(nme.apply) .appliedToType(tp) diff --git a/tests/run/i22345c.scala b/tests/run/i22345c.scala new file mode 100644 index 000000000000..25bafae0c390 --- /dev/null +++ b/tests/run/i22345c.scala @@ -0,0 +1,4 @@ +def makeSeq[T](args: T*): Seq[T] = args + +@main def Test: Unit = + val a: Array[(Int, String)] = makeSeq[Int *: String *: EmptyTuple]().toArray