Skip to content

Commit 50dc2a2

Browse files
committed
Move tupleArity to TypeErasure and document its shortcomings
1 parent 66b6dff commit 50dc2a2

File tree

3 files changed

+29
-23
lines changed

3 files changed

+29
-23
lines changed

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,32 @@ object TypeErasure {
7474
private def erasureDependsOnArgs(sym: Symbol)(using Context) =
7575
sym == defn.ArrayClass || sym == defn.PairClass || isDerivedValueClass(sym)
7676

77+
/** The arity of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs.
78+
*
79+
* NOTE: This method is used to determine how to erase tuples, so it can
80+
* only be changed in very limited ways without breaking
81+
* binary-compatibility. In particular, note that it returns -1 for
82+
* all tuples that end with the `EmptyTuple` type alias instead of
83+
* `EmptyTuple.type` because of a missing dealias, but this is now
84+
* impossible to fix.
85+
*
86+
* @return The arity if it can be determined or -1 otherwise.
87+
*/
88+
def tupleArity(tp: Type)(using Context): Int = tp/*.dealias*/ match {
89+
case AppliedType(tycon, _ :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
90+
val arity = tupleArity(tl)
91+
if (arity < 0) arity else arity + 1
92+
case tp: SingletonType =>
93+
if tp.termSymbol == defn.EmptyTupleModule then 0 else -1
94+
case tp: AndOrType =>
95+
val arity1 = tupleArity(tp.tp1)
96+
val arity2 = tupleArity(tp.tp2)
97+
if arity1 == arity2 then arity1 else -1
98+
case _ =>
99+
if defn.isTupleNType(tp) then tp.dealias.argInfos.length
100+
else -1
101+
}
102+
77103
def normalizeClass(cls: ClassSymbol)(using Context): ClassSymbol = {
78104
if (cls.owner == defn.ScalaPackageClass) {
79105
if (defn.specialErasure.contains(cls))
@@ -740,9 +766,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
740766
}
741767

742768
private def erasePair(tp: Type)(using Context): Type = {
743-
// NOTE: `tupleArity` does not consider TypeRef(EmptyTuple$) equivalent to EmptyTuple.type,
744-
// we fix this for printers, but type erasure should be preserved.
745-
val arity = tp.tupleArity
769+
val arity = tupleArity(tp)
746770
if (arity < 0) defn.ProductClass.typeRef
747771
else if (arity <= Definitions.MaxTupleArity) defn.TupleType(arity).nn
748772
else defn.TupleXXLClass.typeRef

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import core.Flags._
1111
import core.Names.Name
1212
import core.Symbols._
1313
import core.TypeApplications.{EtaExpansion, TypeParamInfo}
14-
import core.TypeErasure.{erasedGlb, erasure, fullErasure, isGenericArrayElement}
14+
import core.TypeErasure.{erasedGlb, erasure, fullErasure, isGenericArrayElement, tupleArity}
1515
import core.Types._
1616
import core.classfile.ClassfileConstants
1717
import SymUtils._
@@ -255,7 +255,7 @@ object GenericSignatures {
255255
case _ => jsig(elemtp)
256256

257257
case RefOrAppliedType(sym, pre, args) =>
258-
if (sym == defn.PairClass && tp.tupleArity > Definitions.MaxTupleArity)
258+
if (sym == defn.PairClass && tupleArity(tp) > Definitions.MaxTupleArity)
259259
jsig(defn.TupleXXLClass.typeRef)
260260
else if (isTypeParameterInSig(sym, sym0)) {
261261
assert(!sym.isAliasType, "Unexpected alias type: " + sym)

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

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,6 @@ object TypeUtils {
4949
case ps => ps.reduceLeft(AndType(_, _))
5050
}
5151

52-
/** The arity of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs,
53-
* or -1 if this is not a tuple type.
54-
*/
55-
def tupleArity(using Context): Int = self/*.dealias*/ match { // TODO: why does dealias cause a failure in tests/run-deep-subtype/Tuple-toArray.scala
56-
case AppliedType(tycon, _ :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
57-
val arity = tl.tupleArity
58-
if (arity < 0) arity else arity + 1
59-
case self: SingletonType =>
60-
if self.termSymbol == defn.EmptyTupleModule then 0 else -1
61-
case self: AndOrType =>
62-
val arity1 = self.tp1.tupleArity
63-
val arity2 = self.tp2.tupleArity
64-
if arity1 == arity2 then arity1 else -1
65-
case _ =>
66-
if defn.isTupleNType(self) then self.dealias.argInfos.length
67-
else -1
68-
}
69-
7052
/** The element types of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs */
7153
def tupleElementTypes(using Context): Option[List[Type]] = self.dealias match {
7254
case AppliedType(tycon, hd :: tl :: Nil) if tycon.isRef(defn.PairClass) =>

0 commit comments

Comments
 (0)