From 90abf995c4c02a5de48fafba054e82dd91dcb222 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Jan 2023 17:18:13 +0000 Subject: [PATCH] Fix tuple member selection so it works with GADT healing --- .../src/dotty/tools/dotc/core/Definitions.scala | 9 +++++++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 17 ++++++++--------- tests/pos/i16590.scala | 13 +++++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 tests/pos/i16590.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ed86050436e8..854608143df9 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1675,6 +1675,15 @@ class Definitions { rec(tp.stripTypeVar, Nil, bound) } + def isSmallGenericTuple(tp: Type)(using Context): Boolean = + if tp.derivesFrom(defn.PairClass) && !defn.isTupleNType(tp.widenDealias) then + // If this is a generic tuple we need to cast it to make the TupleN/ members accessible. + // This works only for generic tuples of known size up to 22. + defn.tupleTypes(tp.widenTermRefExpr) match + case Some(elems) if elems.length <= Definitions.MaxTupleArity => true + case _ => false + else false + def isProductSubType(tp: Type)(using Context): Boolean = tp.derivesFrom(ProductClass) /** Is `tp` (an alias) of either a scala.FunctionN or a scala.ContextFunctionN diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3c4146f7ca2d..67b5736b2c22 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -635,6 +635,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`, // but that is done only after we search for extension methods or conversions. typedSelect(tree, pt, qual) + else if defn.isSmallGenericTuple(qual.tpe) then + val elems = defn.tupleTypes(qual.tpe.widenTermRefExpr).getOrElse(Nil) + typedSelect(tree, pt, qual.cast(defn.tupleType(elems))) else val tree1 = tryExtensionOrConversion( tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true) @@ -654,6 +657,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if checkedType1.exists then gadts.println(i"Member selection healed by GADT approximation") finish(tree1, qual1, checkedType1) + else if defn.isSmallGenericTuple(qual1.tpe) then + gadts.println(i"Tuple member selection healed by GADT approximation") + typedSelect(tree, pt, qual1) else tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true) else EmptyTree @@ -3998,15 +4004,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else err.typeMismatch(tree, pt, failure) pt match - case pt: SelectionProto => - 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 works only for generic tuples of known size up to 22. - defn.tupleTypes(tree.tpe.widenTermRefExpr) match - case Some(elems) if elems.length <= Definitions.MaxTupleArity => - tree.cast(defn.tupleType(elems)) - case _ => tree - else tree // other adaptations for selections are handled in typedSelect + case _: SelectionProto => + tree // adaptations for selections are handled in typedSelect case _ if ctx.mode.is(Mode.ImplicitsEnabled) && tree.tpe.isValueType => if pt.isRef(defn.AnyValClass, skipRefined = false) || pt.isRef(defn.ObjectClass, skipRefined = false) diff --git a/tests/pos/i16590.scala b/tests/pos/i16590.scala new file mode 100644 index 000000000000..d70054fd52b4 --- /dev/null +++ b/tests/pos/i16590.scala @@ -0,0 +1,13 @@ +enum Tag[A]: + case MyTuple extends Tag[(String, String)] + +def printIt[A](t: Tag[A], a: A): Unit = + t match + case Tag.MyTuple => println(a._1) + +enum Tag2[A]: + case MyTuple extends Tag2[String *: String *: EmptyTuple] + +def printIt2[A](t: Tag2[A], a: A): Unit = + t match + case Tag2.MyTuple => println(a._1)