diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ca2dd9b3c1dd..9f6793760017 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -483,16 +483,17 @@ object Checking { } tp1 case tp: ClassInfo => + def transformedParent(tp: Type): Type = tp match { + case ref: TypeRef => ref + case ref: AppliedType => ref + case AnnotatedType(parent, annot) => + AnnotatedType(transformedParent(parent), annot) + case _ => defn.ObjectType // can happen if class files are missing + } tp.derivedClassInfo( prefix = apply(tp.prefix), classParents = - tp.parents.map { p => - apply(p).stripAnnots match { - case ref: TypeRef => ref - case ref: AppliedType => ref - case _ => defn.ObjectType // can happen if class files are missing - } - } + tp.parents.map(p => transformedParent(apply(p))) ) case _ => mapOver(tp) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index f8d83964e82b..ed32410d2f74 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -888,7 +888,7 @@ class Namer { typer: Typer => * (4) If the class is sealed, it is defined in the same compilation unit as the current class */ def checkedParentType(parent: untpd.Tree): Type = { - val ptype = parentType(parent)(ctx.superCallContext).dealias + val ptype = parentType(parent)(ctx.superCallContext).dealiasKeepAnnots if (cls.isRefinementClass) ptype else { val pt = checkClassType(ptype, parent.pos, diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 240999864503..ee076403eb35 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1572,11 +1572,11 @@ class Typer extends Namer checkNoDoubleDeclaration(cls) val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) .withType(dummy.termRef) - checkVariance(impl1) if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls, cdef.namePos) if (cls.is(Case) && cls.derivesFrom(defn.EnumClass)) checkEnum(cdef, cls) val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls) + checkVariance(cdef1) if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) { val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass)) ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true, diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index 0bef02453441..fa626f8b6825 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -95,8 +95,8 @@ class VarianceChecker()(implicit ctx: Context) { this(status, tp.resultType) // params will be checked in their TypeDef or ValDef nodes. case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedVarianceAnnot => status - //case tp: ClassInfo => - // ??? not clear what to do here yet. presumably, it's all checked at local typedefs + case tp: ClassInfo => + foldOver(status, tp.classParents) case _ => foldOver(status, tp) } @@ -142,14 +142,16 @@ class VarianceChecker()(implicit ctx: Context) { ctx.debuglog(s"Skipping variance check of ${sym.showDcl}") case tree: TypeDef => checkVariance(sym, tree.pos) + tree.rhs match { + case rhs: Template => traverseChildren(rhs) + case _ => + } case tree: ValDef => checkVariance(sym, tree.pos) case DefDef(_, tparams, vparamss, _, _) => checkVariance(sym, tree.pos) tparams foreach traverse vparamss foreach (_ foreach traverse) - case Template(_, _, _, body) => - traverseChildren(tree) case _ => } } diff --git a/tests/neg/i2973.scala b/tests/neg/i2973.scala new file mode 100644 index 000000000000..8edd4c66fd70 --- /dev/null +++ b/tests/neg/i2973.scala @@ -0,0 +1,68 @@ +import annotation.unchecked.uncheckedVariance + +class InvClass[U] +trait InvTrait[U] +class ContraClass[-T] +trait ContraTrait[-T] +class CoClass[+T] +trait CoTrait[+T] + +class Contra[-T] extends InvClass[T] // error: contravariant type T occurs in invariant position +class Co[+T] extends InvClass[T] // error + +class ContraInvT[-T] extends InvTrait[T] // error +class ContraCoT[-T] extends CoTrait[T] // error +class CoInvT[+T] extends InvTrait[T] // error +class CoContraT[+T] extends ContraTrait[T] // error +class CoInvCoT[+T] extends InvTrait[T] with CoTrait[T] // error +class CoContraCoT[+T] extends ContraTrait[T] with CoTrait[T] // error + +class ContraContraInv[-T] extends ContraClass[T] with InvTrait[T] // error +class ContraCoInv[-T] extends CoClass[T] with InvTrait[T] // error +class CoCoInv[+T] extends CoClass[T] with InvTrait[T] // error + +class ContraInvContra[-T] extends InvClass[T] with ContraTrait[T] // error +class CoInvContra[+T] extends InvClass[T] with ContraTrait[T] // error +class ContraInvCo[-T] extends InvClass[T] with CoTrait[T] // error +class CoInvCo[+T] extends InvClass[T] with CoTrait[T] // error + +// @uncheckedVariance but in the wrong place +class ContraContraInv2[-T] extends ContraClass[T] @uncheckedVariance with InvTrait[T] // error +class ContraCoInv2[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] // error + +trait Prefix1 { + trait Prefix2 { + class InvClass[U] + trait InvTrait[U] + class ContraClass[-T] + trait ContraTrait[-T] + class CoClass[+T] + trait CoTrait[+T] + } +} + +trait Prefix3 { + def f(v1: Prefix1)(v2: v1.Prefix2): Unit = { + class Contra[-T] extends InvClass[T] // error: contravariant type T occurs in invariant position + class Co[+T] extends InvClass[T] // error + + class ContraInvT[-T] extends InvTrait[T] // error + class ContraCoT[-T] extends CoTrait[T] // error + class CoInvT[+T] extends InvTrait[T] // error + class CoContraT[+T] extends ContraTrait[T] // error + class CoInvCoT[+T] extends InvTrait[T] with CoTrait[T] // error + class CoContraCoT[+T] extends ContraTrait[T] with CoTrait[T] // error + + class ContraContraInv[-T] extends ContraClass[T] with InvTrait[T] // error + class ContraCoInv[-T] extends CoClass[T] with InvTrait[T] // error + class CoCoInv[+T] extends CoClass[T] with InvTrait[T] // error + + class ContraInvContra[-T] extends InvClass[T] with ContraTrait[T] // error + class CoInvContra[+T] extends InvClass[T] with ContraTrait[T] // error + class ContraInvCo[-T] extends InvClass[T] with CoTrait[T] // error + class CoInvCo[+T] extends InvClass[T] with CoTrait[T] // error + + class ContraContraInv2[-T] extends ContraClass[T] @uncheckedVariance with InvTrait[T] // error + class ContraCoInv2[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] // error + } +} diff --git a/tests/pos/i2973.scala b/tests/pos/i2973.scala new file mode 100644 index 000000000000..f89f82070653 --- /dev/null +++ b/tests/pos/i2973.scala @@ -0,0 +1,71 @@ +import annotation.unchecked.uncheckedVariance + +class InvClass[U] +trait InvTrait[U] +class ContraClass[-T] +trait ContraTrait[-T] +class CoClass[+T] +trait CoTrait[+T] + +trait Prefix1 { + trait Prefix2 { + class InvClass[U] + trait InvTrait[U] + class ContraClass[-T] + trait ContraTrait[-T] + class CoClass[+T] + trait CoTrait[+T] + } +} + +class Contra[-T] extends InvClass[T] @uncheckedVariance +class Co[+T] extends InvClass[T] @uncheckedVariance + +class ContraInvT[-T] extends InvTrait[T] @uncheckedVariance +class ContraCoT[-T] extends CoTrait[T] @uncheckedVariance +class CoInvT[+T] extends InvTrait[T] @uncheckedVariance +class CoContraT[+T] extends ContraTrait[T] @uncheckedVariance +class CoInvCoT[+T] extends InvTrait[T] @uncheckedVariance with CoTrait[T] +class CoContraCoT[+T] extends ContraTrait[T] @uncheckedVariance with CoTrait[T] + +class ContraContraInv[-T] extends ContraClass[T] with InvTrait[T] @uncheckedVariance +class ContraCoInv[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance +class CoCoInv[+T] extends CoClass[T] with InvTrait[T] @uncheckedVariance + +class ContraInvContra[-T] extends InvClass[T] @uncheckedVariance with ContraTrait[T] +class CoInvContra[+T] extends InvClass[T] @uncheckedVariance with ContraTrait[T] @uncheckedVariance +class ContraInvCo[-T] extends InvClass[T] @uncheckedVariance with CoTrait[T] @uncheckedVariance +class CoInvCo[+T] extends InvClass[T] @uncheckedVariance with CoTrait[T] + +// @uncheckedVariance but also in the wrong place +class CoInvCoT2[+T] extends InvTrait[T] @uncheckedVariance with CoTrait[T] @uncheckedVariance +class ContraContraInv2[-T] extends ContraClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance +class ContraCoInv2[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance + +trait Prefix3 { + def f(v1: Prefix1)(v2: v1.Prefix2): Unit = { + class Contra[-T] extends InvClass[T] @uncheckedVariance + class Co[+T] extends InvClass[T] @uncheckedVariance + + class ContraInvT[-T] extends InvTrait[T] @uncheckedVariance + class ContraCoT[-T] extends CoTrait[T] @uncheckedVariance + class CoInvT[+T] extends InvTrait[T] @uncheckedVariance + class CoContraT[+T] extends ContraTrait[T] @uncheckedVariance + class CoInvCoT[+T] extends InvTrait[T] @uncheckedVariance with CoTrait[T] + class CoContraCoT[+T] extends ContraTrait[T] @uncheckedVariance with CoTrait[T] + + class ContraContraInv[-T] extends ContraClass[T] with InvTrait[T] @uncheckedVariance + class ContraCoInv[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance + class CoCoInv[+T] extends CoClass[T] with InvTrait[T] @uncheckedVariance + + class ContraInvContra[-T] extends InvClass[T] @uncheckedVariance with ContraTrait[T] + class CoInvContra[+T] extends InvClass[T] @uncheckedVariance with ContraTrait[T] @uncheckedVariance + class ContraInvCo[-T] extends InvClass[T] @uncheckedVariance with CoTrait[T] @uncheckedVariance + class CoInvCo[+T] extends InvClass[T] @uncheckedVariance with CoTrait[T] + + // @uncheckedVariance but also in the wrong place + class CoInvCoT2[+T] extends InvTrait[T] @uncheckedVariance with CoTrait[T] @uncheckedVariance + class ContraContraInv2[-T] extends ContraClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance + class ContraCoInv2[-T] extends CoClass[T] @uncheckedVariance with InvTrait[T] @uncheckedVariance + } +}