Permalink
Browse files

SI-6566, unsoundness with alias variance.

This wasn't as bad as it could have been. All these changes
plug soundness holes in trunk. Mostly we're looking at type
aliases which were merely protected when they had to be
protected[this] not to allow unsound variance crossover.
  • Loading branch information...
1 parent 567df8e commit a419799f872d5aae99728d711b1ced89e06804a8 @paulp paulp committed Jan 2, 2013
@@ -77,7 +77,7 @@ trait TraversableLike[+A, +Repr] extends Any
import Traversable.breaks._
/** The type implementing this traversable */
- protected type Self = Repr
+ protected[this] type Self = Repr
/** The collection of type $coll underlying this `TraversableLike` object.
* By default this is implemented as the `TraversableLike` object itself,
@@ -19,7 +19,7 @@ import scala.reflect.ClassTag
* @author Aleksandar Prokopec
*/
abstract class GenericClassTagCompanion[+CC[X] <: Traversable[X]] {
- type Coll = CC[_]
+ protected[this] type Coll = CC[_]
def newBuilder[A](implicit ord: ClassTag[A]): Builder[A, CC[A]]
@@ -24,7 +24,7 @@ import scala.language.higherKinds
*/
abstract class GenericCompanion[+CC[X] <: GenTraversable[X]] {
/** The underlying collection type with unknown element type */
- type Coll = CC[_]
+ protected[this] type Coll = CC[_]
/** The default builder for `$Coll` objects.
* @tparam A the type of the ${coll}'s elements
@@ -19,7 +19,7 @@ import scala.language.higherKinds
* @since 2.8
*/
abstract class GenericOrderedCompanion[+CC[X] <: Traversable[X]] {
- type Coll = CC[_]
+ protected[this] type Coll = CC[_]
def newBuilder[A](implicit ord: Ordering[A]): Builder[A, CC[A]]
@@ -46,7 +46,7 @@ private[collection] abstract class TrieIterator[+T](elems: Array[Iterable[T]]) e
case x: HashSetCollision1[_] => x.ks.map(x => HashSet(x)).toArray
}).asInstanceOf[Array[Iterable[T]]]
- private type SplitIterators = ((Iterator[T], Int), Iterator[T])
+ private[this] type SplitIterators = ((Iterator[T], Int), Iterator[T])
private def isTrie(x: AnyRef) = x match {
case _: HashTrieMap[_,_] | _: HashTrieSet[_] => true
@@ -44,8 +44,8 @@ trait ParSeqLike[+T, +Repr <: ParSeq[T], +Sequential <: Seq[T] with SeqLike[T, S
extends scala.collection.GenSeqLike[T, Repr]
with ParIterableLike[T, Repr, Sequential] {
self =>
-
- type SuperParIterator = IterableSplitter[T]
+
+ protected[this] type SuperParIterator = IterableSplitter[T]
/** A more refined version of the iterator found in the `ParallelIterable` trait,
* this iterator can be split into arbitrary subsets of iterators.
@@ -900,7 +900,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
}
/** The variance of this symbol. */
- final def variance: Variance =
+ def variance: Variance =
if (isCovariant) Covariant
else if (isContravariant) Contravariant
else Invariant
@@ -2635,6 +2635,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
class AliasTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName)
extends TypeSymbol(initOwner, initPos, initName) {
type TypeOfClonedSymbol = TypeSymbol
+ override def variance = if (hasLocalFlag) Bivariant else info.typeSymbol.variance
+ override def isContravariant = variance.isContravariant
+ override def isCovariant = variance.isCovariant
final override def isAliasType = true
override def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeSymbol =
owner.newNonClassSymbol(name, pos, newFlags)
@@ -73,9 +73,8 @@ trait Variances {
def nextVariance(sym: Symbol, v: Variance): Variance = (
if (shouldFlip(sym, tvar)) v.flip
else if (isLocalOnly(sym)) Bivariant
- else if (!sym.isAliasType) v
- else if (sym.isOverridingSymbol) Invariant
- else Bivariant
+ else if (sym.isAliasType) Invariant
+ else v
)
def loop(sym: Symbol, v: Variance): Variance = (
if (sym == tvar.owner || v.isBivariant) v
@@ -145,12 +144,12 @@ trait Variances {
|| sym.owner.isCaseApplyOrUnapply
)
tree match {
+ case defn: MemberDef if skip =>
+ log(s"Skipping variance check of ${sym.defString}")
case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) =>
validateVariance(sym)
super.traverse(tree)
// ModuleDefs need not be considered because they have been eliminated already
- case defn: ValOrDefDef if skip =>
- log(s"Skipping variance check of $sym")
case ValDef(_, _, _, _) =>
validateVariance(sym)
case DefDef(_, _, tparams, vparamss, _, _) =>
@@ -0,0 +1,4 @@
+t6566a.scala:2: error: covariant type T occurs in invariant position in type T of type MyType
+ class TypeCheat[+T] { type MyType = T }
+ ^
+one error found
@@ -0,0 +1,17 @@
+object WhatsYourTypeIsMyType {
+ class TypeCheat[+T] { type MyType = T }
+
+ class Foo {
+ val tc = new TypeCheat[Foo]
+ var x: tc.MyType = _
+ def setX() = x = new Foo
+ }
+ class Bar extends Foo {
+ override val tc = new TypeCheat[Bar]
+ def unsound = this
+
+ setX()
+ println(x.unsound)
+ }
+ def main(args: Array[String]): Unit = new Bar
+}
@@ -0,0 +1,4 @@
+t6566b.scala:3: error: covariant type T occurs in invariant position in type T of type MyType
+ type MyType = T
+ ^
+one error found
@@ -0,0 +1,19 @@
+object WhatsYourTypeIsMyType {
+ trait WithMyType[+T] {
+ type MyType = T
+ }
+
+ class Foo extends WithMyType[Foo] {
+ var x: MyType = _
+ def setX() = x = new Foo
+ }
+
+ class Bar extends Foo with WithMyType[Bar] {
+ def unsound { println("iAmABar") }
+
+ setX()
+ println(x.unsound)
+ }
+
+ def main(args: Array[String]): Unit = new Bar
+}
@@ -13,7 +13,10 @@ variances.scala:21: error: covariant type A occurs in invariant position in supe
variances.scala:74: error: covariant type A occurs in contravariant position in type => test.Covariant.T[A]{val m: A => A} of value x
val x: T[A] {
^
+variances.scala:89: error: covariant type T occurs in invariant position in type T of type A
+ type A = T
+ ^
variances.scala:90: error: covariant type T occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo
def foo: B[A]
^
-6 errors found
+7 errors found
@@ -0,0 +1,7 @@
+class Foo1[+T] {
+ private[this] type MyType = T
+}
+
+class Foo2[+T] {
+ protected[this] type MyType = T
+}

0 comments on commit a419799

Please sign in to comment.