Skip to content

Commit 26f0d75

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 b9f3ab7 commit 26f0d75

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
@@ -711,6 +711,8 @@ class Definitions {
711711

712712
lazy val PairType = ctx.requiredClassRef("scala.*:")
713713
def PairClass(implicit ctx: Context) = PairType.symbol.asClass
714+
lazy val TupleXXLType = ctx.requiredClassRef("scala.TupleXXL")
715+
def TupleXXLClass(implicit ctx: Context) = TupleXXLType.symbol.asClass
714716

715717
// Annotation base classes
716718
lazy val AnnotationType = ctx.requiredClassRef("scala.annotation.Annotation")
@@ -1203,7 +1205,7 @@ class Definitions {
12031205
def isValueSubClass(sym1: Symbol, sym2: Symbol) =
12041206
valueTypeEnc(sym2.asClass.name) % valueTypeEnc(sym1.asClass.name) == 0
12051207

1206-
lazy val erasedToObject = Set[Symbol](AnyClass, AnyValClass, TupleClass, PairClass, SingletonClass)
1208+
lazy val erasedToObject = Set[Symbol](AnyClass, AnyValClass, TupleClass, SingletonClass)
12071209

12081210
// ----- Initialization ---------------------------------------------------
12091211

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 =>
@@ -389,6 +392,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
389392
else eraseNormalClassRef(tp)
390393
case tp: AppliedType =>
391394
if (tp.tycon.isRef(defn.ArrayClass)) eraseArray(tp)
395+
else if (tp.tycon.isRef(defn.PairClass)) erasePair(tp)
392396
else if (tp.isRepeatedParam) apply(tp.underlyingIfRepeated(isJava))
393397
else apply(tp.superType)
394398
case _: TermRef | _: ThisType =>
@@ -419,9 +423,13 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
419423
case tp @ ClassInfo(pre, cls, parents, decls, _) =>
420424
if (cls is Package) tp
421425
else {
426+
def eraseParent(tp: Type) = tp.dealias match {
427+
case tp: AppliedType if tp.tycon.isRef(defn.PairClass) => defn.ObjectType
428+
case _ => apply(tp)
429+
}
422430
val erasedParents: List[Type] =
423431
if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
424-
else parents.mapConserve(apply) match {
432+
else parents.mapConserve(eraseParent) match {
425433
case tr :: trs1 =>
426434
assert(!tr.classSymbol.is(Trait), cls)
427435
val tr1 = if (cls is Trait) defn.ObjectType else tr
@@ -449,6 +457,22 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
449457
else JavaArrayType(arrayErasure(elemtp))
450458
}
451459

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