Skip to content

Commit 32f95e0

Browse files
committed
Refactor check into NamedTuple.unapply for consistency
1 parent 6180de2 commit 32f95e0

File tree

7 files changed

+35
-33
lines changed

7 files changed

+35
-33
lines changed

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,10 +1337,25 @@ class Definitions {
13371337
object NamedTuple:
13381338
def apply(nmes: Type, vals: Type)(using Context): Type =
13391339
AppliedType(NamedTupleTypeRef, nmes :: vals :: Nil)
1340-
def unapply(t: Type)(using Context): Option[(Type, Type)] = t match
1341-
case AppliedType(tycon, nmes :: vals :: Nil) if tycon.typeSymbol == NamedTupleTypeRef.symbol =>
1342-
Some((nmes, vals))
1343-
case _ => None
1340+
def unapply(t: Type)(using Context): Option[(Type, Type)] =
1341+
t match
1342+
case AppliedType(tycon, nmes :: vals :: Nil) if tycon.typeSymbol == NamedTupleTypeRef.symbol =>
1343+
Some((nmes, vals))
1344+
case tp: TypeProxy =>
1345+
val t = unapply(tp.superType); t
1346+
case tp: OrType =>
1347+
(unapply(tp.tp1), unapply(tp.tp2)) match
1348+
case (Some(lhsName, lhsVal), Some(rhsName, rhsVal)) if lhsName == rhsName =>
1349+
Some(lhsName, lhsVal | rhsVal)
1350+
case _ => None
1351+
case tp: AndType =>
1352+
(unapply(tp.tp1), unapply(tp.tp2)) match
1353+
case (Some(lhsName, lhsVal), Some(rhsName, rhsVal)) if lhsName == rhsName =>
1354+
Some(lhsName, lhsVal & rhsVal)
1355+
case (lhs, None) => lhs
1356+
case (None, rhs) => rhs
1357+
case _ => None
1358+
case _ => None
13441359

13451360
final def isCompiletime_S(sym: Symbol)(using Context): Boolean =
13461361
sym.name == tpnme.S && sym.owner == CompiletimeOpsIntModuleClass

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

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -129,26 +129,21 @@ class TypeUtils:
129129

130130
def namedTupleElementTypesUpTo(bound: Int, derived: Boolean, normalize: Boolean = true)(using Context): List[(TermName, Type)] =
131131
(if normalize then self.normalized else self).dealias match
132+
// for desugaring and printer, ignore derived types to avoid infinite recursion in NamedTuple.unapply
133+
case AppliedType(tycon, nmes :: vals :: Nil) if !derived && tycon.typeSymbol == defn.NamedTupleTypeRef.symbol =>
134+
val names = nmes.tupleElementTypesUpTo(bound, normalize).getOrElse(Nil).map(_.dealias).map:
135+
case ConstantType(Constant(str: String)) => str.toTermName
136+
case t => throw TypeError(em"Malformed NamedTuple: names must be string types, but $t was found.")
137+
val values = vals.tupleElementTypesUpTo(bound, normalize).getOrElse(Nil)
138+
names.zip(values)
139+
case t if !derived => Nil
140+
// default cause, used for post-typing
132141
case defn.NamedTuple(nmes, vals) =>
133142
val names = nmes.tupleElementTypesUpTo(bound, normalize).getOrElse(Nil).map(_.dealias).map:
134143
case ConstantType(Constant(str: String)) => str.toTermName
135144
case t => throw TypeError(em"Malformed NamedTuple: names must be string types, but $t was found.")
136145
val values = vals.tupleElementTypesUpTo(bound, normalize).getOrElse(Nil)
137146
names.zip(values)
138-
case tp: TypeProxy if derived =>
139-
tp.superType.namedTupleElementTypesUpTo(bound - 1, normalize)
140-
case tp: OrType if derived =>
141-
val lhs = tp.tp1.namedTupleElementTypesUpTo(bound - 1, normalize)
142-
val rhs = tp.tp2.namedTupleElementTypesUpTo(bound - 1, normalize)
143-
if (lhs.map(_._1) != rhs.map(_._1)) throw TypeError(em"Malformed Union Type: Named Tuple elements must be the same, but $lhs and $rhs were found.")
144-
lhs.zip(rhs).map((lhs, rhs) => (lhs._1, lhs._2 | rhs._2))
145-
case tp: AndType if derived =>
146-
(tp.tp1.namedTupleElementTypesUpTo(bound - 1, normalize), tp.tp2.namedTupleElementTypesUpTo(bound - 1, normalize)) match
147-
case (Nil, rhs) => rhs
148-
case (lhs, Nil) => lhs
149-
case (lhs, rhs) =>
150-
if (lhs.map(_._1) != rhs.map(_._1)) throw TypeError(em"Malformed Intersection Type: Named Tuple elements must be the same, but $lhs and $rhs were found.")
151-
lhs.zip(rhs).map((lhs, rhs) => (lhs._1, lhs._2 & rhs._2))
152147
case t =>
153148
Nil
154149

@@ -159,15 +154,6 @@ class TypeUtils:
159154
case defn.NamedTuple(_, _) => true
160155
case _ => false
161156

162-
def derivesFromNamedTuple(using Context): Boolean = self match
163-
case defn.NamedTuple(_, _) => true
164-
case tp: MatchType =>
165-
tp.bound.derivesFromNamedTuple || tp.reduced.derivesFromNamedTuple
166-
case tp: TypeProxy => tp.superType.derivesFromNamedTuple
167-
case tp: AndType => tp.tp1.derivesFromNamedTuple || tp.tp2.derivesFromNamedTuple
168-
case tp: OrType => tp.tp1.derivesFromNamedTuple && tp.tp2.derivesFromNamedTuple
169-
case _ => false
170-
171157
/** Drop all named elements in tuple type */
172158
def stripNamedTuple(using Context): Type = self.normalized.dealias match
173159
case defn.NamedTuple(_, vals) =>

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ object Completion:
543543
.groupByName
544544

545545
val qualTpe = qual.typeOpt
546-
if qualTpe.derivesFromNamedTuple then
546+
if qualTpe.isNamedTupleType then
547547
namedTupleCompletionsFromType(qualTpe)
548548
else if qualTpe.derivesFrom(defn.SelectableClass) then
549549
val pre = if !TypeOps.isLegalPrefix(qualTpe) then Types.SkolemType(qualTpe) else qualTpe

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
248248
def appliedText(tp: Type): Text = tp match
249249
case tp @ AppliedType(tycon, args) =>
250250
val namedElems =
251-
try tp.namedTupleElementTypesUpTo(200, false, normalize = false) // TODO: should the printer use derived or not?
252-
catch case ex: TypeError => Nil
251+
try tp.namedTupleElementTypesUpTo(200, false, normalize = false)
252+
catch
253+
case ex: TypeError => Nil
253254
if namedElems.nonEmpty then
254255
toTextNamedTuple(namedElems)
255256
else tp.tupleElementTypesUpTo(200, normalize = false) match

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,7 @@ trait Implicits:
876876
|| inferView(dummyTreeOfType(from), to)
877877
(using ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState()).isSuccess
878878
// TODO: investigate why we can't TyperState#test here
879-
|| from.widen.derivesFromNamedTuple && to.derivesFrom(defn.TupleClass)
879+
|| from.widen.isNamedTupleType && to.derivesFrom(defn.TupleClass)
880880
&& from.widen.stripNamedTuple <:< to
881881
)
882882

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4663,7 +4663,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
46634663
case _: SelectionProto =>
46644664
tree // adaptations for selections are handled in typedSelect
46654665
case _ if ctx.mode.is(Mode.ImplicitsEnabled) && tree.tpe.isValueType =>
4666-
if tree.tpe.derivesFromNamedTuple && pt.derivesFrom(defn.TupleClass) then
4666+
if tree.tpe.isNamedTupleType && pt.derivesFrom(defn.TupleClass) then
46674667
readapt(typed(untpd.Select(untpd.TypedSplice(tree), nme.toTuple)))
46684668
else if pt.isRef(defn.AnyValClass, skipRefined = false)
46694669
|| pt.isRef(defn.ObjectClass, skipRefined = false)

tests/run/i22150.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ val directionsNT = IArray(
1010
val IArray(UpNT @ _, _, _, _) = directionsNT
1111

1212
object NT:
13-
// def foo[T <: (x: Int, y: String)](tup: T): Int =
13+
// def foo[T <: (x: Int, y: String)](tup: T): Int = // TODO 3: this fails with similar error to https://github.com/scala/scala3/issues/22324 not sure if related?
1414
// tup.x
1515

1616
def union[T](tup: (x: Int, y: String) | (x: Int, y: String)): Int =

0 commit comments

Comments
 (0)