Skip to content

Commit 724376e

Browse files
authored
Merge pull request #4932 from dotty-staging/fix-#2973
Fix #2973: Check variances of class parents
2 parents 7914492 + 131b36c commit 724376e

File tree

6 files changed

+155
-13
lines changed

6 files changed

+155
-13
lines changed

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -483,16 +483,17 @@ object Checking {
483483
}
484484
tp1
485485
case tp: ClassInfo =>
486+
def transformedParent(tp: Type): Type = tp match {
487+
case ref: TypeRef => ref
488+
case ref: AppliedType => ref
489+
case AnnotatedType(parent, annot) =>
490+
AnnotatedType(transformedParent(parent), annot)
491+
case _ => defn.ObjectType // can happen if class files are missing
492+
}
486493
tp.derivedClassInfo(
487494
prefix = apply(tp.prefix),
488495
classParents =
489-
tp.parents.map { p =>
490-
apply(p).stripAnnots match {
491-
case ref: TypeRef => ref
492-
case ref: AppliedType => ref
493-
case _ => defn.ObjectType // can happen if class files are missing
494-
}
495-
}
496+
tp.parents.map(p => transformedParent(apply(p)))
496497
)
497498
case _ =>
498499
mapOver(tp)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ class Namer { typer: Typer =>
888888
* (4) If the class is sealed, it is defined in the same compilation unit as the current class
889889
*/
890890
def checkedParentType(parent: untpd.Tree): Type = {
891-
val ptype = parentType(parent)(ctx.superCallContext).dealias
891+
val ptype = parentType(parent)(ctx.superCallContext).dealiasKeepAnnots
892892
if (cls.isRefinementClass) ptype
893893
else {
894894
val pt = checkClassType(ptype, parent.pos,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1572,11 +1572,11 @@ class Typer extends Namer
15721572
checkNoDoubleDeclaration(cls)
15731573
val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1)
15741574
.withType(dummy.termRef)
1575-
checkVariance(impl1)
15761575
if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper)
15771576
checkRealizableBounds(cls, cdef.namePos)
15781577
if (cls.is(Case) && cls.derivesFrom(defn.EnumClass)) checkEnum(cdef, cls)
15791578
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls)
1579+
checkVariance(cdef1)
15801580
if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) {
15811581
val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass))
15821582
ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true,

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ class VarianceChecker()(implicit ctx: Context) {
9595
this(status, tp.resultType) // params will be checked in their TypeDef or ValDef nodes.
9696
case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedVarianceAnnot =>
9797
status
98-
//case tp: ClassInfo =>
99-
// ??? not clear what to do here yet. presumably, it's all checked at local typedefs
98+
case tp: ClassInfo =>
99+
foldOver(status, tp.classParents)
100100
case _ =>
101101
foldOver(status, tp)
102102
}
@@ -142,14 +142,16 @@ class VarianceChecker()(implicit ctx: Context) {
142142
ctx.debuglog(s"Skipping variance check of ${sym.showDcl}")
143143
case tree: TypeDef =>
144144
checkVariance(sym, tree.pos)
145+
tree.rhs match {
146+
case rhs: Template => traverseChildren(rhs)
147+
case _ =>
148+
}
145149
case tree: ValDef =>
146150
checkVariance(sym, tree.pos)
147151
case DefDef(_, tparams, vparamss, _, _) =>
148152
checkVariance(sym, tree.pos)
149153
tparams foreach traverse
150154
vparamss foreach (_ foreach traverse)
151-
case Template(_, _, _, body) =>
152-
traverseChildren(tree)
153155
case _ =>
154156
}
155157
}

tests/neg/i2973.scala

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import annotation.unchecked.uncheckedVariance
2+
3+
class InvClass[U]
4+
trait InvTrait[U]
5+
class ContraClass[-T]
6+
trait ContraTrait[-T]
7+
class CoClass[+T]
8+
trait CoTrait[+T]
9+
10+
class Contra[-T] extends InvClass[T] // error: contravariant type T occurs in invariant position
11+
class Co[+T] extends InvClass[T] // error
12+
13+
class ContraInvT[-T] extends InvTrait[T] // error
14+
class ContraCoT[-T] extends CoTrait[T] // error
15+
class CoInvT[+T] extends InvTrait[T] // error
16+
class CoContraT[+T] extends ContraTrait[T] // error
17+
class CoInvCoT[+T] extends InvTrait[T] with CoTrait[T] // error
18+
class CoContraCoT[+T] extends ContraTrait[T] with CoTrait[T] // error
19+
20+
class ContraContraInv[-T] extends ContraClass[T] with InvTrait[T] // error
21+
class ContraCoInv[-T] extends CoClass[T] with InvTrait[T] // error
22+
class CoCoInv[+T] extends CoClass[T] with InvTrait[T] // error
23+
24+
class ContraInvContra[-T] extends InvClass[T] with ContraTrait[T] // error
25+
class CoInvContra[+T] extends InvClass[T] with ContraTrait[T] // error
26+
class ContraInvCo[-T] extends InvClass[T] with CoTrait[T] // error
27+
class CoInvCo[+T] extends InvClass[T] with CoTrait[T] // error
28+
29+
// @uncheckedVariance but in the wrong place
30+
class ContraContraInv2[-T] extends ContraClass[T] @uncheckedVariance with InvTrait[T] // error
31+
class ContraCoInv2[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] // error
32+
33+
trait Prefix1 {
34+
trait Prefix2 {
35+
class InvClass[U]
36+
trait InvTrait[U]
37+
class ContraClass[-T]
38+
trait ContraTrait[-T]
39+
class CoClass[+T]
40+
trait CoTrait[+T]
41+
}
42+
}
43+
44+
trait Prefix3 {
45+
def f(v1: Prefix1)(v2: v1.Prefix2): Unit = {
46+
class Contra[-T] extends InvClass[T] // error: contravariant type T occurs in invariant position
47+
class Co[+T] extends InvClass[T] // error
48+
49+
class ContraInvT[-T] extends InvTrait[T] // error
50+
class ContraCoT[-T] extends CoTrait[T] // error
51+
class CoInvT[+T] extends InvTrait[T] // error
52+
class CoContraT[+T] extends ContraTrait[T] // error
53+
class CoInvCoT[+T] extends InvTrait[T] with CoTrait[T] // error
54+
class CoContraCoT[+T] extends ContraTrait[T] with CoTrait[T] // error
55+
56+
class ContraContraInv[-T] extends ContraClass[T] with InvTrait[T] // error
57+
class ContraCoInv[-T] extends CoClass[T] with InvTrait[T] // error
58+
class CoCoInv[+T] extends CoClass[T] with InvTrait[T] // error
59+
60+
class ContraInvContra[-T] extends InvClass[T] with ContraTrait[T] // error
61+
class CoInvContra[+T] extends InvClass[T] with ContraTrait[T] // error
62+
class ContraInvCo[-T] extends InvClass[T] with CoTrait[T] // error
63+
class CoInvCo[+T] extends InvClass[T] with CoTrait[T] // error
64+
65+
class ContraContraInv2[-T] extends ContraClass[T] @uncheckedVariance with InvTrait[T] // error
66+
class ContraCoInv2[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] // error
67+
}
68+
}

tests/pos/i2973.scala

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import annotation.unchecked.uncheckedVariance
2+
3+
class InvClass[U]
4+
trait InvTrait[U]
5+
class ContraClass[-T]
6+
trait ContraTrait[-T]
7+
class CoClass[+T]
8+
trait CoTrait[+T]
9+
10+
trait Prefix1 {
11+
trait Prefix2 {
12+
class InvClass[U]
13+
trait InvTrait[U]
14+
class ContraClass[-T]
15+
trait ContraTrait[-T]
16+
class CoClass[+T]
17+
trait CoTrait[+T]
18+
}
19+
}
20+
21+
class Contra[-T] extends InvClass[T] @uncheckedVariance
22+
class Co[+T] extends InvClass[T] @uncheckedVariance
23+
24+
class ContraInvT[-T] extends InvTrait[T] @uncheckedVariance
25+
class ContraCoT[-T] extends CoTrait[T] @uncheckedVariance
26+
class CoInvT[+T] extends InvTrait[T] @uncheckedVariance
27+
class CoContraT[+T] extends ContraTrait[T] @uncheckedVariance
28+
class CoInvCoT[+T] extends InvTrait[T] @uncheckedVariance with CoTrait[T]
29+
class CoContraCoT[+T] extends ContraTrait[T] @uncheckedVariance with CoTrait[T]
30+
31+
class ContraContraInv[-T] extends ContraClass[T] with InvTrait[T] @uncheckedVariance
32+
class ContraCoInv[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance
33+
class CoCoInv[+T] extends CoClass[T] with InvTrait[T] @uncheckedVariance
34+
35+
class ContraInvContra[-T] extends InvClass[T] @uncheckedVariance with ContraTrait[T]
36+
class CoInvContra[+T] extends InvClass[T] @uncheckedVariance with ContraTrait[T] @uncheckedVariance
37+
class ContraInvCo[-T] extends InvClass[T] @uncheckedVariance with CoTrait[T] @uncheckedVariance
38+
class CoInvCo[+T] extends InvClass[T] @uncheckedVariance with CoTrait[T]
39+
40+
// @uncheckedVariance but also in the wrong place
41+
class CoInvCoT2[+T] extends InvTrait[T] @uncheckedVariance with CoTrait[T] @uncheckedVariance
42+
class ContraContraInv2[-T] extends ContraClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance
43+
class ContraCoInv2[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance
44+
45+
trait Prefix3 {
46+
def f(v1: Prefix1)(v2: v1.Prefix2): Unit = {
47+
class Contra[-T] extends InvClass[T] @uncheckedVariance
48+
class Co[+T] extends InvClass[T] @uncheckedVariance
49+
50+
class ContraInvT[-T] extends InvTrait[T] @uncheckedVariance
51+
class ContraCoT[-T] extends CoTrait[T] @uncheckedVariance
52+
class CoInvT[+T] extends InvTrait[T] @uncheckedVariance
53+
class CoContraT[+T] extends ContraTrait[T] @uncheckedVariance
54+
class CoInvCoT[+T] extends InvTrait[T] @uncheckedVariance with CoTrait[T]
55+
class CoContraCoT[+T] extends ContraTrait[T] @uncheckedVariance with CoTrait[T]
56+
57+
class ContraContraInv[-T] extends ContraClass[T] with InvTrait[T] @uncheckedVariance
58+
class ContraCoInv[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance
59+
class CoCoInv[+T] extends CoClass[T] with InvTrait[T] @uncheckedVariance
60+
61+
class ContraInvContra[-T] extends InvClass[T] @uncheckedVariance with ContraTrait[T]
62+
class CoInvContra[+T] extends InvClass[T] @uncheckedVariance with ContraTrait[T] @uncheckedVariance
63+
class ContraInvCo[-T] extends InvClass[T] @uncheckedVariance with CoTrait[T] @uncheckedVariance
64+
class CoInvCo[+T] extends InvClass[T] @uncheckedVariance with CoTrait[T]
65+
66+
// @uncheckedVariance but also in the wrong place
67+
class CoInvCoT2[+T] extends InvTrait[T] @uncheckedVariance with CoTrait[T] @uncheckedVariance
68+
class ContraContraInv2[-T] extends ContraClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance
69+
class ContraCoInv2[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance
70+
}
71+
}

0 commit comments

Comments
 (0)