From f8b12dd4b73ae832278e21d4da0cd6a9fb6b6846 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Wed, 16 Apr 2025 11:12:36 +0200 Subject: [PATCH 1/4] Call dealias after stripping type variables for tupleElementTypesUpTo --- .../src/dotty/tools/dotc/core/TypeUtils.scala | 3 +- tests/pos/i22643.scala | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i22643.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index 739cc2b74a16..fe73a18e31d5 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -83,7 +83,8 @@ class TypeUtils: case _ => if defn.isTupleClass(tp.typeSymbol) && !normalize then Some(tp.dealias.argInfos) else None - recur(self.stripTypeVar, bound) + val stripped = if normalize then self.stripTypeVar.dealias else self.stripTypeVar // keep error reporting aliased + recur(stripped, bound) /** Is this a generic tuple but not already an instance of one of Tuple1..22? */ def isGenericTuple(using Context): Boolean = diff --git a/tests/pos/i22643.scala b/tests/pos/i22643.scala new file mode 100644 index 000000000000..5b203c678740 --- /dev/null +++ b/tests/pos/i22643.scala @@ -0,0 +1,48 @@ +import language.experimental.namedTuples + + + +object ExhibitB: + + trait JoinB[A <: Tuple, B <: Tuple]: + type NTB = NamedTuple.NamedTuple[Tuple.Concat[A, B], (String, Int)] + val ntB: NTB = ??? + + val joinB: JoinB[Tuple1["nameB"], Tuple1["ageB"]] = ??? + + joinB.ntB.nameB // works + + +object ExhibitC: + + type A = Tuple1["nameC"] + type B = Tuple1["ageC"] + + type NamesC = Tuple.Concat[A, B] + type NTC = NamedTuple.NamedTuple[NamesC, (String, Int)] + val ntC: NTC = ??? + + ntC.nameC // works + + +object ExhibitD: + + trait JoinD[A, B]: + type NamesD = (A, B) + type NTD = NamedTuple.NamedTuple[NamesD, (String, Int)] + val ntD: NTD = ??? + + val joinD: JoinD["nameD", "ageD"] = ??? + + joinD.ntD.nameD // works + +object ExhibitA: + + trait JoinA[A <: Tuple, B <: Tuple]: + type NamesA = Tuple.Concat[A, B] + type NTA = NamedTuple.NamedTuple[NamesA, (String, Int)] + val ntA: NTA = ??? + + val joinA: JoinA[Tuple1["nameA"], Tuple1["ageA"]] = ??? + + joinA.ntA.nameA // fixed From 5b869a43016a8642bd19078c57a18006e11157e7 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Wed, 16 Apr 2025 11:57:04 +0200 Subject: [PATCH 2/4] Add tests from #22645 --- tests/pos/i22645a.scala | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/pos/i22645a.scala diff --git a/tests/pos/i22645a.scala b/tests/pos/i22645a.scala new file mode 100644 index 000000000000..3015d7dd161c --- /dev/null +++ b/tests/pos/i22645a.scala @@ -0,0 +1,44 @@ +import language.experimental.namedTuples +object ExhibitA: // fails + + class SelectableNT[N <: Tuple] extends Selectable: + def selectDynamic(name: String) = ??? + type Names = Tuple.Map[N, [X] =>> X] + type Fields = NamedTuple.NamedTuple[Names, (String, Int)] + + val x = new SelectableNT[("name", "age")] + x.name // fails + + +object ExhibitB: // works + + class SelectableNT[N <: Tuple] extends Selectable: + def selectDynamic(name: String) = ??? + type Fields = NamedTuple.NamedTuple[N, (String, Int)] + + val x = new SelectableNT[("name", "age")] + x.name + + +object ExhibitC: // works + + class SelectableNT[N <: Tuple] extends Selectable: + def selectDynamic(name: String) = ??? + type Fields = NamedTuple.NamedTuple[N, (String, Int)] + + type N = ("name", "age") + val x = new SelectableNT[N] + x.name + + +object ExhibitD: // works + + class SelectableNT[N <: Tuple] extends Selectable: + def selectDynamic(name: String) = ??? + type Fields = NamedTuple.NamedTuple[N, (String, Int)] + + type N = ("name", "age") + type Names = Tuple.Map[N, [X] =>> X] + val x = new SelectableNT[Names] + x.name + From 0cded6554dfcb19a87997f8745b18457eca353d2 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Thu, 17 Apr 2025 10:46:40 +0200 Subject: [PATCH 3/4] Move delias into recur for tupleElementTypesUpTo --- compiler/src/dotty/tools/dotc/core/TypeUtils.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index fe73a18e31d5..ab035db68d9f 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -68,7 +68,7 @@ class TypeUtils: def tupleElementTypesUpTo(bound: Int, normalize: Boolean = true)(using Context): Option[List[Type]] = def recur(tp: Type, bound: Int): Option[List[Type]] = if bound < 0 then Some(Nil) - else (if normalize then tp.normalized else tp).dealias match + else (if normalize then tp.dealias.normalized else tp).dealias match case AppliedType(tycon, hd :: tl :: Nil) if tycon.isRef(defn.PairClass) => recur(tl, bound - 1).map(hd :: _) case tp: AppliedType if defn.isTupleNType(tp) && normalize => @@ -83,8 +83,7 @@ class TypeUtils: case _ => if defn.isTupleClass(tp.typeSymbol) && !normalize then Some(tp.dealias.argInfos) else None - val stripped = if normalize then self.stripTypeVar.dealias else self.stripTypeVar // keep error reporting aliased - recur(stripped, bound) + recur(self.stripTypeVar, bound) /** Is this a generic tuple but not already an instance of one of Tuple1..22? */ def isGenericTuple(using Context): Boolean = From fbaca4c3cab6505b4f93990b91b576a23780609b Mon Sep 17 00:00:00 2001 From: aherlihy Date: Thu, 17 Apr 2025 11:11:52 +0200 Subject: [PATCH 4/4] Readd ExhibitB test with pickling exclusion --- compiler/test/dotc/pos-test-pickling.excludelist | 1 + tests/pos/i22645b.scala | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/pos/i22645b.scala diff --git a/compiler/test/dotc/pos-test-pickling.excludelist b/compiler/test/dotc/pos-test-pickling.excludelist index 23c79affada0..28bce963bfd1 100644 --- a/compiler/test/dotc/pos-test-pickling.excludelist +++ b/compiler/test/dotc/pos-test-pickling.excludelist @@ -71,6 +71,7 @@ i18211.scala named-tuples1.scala i20897.scala i20512.scala +i22645b.scala # Opaque type i5720.scala diff --git a/tests/pos/i22645b.scala b/tests/pos/i22645b.scala new file mode 100644 index 000000000000..acce89f25ee9 --- /dev/null +++ b/tests/pos/i22645b.scala @@ -0,0 +1,12 @@ +import language.experimental.namedTuples +object ExhibitE: // works + + type N = ("name", "age") + type Names = Tuple.Map[N, [X] =>> X] + + class SelectableNT extends Selectable: + def selectDynamic(name: String) = ??? + type Fields = NamedTuple.NamedTuple[Names, (String, Int)] + + val x = new SelectableNT + x.name \ No newline at end of file