From 584c05bd80be0bb048365470be9fe73e15b49796 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 Jan 2022 17:25:04 +0100 Subject: [PATCH] Adapt generic tuples to be able to access members Fixes #14215 --- .../src/dotty/tools/dotc/typer/Typer.scala | 6 +++ tests/neg/genericTupleMembers.scala | 38 +++++++++++++++ tests/run/genericTupleMembers.scala | 46 +++++++++++++++++++ tests/run/i14215.scala | 12 +++++ 4 files changed, 102 insertions(+) create mode 100644 tests/neg/genericTupleMembers.scala create mode 100644 tests/run/genericTupleMembers.scala create mode 100644 tests/run/i14215.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 957a51532886..5f1a45fbd2d8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3854,6 +3854,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer gadts.println(i"Member selection healed by GADT approximation") tree.cast(gadtApprox) else tree + else if tree.tpe.derivesFrom(defn.PairClass) && !defn.isTupleNType(tree.tpe.widenDealias) then + // If this is a generic tuple we need to cast it to make the TupleN/ members accessible. + // This only works for generic tuples of know size up to 22. + defn.tupleTypes(tree.tpe.widenTermRefExpr, Definitions.MaxTupleArity) match + case Some(elems) => tree.cast(defn.tupleType(elems)) + case None => tree else tree // other adaptations for selections are handled in typedSelect case _ if ctx.mode.is(Mode.ImplicitsEnabled) && tree.tpe.isValueType => checkConversionsSpecific(pt, tree.srcPos) diff --git a/tests/neg/genericTupleMembers.scala b/tests/neg/genericTupleMembers.scala new file mode 100644 index 000000000000..13c70fa73b6f --- /dev/null +++ b/tests/neg/genericTupleMembers.scala @@ -0,0 +1,38 @@ +def Test: Unit = + val tup1 = 1 *: EmptyTuple + val tup2 = 1 *: 2 *: EmptyTuple + val tup3 = 1 *: 2 *: 3 *: EmptyTuple + val tup4 = 1 *: 2 *: 3 *: 4 *: EmptyTuple + val tup5 = 1 *: 2 *: 3 *: 4 *: 5 *: EmptyTuple + val tup22 = 1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: EmptyTuple + val tup23 = 1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: 23 *: EmptyTuple + + tup1._2 // error + + tup2._3 // error + + tup22._23 // error + + tup23._1 // error + tup23._2 // error + tup23._3 // error + tup23._4 // error + tup23._5 // error + tup23._6 // error + tup23._7 // error + tup23._8 // error + tup23._9 // error + tup23._10 // error + tup23._11 // error + tup23._12 // error + tup23._13 // error + tup23._14 // error + tup23._15 // error + tup23._16 // error + tup23._17 // error + tup23._18 // error + tup23._19 // error + tup23._20 // error + tup23._21 // error + tup23._22 // error + tup23._23 // error diff --git a/tests/run/genericTupleMembers.scala b/tests/run/genericTupleMembers.scala new file mode 100644 index 000000000000..1b6de1486a05 --- /dev/null +++ b/tests/run/genericTupleMembers.scala @@ -0,0 +1,46 @@ + +@main def Test: Unit = + val tup1 = 1 *: EmptyTuple + val tup2 = 1 *: 2 *: EmptyTuple + val tup3 = 1 *: 2 *: 3 *: EmptyTuple + val tup4 = 1 *: 2 *: 3 *: 4 *: EmptyTuple + val tup5 = 1 *: 2 *: 3 *: 4 *: 5 *: EmptyTuple + val tup22 = 1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: 21 *: 22 *: EmptyTuple + + tup1._1 + + tup2._1 + tup2._2 + tup2.swap + + tup3._1 + tup3._2 + tup3._3 + + tup4._1 + tup4._2 + tup4._3 + tup4._4 + + tup22._1 + tup22._2 + tup22._3 + tup22._4 + tup22._5 + tup22._6 + tup22._7 + tup22._8 + tup22._9 + tup22._10 + tup22._11 + tup22._12 + tup22._13 + tup22._14 + tup22._15 + tup22._16 + tup22._17 + tup22._18 + tup22._19 + tup22._20 + tup22._21 + tup22._22 diff --git a/tests/run/i14215.scala b/tests/run/i14215.scala new file mode 100644 index 000000000000..d732337ce0f2 --- /dev/null +++ b/tests/run/i14215.scala @@ -0,0 +1,12 @@ +def f[T <: Tuple2[Int, Int]](tup: T): T = tup + +@main def Test: Unit = + (1, 2)._1 + f((1, 2))._1 + + (1 *: 2 *: EmptyTuple)._1 + f(1 *: 2 *: EmptyTuple)._1 + f[Int *: Int *: EmptyTuple](1 *: 2 *: EmptyTuple)._1 + + f[Int *: Int *: EmptyTuple]((1, 2))._1 + f[Tuple2[Int, Int]](1 *: 2 *: EmptyTuple)._1