Skip to content

Commit f545d10

Browse files
authored
Fix tuple member selection so it works with GADT healing (#16766)
2 parents be0844e + 90abf99 commit f545d10

File tree

3 files changed

+30
-9
lines changed

3 files changed

+30
-9
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,15 @@ class Definitions {
16751675
rec(tp.stripTypeVar, Nil, bound)
16761676
}
16771677

1678+
def isSmallGenericTuple(tp: Type)(using Context): Boolean =
1679+
if tp.derivesFrom(defn.PairClass) && !defn.isTupleNType(tp.widenDealias) then
1680+
// If this is a generic tuple we need to cast it to make the TupleN/ members accessible.
1681+
// This works only for generic tuples of known size up to 22.
1682+
defn.tupleTypes(tp.widenTermRefExpr) match
1683+
case Some(elems) if elems.length <= Definitions.MaxTupleArity => true
1684+
case _ => false
1685+
else false
1686+
16781687
def isProductSubType(tp: Type)(using Context): Boolean = tp.derivesFrom(ProductClass)
16791688

16801689
/** Is `tp` (an alias) of either a scala.FunctionN or a scala.ContextFunctionN

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
635635
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
636636
// but that is done only after we search for extension methods or conversions.
637637
typedSelect(tree, pt, qual)
638+
else if defn.isSmallGenericTuple(qual.tpe) then
639+
val elems = defn.tupleTypes(qual.tpe.widenTermRefExpr).getOrElse(Nil)
640+
typedSelect(tree, pt, qual.cast(defn.tupleType(elems)))
638641
else
639642
val tree1 = tryExtensionOrConversion(
640643
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
@@ -654,6 +657,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
654657
if checkedType1.exists then
655658
gadts.println(i"Member selection healed by GADT approximation")
656659
finish(tree1, qual1, checkedType1)
660+
else if defn.isSmallGenericTuple(qual1.tpe) then
661+
gadts.println(i"Tuple member selection healed by GADT approximation")
662+
typedSelect(tree, pt, qual1)
657663
else
658664
tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true)
659665
else EmptyTree
@@ -4010,15 +4016,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
40104016
else err.typeMismatch(tree, pt, failure)
40114017

40124018
pt match
4013-
case pt: SelectionProto =>
4014-
if tree.tpe.derivesFrom(defn.PairClass) && !defn.isTupleNType(tree.tpe.widenDealias) then
4015-
// If this is a generic tuple we need to cast it to make the TupleN/ members accessible.
4016-
// This works only for generic tuples of known size up to 22.
4017-
defn.tupleTypes(tree.tpe.widenTermRefExpr) match
4018-
case Some(elems) if elems.length <= Definitions.MaxTupleArity =>
4019-
tree.cast(defn.tupleType(elems))
4020-
case _ => tree
4021-
else tree // other adaptations for selections are handled in typedSelect
4019+
case _: SelectionProto =>
4020+
tree // adaptations for selections are handled in typedSelect
40224021
case _ if ctx.mode.is(Mode.ImplicitsEnabled) && tree.tpe.isValueType =>
40234022
if pt.isRef(defn.AnyValClass, skipRefined = false)
40244023
|| pt.isRef(defn.ObjectClass, skipRefined = false)

tests/pos/i16590.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
enum Tag[A]:
2+
case MyTuple extends Tag[(String, String)]
3+
4+
def printIt[A](t: Tag[A], a: A): Unit =
5+
t match
6+
case Tag.MyTuple => println(a._1)
7+
8+
enum Tag2[A]:
9+
case MyTuple extends Tag2[String *: String *: EmptyTuple]
10+
11+
def printIt2[A](t: Tag2[A], a: A): Unit =
12+
t match
13+
case Tag2.MyTuple => println(a._1)

0 commit comments

Comments
 (0)