Skip to content

Commit 5939334

Browse files
committed
Refactor some tuple methods
- Move from Definitions to TypeUtils - Unify TypeUtils.tupleElementTypes and Definitions.tupleTypes. They do the same thing. I'd like to move more things out of Definitions and into TypeUtils and SymUtils. Then I'd like to move these files to the core package, and make their operations accessible automatically by having the companion objects of Types and Symbols inherit from them. This is a first step into that direction.
1 parent f831830 commit 5939334

File tree

6 files changed

+50
-47
lines changed

6 files changed

+50
-47
lines changed

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

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1749,26 +1749,6 @@ class Definitions {
17491749
else TypeOps.nestedPairs(elems)
17501750
}
17511751

1752-
def tupleTypes(tp: Type, bound: Int = Int.MaxValue)(using Context): Option[List[Type]] = {
1753-
@tailrec def rec(tp: Type, acc: List[Type], bound: Int): Option[List[Type]] = tp.normalized.dealias match {
1754-
case _ if bound < 0 => Some(acc.reverse)
1755-
case tp: AppliedType if PairClass == tp.classSymbol => rec(tp.args(1), tp.args.head :: acc, bound - 1)
1756-
case tp: AppliedType if isTupleNType(tp) => Some(acc.reverse ::: tp.args)
1757-
case tp: TermRef if tp.symbol == defn.EmptyTupleModule => Some(acc.reverse)
1758-
case _ => None
1759-
}
1760-
rec(tp.stripTypeVar, Nil, bound)
1761-
}
1762-
1763-
def isSmallGenericTuple(tp: Type)(using Context): Boolean =
1764-
if tp.derivesFrom(defn.PairClass) && !defn.isTupleNType(tp.widenDealias) then
1765-
// If this is a generic tuple we need to cast it to make the TupleN/ members accessible.
1766-
// This works only for generic tuples of known size up to 22.
1767-
defn.tupleTypes(tp.widenTermRefExpr) match
1768-
case Some(elems) if elems.length <= Definitions.MaxTupleArity => true
1769-
case _ => false
1770-
else false
1771-
17721752
def isProductSubType(tp: Type)(using Context): Boolean = tp.derivesFrom(ProductClass)
17731753

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
235235

236236
def appliedText(tp: Type): Text = tp match
237237
case tp @ AppliedType(tycon, args) =>
238-
tp.tupleElementTypes match
238+
tp.tupleElementTypesUpTo(200, normalize = false) match
239239
case Some(types) if types.size >= 2 && !printDebug => toTextTuple(types)
240240
case _ =>
241241
val tsym = tycon.typeSymbol

compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import StdNames.*
1010
import Symbols.*
1111
import MegaPhase.*
1212
import Types.*
13+
import transform.TypeUtils.*
1314
import dotty.tools.dotc.ast.tpd
1415

15-
1616
/** Optimize generic operations on tuples */
1717
class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
1818
import tpd.*
@@ -33,7 +33,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
3333

3434
private def transformTupleCons(tree: tpd.Apply)(using Context): Tree = {
3535
val head :: tail :: Nil = tree.args: @unchecked
36-
defn.tupleTypes(tree.tpe.widenTermRefExpr.dealias) match {
36+
tree.tpe.widenTermRefExpr.tupleElementTypes match {
3737
case Some(tpes) =>
3838
// Generate a the tuple directly with TupleN+1.apply
3939
val size = tpes.size
@@ -61,7 +61,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
6161

6262
private def transformTupleTail(tree: tpd.Apply)(using Context): Tree = {
6363
val Apply(_, tup :: Nil) = tree: @unchecked
64-
defn.tupleTypes(tup.tpe.widenTermRefExpr.dealias, MaxTupleArity + 1) match {
64+
tup.tpe.widenTermRefExpr.tupleElementTypesUpTo(MaxTupleArity + 1) match {
6565
case Some(tpes) =>
6666
// Generate a the tuple directly with TupleN-1.apply
6767
val size = tpes.size
@@ -104,7 +104,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
104104

105105
private def transformTupleConcat(tree: tpd.Apply)(using Context): Tree = {
106106
val Apply(_, self :: that :: Nil) = tree: @unchecked
107-
(defn.tupleTypes(self.tpe.widenTermRefExpr.dealias), defn.tupleTypes(that.tpe.widenTermRefExpr.dealias)) match {
107+
(self.tpe.widenTermRefExpr.tupleElementTypes, that.tpe.widenTermRefExpr.tupleElementTypes) match {
108108
case (Some(tpes1), Some(tpes2)) =>
109109
// Generate a the tuple directly with TupleN+M.apply
110110
val n = tpes1.size
@@ -139,7 +139,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
139139

140140
private def transformTupleApply(tree: tpd.Apply)(using Context): Tree = {
141141
val Apply(_, tup :: nTree :: Nil) = tree: @unchecked
142-
(defn.tupleTypes(tup.tpe.widenTermRefExpr.dealias), nTree.tpe) match {
142+
(tup.tpe.widenTermRefExpr.tupleElementTypes, nTree.tpe) match {
143143
case (Some(tpes), nTpe: ConstantType) =>
144144
// Get the element directly with TupleM._n+1 or TupleXXL.productElement(n)
145145
val size = tpes.size
@@ -166,7 +166,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
166166

167167
private def transformTupleToArray(tree: tpd.Apply)(using Context): Tree = {
168168
val Apply(_, tup :: Nil) = tree: @unchecked
169-
defn.tupleTypes(tup.tpe.widen, MaxTupleArity) match {
169+
tup.tpe.widen.tupleElementTypesUpTo(MaxTupleArity) match {
170170
case Some(tpes) =>
171171
val size = tpes.size
172172
if (size == 0)

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

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,45 @@ object TypeUtils {
4545
case ps => ps.reduceLeft(AndType(_, _))
4646
}
4747

48-
/** The element types of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs */
49-
def tupleElementTypes(using Context): Option[List[Type]] = self.dealias match {
50-
case AppliedType(tycon, hd :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
51-
tl.tupleElementTypes.map(hd :: _)
52-
case self: SingletonType =>
53-
if self.termSymbol == defn.EmptyTupleModule then Some(Nil) else None
54-
case AndType(tp1, tp2) =>
55-
// We assume that we have the following property:
56-
// (T1, T2, ..., Tn) & (U1, U2, ..., Un) = (T1 & U1, T2 & U2, ..., Tn & Un)
57-
tp1.tupleElementTypes.zip(tp2.tupleElementTypes).map { case (t1, t2) => t1.intersect(t2) }
58-
case OrType(tp1, tp2) =>
59-
None // We can't combine the type of two tuples
60-
case _ =>
61-
if defn.isTupleClass(self.typeSymbol) then Some(self.dealias.argInfos)
62-
else None
63-
}
48+
/** The element types of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs
49+
*/
50+
def tupleElementTypes(using Context): Option[List[Type]] =
51+
tupleElementTypesUpTo(Int.MaxValue)
52+
53+
/** The element types of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs
54+
* @param bound The maximum number of elements that needs generating minus 1
55+
* The generation will stop once more than bound elems have been generated
56+
* @param normalize If true, normalize and dealias at each step.
57+
* If false, never normalize and dealias only to find *:
58+
* and EmptyTuple types. This is useful for printing.
59+
*/
60+
def tupleElementTypesUpTo(bound: Int, normalize: Boolean = true)(using Context): Option[List[Type]] =
61+
def recur(tp: Type, bound: Int): Option[List[Type]] =
62+
if bound < 0 then Some(Nil)
63+
else (if normalize then tp.normalized else tp).dealias match
64+
case AppliedType(tycon, hd :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
65+
recur(tl, bound - 1).map(hd :: _)
66+
case tp: AppliedType if defn.isTupleNType(tp) && normalize =>
67+
Some(tp.args) // if normalize is set, use the dealiased tuple
68+
// otherwise rely on the default case below to print unaliased tuples.
69+
case tp: TermRef if tp.termSymbol == defn.EmptyTupleModule =>
70+
Some(Nil)
71+
case _ =>
72+
if defn.isTupleClass(tp.typeSymbol) && !normalize then Some(tp.dealias.argInfos)
73+
else None
74+
recur(self.stripTypeVar, bound)
75+
76+
/** Is this a generic tuple that would fit into the range 1..22,
77+
* but is not already an instance of one of Tuple1..22?
78+
* In this case we need to cast it to make the TupleN/ members accessible.
79+
* This works only for generic tuples of known size up to 22.
80+
*/
81+
def isSmallGenericTuple(using Context): Boolean =
82+
self.derivesFrom(defn.PairClass)
83+
&& !defn.isTupleNType(self.widenDealias)
84+
&& self.widenTermRefExpr.tupleElementTypesUpTo(Definitions.MaxTupleArity).match
85+
case Some(elems) if elems.length <= Definitions.MaxTupleArity => true
86+
case _ => false
6487

6588
/** The `*:` equivalent of an instance of a Tuple class */
6689
def toNestedPairs(using Context): Type =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
119119
// TupledFunction[?, (...) => R]
120120
tupled.functionArgInfos match
121121
case tupledArgs :: funRet :: Nil =>
122-
defn.tupleTypes(tupledArgs.dealias) match
122+
tupledArgs.tupleElementTypes match
123123
case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) =>
124124
// TupledFunction[?, ((...funArgs...)) => funRet]
125125
funArgs.size

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -707,8 +707,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
707707
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
708708
// but that is done only after we search for extension methods or conversions.
709709
typedSelect(tree, pt, qual)
710-
else if defn.isSmallGenericTuple(qual.tpe) then
711-
val elems = defn.tupleTypes(qual.tpe.widenTermRefExpr).getOrElse(Nil)
710+
else if qual.tpe.isSmallGenericTuple then
711+
val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil)
712712
typedSelect(tree, pt, qual.cast(defn.tupleType(elems)))
713713
else
714714
val tree1 = tryExtensionOrConversion(
@@ -729,7 +729,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
729729
if checkedType1.exists then
730730
gadts.println(i"Member selection healed by GADT approximation")
731731
finish(tree1, qual1, checkedType1)
732-
else if defn.isSmallGenericTuple(qual1.tpe) then
732+
else if qual1.tpe.isSmallGenericTuple then
733733
gadts.println(i"Tuple member selection healed by GADT approximation")
734734
typedSelect(tree, pt, qual1)
735735
else

0 commit comments

Comments
 (0)