Skip to content

Commit 1c7be37

Browse files
committed
Erase *: to tuple classes
Was erased to Object before, but this loses precision and breaks binary compatibility with Scala 2.
1 parent 6ea98e3 commit 1c7be37

File tree

4 files changed

+34
-8
lines changed

4 files changed

+34
-8
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,8 @@ class Definitions {
708708

709709
lazy val PairType = ctx.requiredClassRef("scala.*:")
710710
def PairClass(implicit ctx: Context) = PairType.symbol.asClass
711+
lazy val TupleXXLType = ctx.requiredClassRef("scala.TupleXXL")
712+
def TupleXXLClass(implicit ctx: Context) = TupleXXLType.symbol.asClass
711713

712714
// Annotation base classes
713715
lazy val AnnotationType = ctx.requiredClassRef("scala.annotation.Annotation")
@@ -1200,7 +1202,7 @@ class Definitions {
12001202
def isValueSubClass(sym1: Symbol, sym2: Symbol) =
12011203
valueTypeEnc(sym2.asClass.name) % valueTypeEnc(sym1.asClass.name) == 0
12021204

1203-
lazy val erasedToObject = Set[Symbol](AnyClass, AnyValClass, TupleClass, PairClass, SingletonClass)
1205+
lazy val erasedToObject = Set[Symbol](AnyClass, AnyValClass, TupleClass, SingletonClass)
12041206

12051207
// ----- Initialization ---------------------------------------------------
12061208

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import scala.annotation.tailrec
3333
*/
3434
object TypeErasure {
3535

36+
private def erasureDependsOnArgs(tp: Type)(implicit ctx: Context) =
37+
tp.isRef(defn.ArrayClass) || tp.isRef(defn.PairClass)
38+
3639
/** A predicate that tests whether a type is a legal erased type. Only asInstanceOf and
3740
* isInstanceOf may have types that do not satisfy the predicate.
3841
* ErasedValueType is considered an erased type because it is valid after Erasure (it is
@@ -44,7 +47,7 @@ object TypeErasure {
4447
case tp: TypeRef =>
4548
val sym = tp.symbol
4649
sym.isClass &&
47-
sym != defn.ArrayClass &&
50+
!erasureDependsOnArgs(tp) &&
4851
!defn.erasedToObject.contains(sym) &&
4952
!defn.isSyntheticFunctionClass(sym)
5053
case _: TermRef =>
@@ -388,6 +391,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
388391
else eraseNormalClassRef(tp)
389392
case tp: AppliedType =>
390393
if (tp.tycon.isRef(defn.ArrayClass)) eraseArray(tp)
394+
else if (tp.tycon.isRef(defn.PairClass)) erasePair(tp)
391395
else if (tp.isRepeatedParam) apply(tp.underlyingIfRepeated(isJava))
392396
else apply(tp.superType)
393397
case _: TermRef | _: ThisType =>
@@ -418,9 +422,13 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
418422
case tp @ ClassInfo(pre, cls, parents, decls, _) =>
419423
if (cls is Package) tp
420424
else {
425+
def eraseParent(tp: Type) = tp.dealias match {
426+
case tp: AppliedType if tp.tycon.isRef(defn.PairClass) => defn.ObjectType
427+
case _ => apply(tp)
428+
}
421429
val erasedParents: List[Type] =
422430
if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
423-
else parents.mapConserve(apply) match {
431+
else parents.mapConserve(eraseParent) match {
424432
case tr :: trs1 =>
425433
assert(!tr.classSymbol.is(Trait), cls)
426434
val tr1 = if (cls is Trait) defn.ObjectType else tr
@@ -448,6 +456,22 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
448456
else JavaArrayType(arrayErasure(elemtp))
449457
}
450458

459+
private def erasePair(tp: Type)(implicit ctx: Context): Type = {
460+
def tupleArity(tp: Type): Int = tp.dealias match {
461+
case AppliedType(tycon, _ :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
462+
val arity = tupleArity(tl)
463+
if (arity < 0) arity else arity + 1
464+
case tp1 =>
465+
if (tp1.isRef(defn.UnitClass)) 0
466+
else if (defn.isTupleClass(tp1.classSymbol)) tp1.dealias.argInfos.length
467+
else -1
468+
}
469+
val arity = tupleArity(tp)
470+
if (arity < 0) defn.ObjectType
471+
else if (arity <= Definitions.MaxTupleArity) defn.TupleType(arity)
472+
else defn.TupleXXLType
473+
}
474+
451475
/** The erasure of a symbol's info. This is different from `apply` in the way `ExprType`s and
452476
* `PolyType`s are treated. `eraseInfo` maps them them to method types, whereas `apply` maps them
453477
* to the underlying type.
@@ -490,7 +514,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
490514
// constructor method should not be semi-erased.
491515
else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp)
492516
else this(tp)
493-
case AppliedType(tycon, _) if !(tycon isRef defn.ArrayClass) =>
517+
case AppliedType(tycon, _) if !erasureDependsOnArgs(tycon) =>
494518
eraseResult(tycon)
495519
case _ =>
496520
this(tp)
@@ -532,7 +556,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
532556
normalizeClass(sym.asClass).fullName.asTypeName
533557
case tp: AppliedType =>
534558
sigName(
535-
if (tp.tycon.isRef(defn.ArrayClass)) this(tp)
559+
if (erasureDependsOnArgs(tp.tycon)) this(tp)
536560
else if (tp.tycon.typeSymbol.isClass) tp.underlying
537561
else tp.superType)
538562
case ErasedValueType(_, underlying) =>

library/src/scala/TupleXXL.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package scala
2-
import java.util.Arrays.deepEquals
2+
import java.util.Arrays.{deepEquals, deepHashCode}
33

44
final class TupleXXL private (es: Array[Object]) {
55
override def toString = elems.mkString("(", ",", ")")
6-
override def hashCode = getClass.hashCode * 41 + elems.deep.hashCode
6+
override def hashCode = getClass.hashCode * 41 + deepHashCode(elems)
77
override def equals(that: Any) = that match {
88
case that: TupleXXL => deepEquals(this.elems, that.elems)
99
case _ => false

tests/run/tuples1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
object Test extends App {
22
val x0 = (); println(x0)
33
val x1 = 1 *: x0; println(x1)
4-
val x2 = "A" *: x1; println(x2)
4+
val x2 = ("A", 1); println(x2)
55
val x3 = 2 *: x2; println(x3)
66
val x4 = "B" *: x3; println(x4)
77
val x5 = 3 *: x4; println(x5)

0 commit comments

Comments
 (0)