Skip to content

Commit 831227a

Browse files
Erasure/Scala2Unpickler hacks to restore binary compatibility
To stay binary compatibility with scala 2.11 binaries, we hijack the unpickling of types to fake an HList structure. Later on in erasure, HList types are eraised back to scala.TupleN (for N <= 22). In addition because the actual scala.TupleN classes are neither `<: Tuple` now `<: TupleCons`, these types needs to be systematically araised to Object. In addition to all these carefully compensating hacks, this also imposes a new contain: the dotty-library, which defines several `Tuple/TupleCons` methods, can should now *always* be compiled by dotty itself. Indeed, when compiled with scalac, these types will stay as they are instead of being eraised to Object.
1 parent 8d7e6b3 commit 831227a

File tree

7 files changed

+307
-22
lines changed

7 files changed

+307
-22
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ object Definitions {
1818
* The limit 22 is chosen for Scala2x interop. It could be something
1919
* else without affecting the set of programs that can be compiled.
2020
*/
21-
val MaxImplementedTupleArity = 6
21+
val MaxImplementedTupleArity = 22
2222

2323
/** The maximum arity N of a function type that's implemented
2424
* as a trait `scala.FunctionN`. Functions of higher arity are possible,

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,10 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
325325

326326
/** The erasure |T| of a type T. This is:
327327
*
328+
* - For dotty.TupleCons:
329+
* - if lenght staticaly known as N, TupleN
330+
* - otherwise Product
331+
* - For dotty.Tuple, java.lang.Object
328332
* - For a refined type scala.Array+[T]:
329333
* - if T is Nothing or Null, []Object
330334
* - otherwise, if T <: Object, []|T|
@@ -356,6 +360,27 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
356360
* - For any other type, exception.
357361
*/
358362
private def apply(tp: Type)(implicit ctx: Context): Type = tp match {
363+
case _ if tp.isRef(defn.TupleConsType.symbol) =>
364+
// Compute the arity of a tuple type, -1 if it's not statically known.
365+
def tupleArity(t: Type, acc: Int = 0): Int = t match {
366+
case RefinedType(RefinedType(_, _, TypeAlias(headType)), _, TypeAlias(tailType)) =>
367+
tupleArity(tailType, acc + 1)
368+
case _ if t.isRef(defn.UnitType.symbol) =>
369+
acc
370+
case tp: TypeProxy =>
371+
tupleArity(tp.underlying, acc)
372+
case AnnotatedType(tpe, _) =>
373+
tupleArity(tpe, acc)
374+
case t =>
375+
-1
376+
}
377+
val arity = tupleArity(tp)
378+
if (arity > 0 && arity <= Definitions.MaxImplementedTupleArity)
379+
defn.TupleNType(arity)
380+
else
381+
defn.ProductType
382+
case _ if tp.isRef(defn.TupleType.symbol) =>
383+
defn.ObjectType
359384
case _: ErasedValueType =>
360385
tp
361386
case tp: TypeRef =>
@@ -467,7 +492,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
467492
// constructor method should not be semi-erased.
468493
else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp)
469494
else this(tp)
470-
case RefinedType(parent, _, _) if !(parent isRef defn.ArrayClass) =>
495+
case RefinedType(parent, _, _) if !(parent isRef defn.ArrayClass) && !(tp isRef defn.TupleConsType.symbol) =>
471496
eraseResult(parent)
472497
case _ =>
473498
this(tp)

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import classfile.ClassfileParser
2626
import scala.collection.{ mutable, immutable }
2727
import scala.collection.mutable.ListBuffer
2828
import scala.annotation.switch
29+
import transform.TupleRewrites.UnfoldedTupleType
2930

3031
object Scala2Unpickler {
3132

@@ -97,7 +98,19 @@ object Scala2Unpickler {
9798
else selfInfo
9899
val tempInfo = new TempClassInfo(denot.owner.thisType, denot.classSymbol, decls, ost)
99100
denot.info = tempInfo // first rough info to avoid CyclicReferences
100-
var parentRefs = ctx.normalizeToClassRefs(parents, cls, decls)
101+
102+
val tupleArity = defn.TupleNSymbol.indexOf(cls)
103+
val tupledParents =
104+
if (cls == defn.UnitType.classSymbol) parents :+ defn.TupleType
105+
else if (tupleArity != -1) {
106+
val productClass = defn.ProductNType(tupleArity).classSymbol
107+
val productTypes = parents.collect { case t: RefinedType => t.baseArgTypes(productClass) }.head
108+
val tupleType = UnfoldedTupleType(productTypes).folded.asTupleConsType
109+
parents :+ tupleType
110+
} else parents
111+
112+
var parentRefs = ctx.normalizeToClassRefs(tupledParents, cls, decls)
113+
101114
if (parentRefs.isEmpty) parentRefs = defn.ObjectType :: Nil
102115
for (tparam <- tparams) {
103116
val tsym = decls.lookup(tparam.name)
@@ -721,7 +734,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
721734
}
722735
else TypeRef(pre, sym.name.asTypeName)
723736
val args = until(end, readTypeRef)
724-
if (sym == defn.ByNameParamClass2x) ExprType(args.head)
737+
val isTupleClass = """Tuple\d+.class""".r.findFirstIn(source.name).nonEmpty
738+
if (!isTupleClass && defn.TupleNSymbol.contains(sym)) UnfoldedTupleType(args).folded.asTupleConsType
739+
else if (sym == defn.ByNameParamClass2x) ExprType(args.head)
725740
else if (args.nonEmpty) tycon.safeAppliedTo(EtaExpandIfHK(sym.typeParams, args))
726741
else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams)
727742
else tycon

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,13 @@ object Erasure extends TypeTestsCasts {
403403
}
404404
}
405405

406-
recur(typed(tree.qualifier, AnySelectionProto))
406+
if (defn.DottyTupleNModule contains tree.qualifier.symbol) {
407+
val arity = defn.DottyTupleNModule.indexOf(tree.qualifier.symbol)
408+
val tupleCompanion = defn.TupleNType(arity).classSymbol.companionModule.symbol
409+
ref(tupleCompanion).select(tree.name).withPos(tree.pos)
410+
} else {
411+
recur(typed(tree.qualifier, AnySelectionProto))
412+
}
407413
}
408414

409415
override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =

0 commit comments

Comments
 (0)