diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | build.sbt | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 3 | ||||
-rw-r--r-- | src/library/scala/collection/GenTraversableOnce.scala | 6 | ||||
-rw-r--r-- | src/library/scala/collection/IndexedSeqLike.scala | 2 | ||||
-rw-r--r-- | src/library/scala/collection/mutable/Builder.scala | 23 | ||||
-rw-r--r-- | src/library/scala/reflect/ClassTag.scala | 15 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Variances.scala | 20 | ||||
-rw-r--r-- | test/files/neg/t8079a.check | 4 | ||||
-rw-r--r-- | test/files/neg/t8079a.scala | 4 | ||||
-rw-r--r-- | test/files/neg/variances.check | 2 | ||||
-rw-r--r-- | test/files/pos/fields_widen_trait_var.scala | 4 | ||||
-rw-r--r-- | test/files/pos/t8079b.scala | 7 | ||||
-rw-r--r-- | test/junit/scala/runtime/LambdaDeserializerTest.java | 1 | ||||
-rw-r--r-- | test/junit/scala/tools/testing/ClearAfterClass.java | 1 | ||||
-rw-r--r-- | test/pending/neg/t8079d.check | 4 | ||||
-rw-r--r-- | test/pending/neg/t8079d.scala | 4 | ||||
-rw-r--r-- | test/pending/pos/t8079c.scala | 7 |
18 files changed, 75 insertions, 38 deletions
@@ -115,7 +115,7 @@ Core commands: - `partest` runs partest tests (accepts options, try `partest --help`) - `publishLocal` publishes a distribution locally (can be used as `scalaVersion` in other sbt projects) - - Optionally `set VersionUtil.baseVersionSuffix in Global := "abcd123-SNAPSHOT"` + - Optionally `set baseVersionSuffix := "abcd123-SNAPSHOT"` where `abcd123` is the git hash of the revision being published. You can also use something custom like `"mypatch"`. This changes the version number from `2.12.0-SNAPSHOT` to something more stable (`2.12.0-abcd123-SNAPSHOT`). @@ -133,7 +133,7 @@ lazy val commonSettings = clearSourceAndResourceDirectories ++ publishSettings + // we always assume that Java classes are standalone and do not have any dependency // on Scala classes compileOrder := CompileOrder.JavaThenScala, - javacOptions in Compile ++= Seq("-g", "-source", "1.8", "-target", "1.8"), + javacOptions in Compile ++= Seq("-g", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked"), // we don't want any unmanaged jars; as a reminder: unmanaged jar is a jar stored // directly on the file system and it's not resolved through Ivy // Ant's build stored unmanaged jars in `lib/` directory @@ -556,7 +556,7 @@ lazy val junit = project.in(file("test") / "junit") .settings(disablePublishing: _*) .settings( fork in Test := true, - javaOptions in Test += "-Xss5M", + javaOptions in Test += "-Xss1M", libraryDependencies ++= Seq(junitDep, junitInterfaceDep, jolDep), testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"), testFrameworks -= new TestFramework("org.scalacheck.ScalaCheckFramework"), diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 3d2b689e4c..0cd547c1eb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -976,7 +976,8 @@ trait Namers extends MethodSynthesis { !tpe.typeSymbolDirect.isModuleClass // Infer Foo.type instead of "object Foo" && (tpe.widen <:< pt) // Don't widen our way out of conforming to pt && ( sym.isVariable - || sym.isMethod && !sym.hasAccessorFlag + || sym.hasFlag(ACCESSOR) && !sym.hasFlag(STABLE) + || sym.isMethod && !sym.hasFlag(ACCESSOR) || isHidden(tpe) ) ) diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 4af2ca23be..d3096a872c 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -96,6 +96,12 @@ trait GenTraversableOnce[+A] extends Any { */ def size: Int + /** The size of this $coll if it is can be cheaply computed + * + * @return the number of elements in this $coll, or -1 if the size cannot be determined cheaply + */ + protected[collection] def sizeHintIfCheap: Int = -1 + /** Tests whether the $coll is empty. * * Note: Implementations in subclasses that are not repeatedly traversable must take diff --git a/src/library/scala/collection/IndexedSeqLike.scala b/src/library/scala/collection/IndexedSeqLike.scala index f4bf58ffe3..f0cede224d 100644 --- a/src/library/scala/collection/IndexedSeqLike.scala +++ b/src/library/scala/collection/IndexedSeqLike.scala @@ -92,4 +92,6 @@ trait IndexedSeqLike[+A, +Repr] extends Any with SeqLike[A, Repr] { copyToBuffer(result) result } + + override protected[collection] def sizeHintIfCheap: Int = size } diff --git a/src/library/scala/collection/mutable/Builder.scala b/src/library/scala/collection/mutable/Builder.scala index 8d6a0ec69d..528f78bd98 100644 --- a/src/library/scala/collection/mutable/Builder.scala +++ b/src/library/scala/collection/mutable/Builder.scala @@ -65,18 +65,18 @@ trait Builder[-Elem, +To] extends Growable[Elem] { /** Gives a hint that one expects the `result` of this builder * to have the same size as the given collection, plus some delta. This will * provide a hint only if the collection is known to have a cheap - * `size` method. Currently this is assumed to be the case if and only if - * the collection is of type `IndexedSeqLike`. - * Some builder classes - * will optimize their representation based on the hint. However, + * `size` method, which is determined by calling `sizeHint`. + * + * Some builder classes will optimize their representation based on the hint. However, * builder implementations are still required to work correctly even if the hint is * wrong, i.e. a different number of elements is added. * * @param coll the collection which serves as a hint for the result's size. */ def sizeHint(coll: TraversableLike[_, _]) { - if (coll.isInstanceOf[collection.IndexedSeqLike[_,_]]) { - sizeHint(coll.size) + coll.sizeHintIfCheap match { + case -1 => + case n => sizeHint(n) } } @@ -94,8 +94,9 @@ trait Builder[-Elem, +To] extends Growable[Elem] { * @param delta a correction to add to the `coll.size` to produce the size hint. */ def sizeHint(coll: TraversableLike[_, _], delta: Int) { - if (coll.isInstanceOf[collection.IndexedSeqLike[_,_]]) { - sizeHint(coll.size + delta) + coll.sizeHintIfCheap match { + case -1 => + case n => sizeHint(n + delta) } } @@ -112,8 +113,10 @@ trait Builder[-Elem, +To] extends Growable[Elem] { * than collection's size are reduced. */ def sizeHintBounded(size: Int, boundingColl: TraversableLike[_, _]) { - if (boundingColl.isInstanceOf[collection.IndexedSeqLike[_,_]]) - sizeHint(size min boundingColl.size) + boundingColl.sizeHintIfCheap match { + case -1 => + case n => sizeHint(size min n) + } } /** Creates a new builder by applying a transformation function to diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala index 3a300e0593..eb3aeeecfc 100644 --- a/src/library/scala/reflect/ClassTag.scala +++ b/src/library/scala/reflect/ClassTag.scala @@ -83,21 +83,6 @@ trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serial ) Some(x.asInstanceOf[T]) else None - // TODO: deprecate overloads in 2.12.0, remove in 2.13.0 - def unapply(x: Byte) : Option[T] = unapplyImpl(x, classOf[Byte]) - def unapply(x: Short) : Option[T] = unapplyImpl(x, classOf[Short]) - def unapply(x: Char) : Option[T] = unapplyImpl(x, classOf[Char]) - def unapply(x: Int) : Option[T] = unapplyImpl(x, classOf[Int]) - def unapply(x: Long) : Option[T] = unapplyImpl(x, classOf[Long]) - def unapply(x: Float) : Option[T] = unapplyImpl(x, classOf[Float]) - def unapply(x: Double) : Option[T] = unapplyImpl(x, classOf[Double]) - def unapply(x: Boolean) : Option[T] = unapplyImpl(x, classOf[Boolean]) - def unapply(x: Unit) : Option[T] = unapplyImpl(x, classOf[Unit]) - - private[this] def unapplyImpl(x: Any, primitiveCls: java.lang.Class[_]): Option[T] = - if (runtimeClass.isInstance(x) || runtimeClass.isAssignableFrom(primitiveCls)) Some(x.asInstanceOf[T]) - else None - // case class accessories override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]] override def equals(x: Any) = x.isInstanceOf[ClassTag[_]] && this.runtimeClass == x.asInstanceOf[ClassTag[_]].runtimeClass diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala index bc8a5de119..98b4e881af 100644 --- a/src/reflect/scala/reflect/internal/Variances.scala +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -50,11 +50,9 @@ trait Variances { sym.isParameter && !(tvar.isTypeParameterOrSkolem && sym.isTypeParameterOrSkolem && tvar.owner == sym.owner) ) - // return Bivariant if `sym` is local to a term - // or is private[this] or protected[this] - def isLocalOnly(sym: Symbol) = !sym.owner.isClass || ( - sym.isTerm // ?? shouldn't this be sym.owner.isTerm according to the comments above? - && (sym.isLocalToThis || sym.isSuperAccessor) // super accessors are implicitly local #4345 + // Is `sym` is local to a term or is private[this] or protected[this]? + def isExemptFromVariance(sym: Symbol): Boolean = !sym.owner.isClass || ( + (sym.isLocalToThis || sym.isSuperAccessor) // super accessors are implicitly local #4345 && !escapedLocals(sym) ) @@ -66,7 +64,7 @@ trait Variances { * Initially the state is covariant, but it might change along the search. * * A local alias type is treated as Bivariant; - * this is OK because we always expand aliases for variance checking. + * this is OK because such aliases are expanded for variance checking. * However, for an alias which might be externally visible, we must assume Invariant, * because there may be references to the type parameter that are not checked, * leading to unsoundness (see SI-6566). @@ -74,7 +72,7 @@ trait Variances { def relativeVariance(tvar: Symbol): Variance = { def nextVariance(sym: Symbol, v: Variance): Variance = ( if (shouldFlip(sym, tvar)) v.flip - else if (isLocalOnly(sym)) Bivariant + else if (isExemptFromVariance(sym)) Bivariant else if (sym.isAliasType) ( // Unsound pre-2.11 behavior preserved under -Xsource:2.10 if (settings.isScala211 || sym.isOverridingSymbol) Invariant @@ -126,7 +124,7 @@ trait Variances { tp match { case _ if isUncheckedVariance(tp) => case _ if resultTypeOnly(tp) => this(tp.resultType) - case TypeRef(_, sym, _) if sym.isAliasType => this(tp.normalize) + case TypeRef(_, sym, _) if shouldDealias(sym) => this(tp.normalize) case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; mapOver(tp) case RefinedType(_, _) => withinRefinement(mapOver(tp)) case ClassInfoType(parents, _, _) => parents foreach this @@ -138,6 +136,12 @@ trait Variances { // than the result of the pattern match above, which normalizes types. tp } + private def shouldDealias(sym: Symbol): Boolean = { + // The RHS of (private|protected)[this] type aliases are excluded from variance checks. This is + // implemented in relativeVariance. + // As such, we need to expand references to them to retain soundness. Example: neg/t8079a.scala + sym.isAliasType && isExemptFromVariance(sym) + } def validateDefinition(base: Symbol) { val saved = this.base this.base = base diff --git a/test/files/neg/t8079a.check b/test/files/neg/t8079a.check new file mode 100644 index 0000000000..6bbe78afa6 --- /dev/null +++ b/test/files/neg/t8079a.check @@ -0,0 +1,4 @@ +t8079a.scala:3: error: contravariant type I occurs in covariant position in type C.this.X of value b + def f2(b: X): Unit + ^ +one error found diff --git a/test/files/neg/t8079a.scala b/test/files/neg/t8079a.scala new file mode 100644 index 0000000000..4997ea282e --- /dev/null +++ b/test/files/neg/t8079a.scala @@ -0,0 +1,4 @@ +trait C[-I] { + private[this] type X = C[I] + def f2(b: X): Unit +} diff --git a/test/files/neg/variances.check b/test/files/neg/variances.check index cb1a60a632..3c1545a375 100644 --- a/test/files/neg/variances.check +++ b/test/files/neg/variances.check @@ -19,7 +19,7 @@ variances.scala:74: error: covariant type A occurs in contravariant position in 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 +variances.scala:90: error: covariant type A occurs in contravariant position in type => test.TestAlias.B[C.this.A] of method foo def foo: B[A] ^ 8 errors found diff --git a/test/files/pos/fields_widen_trait_var.scala b/test/files/pos/fields_widen_trait_var.scala new file mode 100644 index 0000000000..0ea9d9629a --- /dev/null +++ b/test/files/pos/fields_widen_trait_var.scala @@ -0,0 +1,4 @@ +// check that the `var x` below is assigned the type `Int`, and not `Constant(0)`, +// and that we can assign to it (if it gets a constant type, the `x` in `x = 42` +// is constant-folded to `0` and we can't find a setter.. +trait C { protected final var x = 0; x = 42 } diff --git a/test/files/pos/t8079b.scala b/test/files/pos/t8079b.scala new file mode 100644 index 0000000000..f3b7b78077 --- /dev/null +++ b/test/files/pos/t8079b.scala @@ -0,0 +1,7 @@ +trait F1[/* - */T, /* + */ R] + +object Test { + import scala.annotation.unchecked._ + type VariantF1[-T, +R] = F1[T @uncheckedVariance, R @uncheckedVariance] + trait C[+T] { def foo: VariantF1[Any, T] } +} diff --git a/test/junit/scala/runtime/LambdaDeserializerTest.java b/test/junit/scala/runtime/LambdaDeserializerTest.java index 3ed1ae1365..4e9c5c8954 100644 --- a/test/junit/scala/runtime/LambdaDeserializerTest.java +++ b/test/junit/scala/runtime/LambdaDeserializerTest.java @@ -136,6 +136,7 @@ public final class LambdaDeserializerTest { } } + @SuppressWarnings("unchecked") private <A> A deserizalizeLambdaCreatingAllowedMap(A f1, HashMap<String, MethodHandle> cache, MethodHandles.Lookup lookup) { SerializedLambda serialized = writeReplace(f1); HashMap<String, MethodHandle> allowed = createAllowedMap(lookup, serialized); diff --git a/test/junit/scala/tools/testing/ClearAfterClass.java b/test/junit/scala/tools/testing/ClearAfterClass.java index 95e170ec13..7f87f9a4d7 100644 --- a/test/junit/scala/tools/testing/ClearAfterClass.java +++ b/test/junit/scala/tools/testing/ClearAfterClass.java @@ -45,6 +45,7 @@ public class ClearAfterClass { } } + @SuppressWarnings("unchecked") public <T> T cached(String key, scala.Function0<T> t) { Map<String, Object> perClassCache = cache.get(getClass()); return (T) perClassCache.computeIfAbsent(key, s -> t.apply()); diff --git a/test/pending/neg/t8079d.check b/test/pending/neg/t8079d.check new file mode 100644 index 0000000000..f63f4902f8 --- /dev/null +++ b/test/pending/neg/t8079d.check @@ -0,0 +1,4 @@ +t8079d.scala:3: error: contravariant type I occurs in covariant position in type C.this.X of value b + def f2(b: X): Unit + ^ +one error found diff --git a/test/pending/neg/t8079d.scala b/test/pending/neg/t8079d.scala new file mode 100644 index 0000000000..ad420b99e3 --- /dev/null +++ b/test/pending/neg/t8079d.scala @@ -0,0 +1,4 @@ +trait C[-I] { + protected[this] type X = C[I] + def f2(b: X): Unit +} diff --git a/test/pending/pos/t8079c.scala b/test/pending/pos/t8079c.scala new file mode 100644 index 0000000000..ae7f37e2bf --- /dev/null +++ b/test/pending/pos/t8079c.scala @@ -0,0 +1,7 @@ +trait F1[/* - */T, /* + */ R] + +object Test { + import scala.annotation.unchecked._ + private[this] type VariantF1[-T, +R] = F1[T @uncheckedVariance, R @uncheckedVariance] + trait C[+T] { def foo: VariantF1[Any, T] } +} |