Skip to content

Commit ea6ee13

Browse files
committed
Fix conflict between tupleArity and tupleElementTypes
There are cases where we know the tuple arity but cannot know the flat element types of the tuple. For example `(A, B) | (C, D)` has arity 2 but we cannot create a single tuple containing these elements. In tupleArity we are missing a dealias. We need to investigate this further. At first glance it seems that the signature of `Tuple.apply(x)` is erased to `(Object): Product` and not `(Object): Tuple1`. This would have been caused be the missing dealias. Unfortunately, if this is the case we might not be able to fix the erasure without breaking binary compatibility of the standard library.
1 parent 2917667 commit ea6ee13

File tree

6 files changed

+42
-37
lines changed

6 files changed

+42
-37
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
691691
private def erasePair(tp: Type)(using Context): Type = {
692692
// NOTE: `tupleArity` does not consider TypeRef(EmptyTuple$) equivalent to EmptyTuple.type,
693693
// we fix this for printers, but type erasure should be preserved.
694-
val arity = tp.tupleArity()
694+
val arity = tp.tupleArity
695695
if (arity < 0) defn.ProductClass.typeRef
696696
else if (arity <= Definitions.MaxTupleArity) defn.TupleType(arity).nn
697697
else defn.TupleXXLClass.typeRef

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,17 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
215215

216216
def appliedText(tp: Type): Text = tp match
217217
case tp @ AppliedType(tycon, args) =>
218-
val cls = tycon.typeSymbol
219-
if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*"
220-
else if defn.isFunctionClass(cls) then toTextFunction(args, cls.name.isContextFunction, cls.name.isErasedFunction)
221-
else if tp.tupleArity(relaxEmptyTuple = true) >= 2 && !printDebug then toTextTuple(tp.tupleElementTypes)
222-
else if isInfixType(tp) then
223-
val l :: r :: Nil = args: @unchecked
224-
val opName = tyconName(tycon)
225-
toTextInfixType(tyconName(tycon), l, r) { simpleNameString(tycon.typeSymbol) }
226-
else Str("")
218+
tp.tupleElementTypes match
219+
case Some(types) if types.size >= 2 && !printDebug => toTextTuple(types)
220+
case _ =>
221+
val cls = tycon.typeSymbol
222+
if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*"
223+
else if defn.isFunctionClass(cls) then toTextFunction(args, cls.name.isContextFunction, cls.name.isErasedFunction)
224+
else if isInfixType(tp) then
225+
val l :: r :: Nil = args: @unchecked
226+
val opName = tyconName(tycon)
227+
toTextInfixType(tyconName(tycon), l, r) { simpleNameString(tycon.typeSymbol) }
228+
else Str("")
227229
case _ =>
228230
Str("")
229231

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ object GenericSignatures {
248248
case _ => jsig(elemtp)
249249

250250
case RefOrAppliedType(sym, pre, args) =>
251-
if (sym == defn.PairClass && tp.tupleArity() > Definitions.MaxTupleArity)
251+
if (sym == defn.PairClass && tp.tupleArity > Definitions.MaxTupleArity)
252252
jsig(defn.TupleXXLClass.typeRef)
253253
else if (isTypeParameterInSig(sym, sym0)) {
254254
assert(!sym.isAliasType, "Unexpected alias type: " + sym)

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,44 +51,44 @@ object TypeUtils {
5151

5252
/** The arity of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs,
5353
* or -1 if this is not a tuple type.
54-
*
55-
* @param relaxEmptyTuple if true then TypeRef(EmptyTuple$) =:= EmptyTuple.type
5654
*/
57-
def tupleArity(relaxEmptyTuple: Boolean = false)(using Context): Int = self match {
55+
def tupleArity(using Context): Int = self/*.dealias*/ match { // TODO: why does dealias cause a failure in tests/run-deep-subtype/Tuple-toArray.scala
5856
case AppliedType(tycon, _ :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
59-
val arity = tl.tupleArity(relaxEmptyTuple)
57+
val arity = tl.tupleArity
6058
if (arity < 0) arity else arity + 1
6159
case self: SingletonType =>
6260
if self.termSymbol == defn.EmptyTupleModule then 0 else -1
63-
case self: TypeRef if relaxEmptyTuple && self.classSymbol == defn.EmptyTupleModule.moduleClass =>
64-
0
6561
case self: AndOrType =>
66-
val arity1 = self.tp1.tupleArity(relaxEmptyTuple)
67-
val arity2 = self.tp2.tupleArity(relaxEmptyTuple)
62+
val arity1 = self.tp1.tupleArity
63+
val arity2 = self.tp2.tupleArity
6864
if arity1 == arity2 then arity1 else -1
6965
case _ =>
7066
if defn.isTupleClass(self.classSymbol) then self.dealias.argInfos.length
7167
else -1
7268
}
7369

7470
/** The element types of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs */
75-
def tupleElementTypes(using Context): List[Type] = self match {
71+
def tupleElementTypes(using Context): Option[List[Type]] = self.dealias match {
7672
case AppliedType(tycon, hd :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
77-
hd :: tl.tupleElementTypes
73+
tl.tupleElementTypes.map(hd :: _)
7874
case self: SingletonType =>
79-
assert(self.termSymbol == defn.EmptyTupleModule, "not a tuple")
80-
Nil
81-
case self: TypeRef if self.classSymbol == defn.EmptyTupleModule.moduleClass =>
82-
Nil
83-
case self if defn.isTupleClass(self.classSymbol) =>
84-
self.dealias.argInfos
75+
if self.termSymbol == defn.EmptyTupleModule then Some(Nil) else None
76+
case AndType(tp1, tp2) =>
77+
// We assume that we have the following property:
78+
// (T1, T2, ..., Tn) & (U1, U2, ..., Un) = (T1 & U1, T2 & U2, ..., Tn & Un)
79+
tp1.tupleElementTypes.zip(tp2.tupleElementTypes).map { case (t1, t2) => t1 & t2 }
80+
case OrType(tp1, tp2) =>
81+
None // We can't combine the type of two tuples
8582
case _ =>
86-
throw new AssertionError("not a tuple")
83+
if defn.isTupleClass(self.classSymbol) then Some(self.dealias.argInfos)
84+
else None
8785
}
8886

8987
/** The `*:` equivalent of an instance of a Tuple class */
9088
def toNestedPairs(using Context): Type =
91-
TypeOps.nestedPairs(tupleElementTypes)
89+
tupleElementTypes match
90+
case Some(types) => TypeOps.nestedPairs(types)
91+
case None => throw new AssertionError("not a tuple")
9292

9393
def refinedWith(name: Name, info: Type)(using Context) = RefinedType(self, name, info)
9494

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -358,13 +358,15 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
358358
reduce(tp.underlying)
359359
case tp: HKTypeLambda if tp.resultType.isInstanceOf[HKTypeLambda] =>
360360
Left(i"its subpart `$tp` is not a supported kind (either `*` or `* -> *`)")
361-
case tp @ AppliedType(tref: TypeRef, _)
362-
if tref.symbol == defn.PairClass
363-
&& tp.tupleArity(relaxEmptyTuple = true) > 0 =>
364-
// avoid type aliases for tuples
365-
Right(MirrorSource.GenericTuple(tp.tupleElementTypes))
366361
case tp: TypeProxy =>
367-
reduce(tp.underlying)
362+
tp match
363+
case tp @ AppliedType(tref: TypeRef, _) if tref.symbol == defn.PairClass =>
364+
tp.tupleElementTypes match
365+
case Some(types) =>
366+
// avoid type aliases for tuples
367+
Right(MirrorSource.GenericTuple(types))
368+
case _ => reduce(tp.underlying)
369+
case _ => reduce(tp.underlying)
368370
case tp @ AndType(l, r) =>
369371
for
370372
lsrc <- reduce(l)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,8 +2768,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
27682768
typed(desugar.smallTuple(tree).withSpan(tree.span), pt)
27692769
else {
27702770
val pts =
2771-
if (arity == pt.tupleArity()) pt.tupleElementTypes
2772-
else List.fill(arity)(defn.AnyType)
2771+
pt.tupleElementTypes match
2772+
case Some(types) if types.size == arity => types
2773+
case _ => List.fill(arity)(defn.AnyType)
27732774
val elems = tree.trees.lazyZip(pts).map(
27742775
if ctx.mode.is(Mode.Type) then typedType(_, _, mapPatternBounds = true)
27752776
else typed(_, _))

0 commit comments

Comments
 (0)