From 979c546c3d19dac3d22708c380a603a4c49a8cdd Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Wed, 2 Jul 2025 15:14:39 +0000 Subject: [PATCH 1/2] Normalize tuple types when making seq literals for var args --- compiler/src/dotty/tools/dotc/core/TypeUtils.scala | 11 +++++++++++ .../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, 20 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 eb526c2b4d85..82c027744c38 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -152,6 +152,17 @@ class TypeUtils: def namedTupleElementTypes(derived: Boolean)(using Context): List[(TermName, Type)] = namedTupleElementTypesUpTo(Int.MaxValue, derived) + /** 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 isNamedTupleType(using Context): Boolean = self match case defn.NamedTuple(_, _) => true case _ => false diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 6499d806a9a5..0bcf2f5cce69 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -942,7 +942,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, inferred = true) + val elemtpt = TypeTree(elemFormal.normalizedTupleType, inferred = true) 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 915bfb8ee1e1..7d2bf2f463ba 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -847,10 +847,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 a324d12dd9ecb4b322c25e7f834f52e360c981de Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Wed, 2 Jul 2025 15:28:05 +0000 Subject: [PATCH 2/2] Normalize tuple types in synthetized classOf instances --- 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 4f596776d497..761a24e10474 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -53,7 +53,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