Skip to content

Fix #2973: Check variances of class parents #4932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 6 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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 _ =>
}
}
Expand Down
68 changes: 68 additions & 0 deletions tests/neg/i2973.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
71 changes: 71 additions & 0 deletions tests/pos/i2973.scala
Original file line number Diff line number Diff line change
@@ -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
}
}