diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 60360a268993..419e1b11b76e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -870,10 +870,21 @@ object desugar { * def pn = x$1._n * body * } + * + * or if `isGenericTuple` + * + * x$1 => { + * def p1 = x$1.apply(0) + * ... + * def pn = x$1.apply(n-1) + * body + * } */ - def makeTupledFunction(params: List[ValDef], body: Tree)(implicit ctx: Context): Tree = { + def makeTupledFunction(params: List[ValDef], body: Tree, isGenericTuple: Boolean)(implicit ctx: Context): Tree = { val param = makeSyntheticParameter() - def selector(n: Int) = Select(refOfDef(param), nme.selectorName(n)) + def selector(n: Int) = + if (isGenericTuple) Apply(Select(refOfDef(param), nme.apply), Literal(Constant(n))) + else Select(refOfDef(param), nme.selectorName(n)) val vdefs = params.zipWithIndex.map{ case (param, idx) => diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 988d641a0432..e44b6b907dff 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -59,8 +59,19 @@ object Applications { extractorMemberType(tp, nme.get, errorPos).exists def productSelectorTypes(tp: Type, errorPos: Position = NoPosition)(implicit ctx: Context): List[Type] = { - val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos) - sels.takeWhile(_.exists).toList + def tupleSelectors(n: Int, tp: Type): List[Type] = { + val sel = extractorMemberType(tp, nme.selectorName(n), errorPos) + // extractorMemberType will return NoType if this is the tail of tuple with an unknown tail + // such as `Int *: T` where `T <: Tuple`. + if (sel.exists) sel :: tupleSelectors(n + 1, tp) else Nil + } + def genTupleSelectors(n: Int, tp: Type): List[Type] = tp match { + case tp: AppliedType if !tp.derivesFrom(defn.ProductClass) && tp.derivesFrom(defn.PairClass) => + val List(head, tail) = tp.args + head :: genTupleSelectors(n, tail) + case _ => tupleSelectors(n, tp) + } + genTupleSelectors(0, tp) } def productArity(tp: Type)(implicit ctx: Context): Int = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 643faf24e0c6..a01777049a02 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -906,7 +906,7 @@ class Typer extends Namer /** Is `formal` a product type which is elementwise compatible with `params`? */ def ptIsCorrectProduct(formal: Type) = { isFullyDefined(formal, ForceDegree.noBottom) && - defn.isProductSubType(formal) && + (defn.isProductSubType(formal) || formal.derivesFrom(defn.PairClass)) && Applications.productSelectorTypes(formal).corresponds(params) { (argType, param) => param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe @@ -915,7 +915,8 @@ class Typer extends Namer val desugared = if (protoFormals.length == 1 && params.length != 1 && ptIsCorrectProduct(protoFormals.head)) { - desugar.makeTupledFunction(params, fnBody) + val isGenericTuple = !protoFormals.head.derivesFrom(defn.ProductClass) + desugar.makeTupledFunction(params, fnBody, isGenericTuple) } else { val inferredParams: List[untpd.ValDef] = diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 7dc319af5a50..83d3dc7733d1 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -23,6 +23,7 @@ i4947b i5119 i5119b i5188a +i5257.scala inline-varargs-1 implicitShortcut inline-case-objects diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 55fa7181faec..ea9be0c5aa20 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -172,6 +172,7 @@ class CompilationTests extends ParallelTesting { implicit val testGroup: TestGroup = TestGroup("runAll") compileFilesInDir("tests/run-custom-args/Yretain-trees", defaultOptions and "-Yretain-trees") + compileFile("tests/run-custom-args/tuple-cons.scala", allowDeepSubtypes) + + compileFile("tests/run-custom-args/i5256.scala", allowDeepSubtypes) + compileFilesInDir("tests/run", defaultOptions) }.checkRuns() diff --git a/tests/run-custom-args/i5256.check b/tests/run-custom-args/i5256.check new file mode 100644 index 000000000000..15007f1b9dfe --- /dev/null +++ b/tests/run-custom-args/i5256.check @@ -0,0 +1 @@ +276 diff --git a/tests/run-custom-args/i5256.scala b/tests/run-custom-args/i5256.scala new file mode 100644 index 000000000000..082efa879f78 --- /dev/null +++ b/tests/run-custom-args/i5256.scala @@ -0,0 +1,11 @@ +object Test { + + def main(args: Array[String]): Unit = { + val f23: ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => Int = { + (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23) => + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + x22 + x23 + } + + println(f23((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23))) + } +} diff --git a/tests/run/i5257.check b/tests/run/i5257.check new file mode 100644 index 000000000000..7ee0007bf1f1 --- /dev/null +++ b/tests/run/i5257.check @@ -0,0 +1,2 @@ +3 +5 diff --git a/tests/run/i5257.scala b/tests/run/i5257.scala new file mode 100644 index 000000000000..1fd1abe26e11 --- /dev/null +++ b/tests/run/i5257.scala @@ -0,0 +1,11 @@ +object Test { + + def main(args: Array[String]): Unit = { + val f: Int *: Int *: Unit => Int = (x, y) => x + y + val g: Int *: Tuple1[Int] => Int = (x, y) => x + y + + println(f((1, 2))) + println(g((2, 3))) + } + +}