Skip to content

Commit c8927bb

Browse files
oderskyWojciechMazur
authored andcommitted
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. [Cherry-picked 366f06a]
1 parent 9b053c2 commit c8927bb

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
@@ -1736,26 +1736,6 @@ class Definitions {
17361736
else TypeOps.nestedPairs(elems)
17371737
}
17381738

1739-
def tupleTypes(tp: Type, bound: Int = Int.MaxValue)(using Context): Option[List[Type]] = {
1740-
@tailrec def rec(tp: Type, acc: List[Type], bound: Int): Option[List[Type]] = tp.normalized.dealias match {
1741-
case _ if bound < 0 => Some(acc.reverse)
1742-
case tp: AppliedType if PairClass == tp.classSymbol => rec(tp.args(1), tp.args.head :: acc, bound - 1)
1743-
case tp: AppliedType if isTupleNType(tp) => Some(acc.reverse ::: tp.args)
1744-
case tp: TermRef if tp.symbol == defn.EmptyTupleModule => Some(acc.reverse)
1745-
case _ => None
1746-
}
1747-
rec(tp.stripTypeVar, Nil, bound)
1748-
}
1749-
1750-
def isSmallGenericTuple(tp: Type)(using Context): Boolean =
1751-
if tp.derivesFrom(defn.PairClass) && !defn.isTupleNType(tp.widenDealias) then
1752-
// If this is a generic tuple we need to cast it to make the TupleN/ members accessible.
1753-
// This works only for generic tuples of known size up to 22.
1754-
defn.tupleTypes(tp.widenTermRefExpr) match
1755-
case Some(elems) if elems.length <= Definitions.MaxTupleArity => true
1756-
case _ => false
1757-
else false
1758-
17591739
def isProductSubType(tp: Type)(using Context): Boolean = tp.derivesFrom(ProductClass)
17601740

17611741
/** 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
@@ -49,22 +49,45 @@ object TypeUtils {
4949
case ps => ps.reduceLeft(AndType(_, _))
5050
}
5151

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

6992
/** The `*:` equivalent of an instance of a Tuple class */
7093
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
@@ -118,7 +118,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
118118
// TupledFunction[?, (...) => R]
119119
tupled.functionArgInfos match
120120
case tupledArgs :: funRet :: Nil =>
121-
defn.tupleTypes(tupledArgs.dealias) match
121+
tupledArgs.tupleElementTypes match
122122
case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) =>
123123
// TupledFunction[?, ((...funArgs...)) => funRet]
124124
funArgs.size

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -698,8 +698,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
698698
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
699699
// but that is done only after we search for extension methods or conversions.
700700
typedSelect(tree, pt, qual)
701-
else if defn.isSmallGenericTuple(qual.tpe) then
702-
val elems = defn.tupleTypes(qual.tpe.widenTermRefExpr).getOrElse(Nil)
701+
else if qual.tpe.isSmallGenericTuple then
702+
val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil)
703703
typedSelect(tree, pt, qual.cast(defn.tupleType(elems)))
704704
else
705705
val tree1 = tryExtensionOrConversion(
@@ -720,7 +720,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
720720
if checkedType1.exists then
721721
gadts.println(i"Member selection healed by GADT approximation")
722722
finish(tree1, qual1, checkedType1)
723-
else if defn.isSmallGenericTuple(qual1.tpe) then
723+
else if qual1.tpe.isSmallGenericTuple then
724724
gadts.println(i"Tuple member selection healed by GADT approximation")
725725
typedSelect(tree, pt, qual1)
726726
else

0 commit comments

Comments
 (0)