summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Checkable.scala40
-rw-r--r--test/files/neg/unchecked2.check12
2 files changed, 37 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
index 763d209dd0..3a1803038c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
@@ -137,6 +137,7 @@ trait Checkable {
opt getOrElse NoType
}
+ def neverMatches = result == StaticallyFalse
def isUncheckable = result == Uncheckable
def uncheckableMessage = uncheckableType match {
case NoType => "something"
@@ -149,13 +150,28 @@ trait Checkable {
/** X, P, [P1], etc. are all explained at the top of the file.
*/
private object CheckabilityChecker {
- private def isNeverSubClass(sym1: Symbol, sym2: Symbol) = (
+ /** Given classes A and B, can it be shown A is never a subclass of B?
+ */
+ private def isNeverSubClass(sym1: Symbol, sym2: Symbol) = /*logResult(s"isNeverSubClass($sym1, $sym2)")*/(
sym1.isClass
&& sym2.isClass
- && sym1.isEffectivelyFinal
&& !(sym1 isSubClass sym2)
+ && (
+ // If B is final, A can only be a subclass of B if it is B itself.
+ // Therefore, A <: B is impossible unless B <: A.
+ if (sym2.isEffectivelyFinal)
+ !(sym2 isSubClass sym1)
+ // If A is final but B is not, a subclass relationship can
+ // still be ruled out if B is sealed and A is not a subclass of
+ // any of B's sealed children.
+ else (
+ sym1.isEffectivelyFinal
+ && sym2.isSealed
+ && sym2.children.forall(child => !(sym1 isSubClass child))
+ )
+ )
)
- private def isNeverSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = {
+ private def isNeverSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = /*logResult(s"isNeverSubArgs($tps1, $tps2, $tparams)")*/{
def isNeverSubArg(t1: Type, t2: Type, variance: Int) = {
if (variance > 0) isNeverSubType(t2, t1)
else if (variance < 0) isNeverSubType(t1, t2)
@@ -165,21 +181,24 @@ trait Checkable {
}
private def isNeverSameType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match {
case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) =>
- sym1.isClass && sym2.isClass && (sym1 ne sym2) && (sym1.isEffectivelyFinal || sym2.isEffectivelyFinal)
+ ( isNeverSubClass(sym1, sym2)
+ || isNeverSubClass(sym2, sym1)
+ || ((sym1 == sym2) && isNeverSubArgs(args1, args2, sym1.typeParams))
+ )
case _ =>
false
}
- private def isNeverSubType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match {
+ // Important to dealias at any entry point (this is the only one at this writing.)
+ private def isNeverSubType(tp1: Type, tp2: Type): Boolean = /*logResult(s"isNeverSubType($tp1, $tp2)")*/((tp1.dealias, tp2.dealias) match {
case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) =>
isNeverSubClass(sym1, sym2) || {
(sym1 isSubClass sym2) && {
val tp1seen = tp1 baseType sym2
-
isNeverSubArgs(tp1seen.typeArgs, args2, sym2.typeParams)
}
}
case _ => false
- }
+ })
}
trait InferCheckable {
@@ -190,6 +209,8 @@ trait Checkable {
* TODO: Eliminate inPattern, canRemedy, which have no place here.
*/
def checkCheckable(tree: Tree, P0: Type, X: Type, inPattern: Boolean, canRemedy: Boolean = false) {
+ def where = if (inPattern) "pattern " else ""
+
// singleton types not considered here
val P = P0.widen
P match {
@@ -202,8 +223,9 @@ trait Checkable {
case _ =>
val checker = new CheckabilityChecker(X.widen, P)
log(checker.summaryString)
- if (checker.isUncheckable) {
- def where = if (inPattern) "pattern " else ""
+ if (checker.neverMatches)
+ getContext.unit.warning(tree.pos, s"fruitless type test: a $X can never be a $P (but still might match its erasure)")
+ else if (checker.isUncheckable) {
val msg = (
if (checker.uncheckableType =:= P) s"abstract type $where$P"
else s"${checker.uncheckableMessage} in type $where$P"
diff --git a/test/files/neg/unchecked2.check b/test/files/neg/unchecked2.check
index 0ff2a249a8..b4a14358c7 100644
--- a/test/files/neg/unchecked2.check
+++ b/test/files/neg/unchecked2.check
@@ -1,22 +1,22 @@
-unchecked2.scala:4: error: non-variable type argument List[String] in type Option[List[String]] is unchecked since it is eliminated by erasure
+unchecked2.scala:4: error: fruitless type test: a Some[List[Int]] can never be a Option[List[String]] (but still might match its erasure)
/* warn */ Some(List(1)).isInstanceOf[Option[List[String]]]
^
unchecked2.scala:5: error: non-variable type argument Option[_] in type Option[Option[_]] is unchecked since it is eliminated by erasure
/* warn */ Some(123).isInstanceOf[Option[Option[_]]]
^
-unchecked2.scala:6: error: non-variable type argument String in type Option[String] is unchecked since it is eliminated by erasure
+unchecked2.scala:6: error: fruitless type test: a Some[Int] can never be a Option[String] (but still might match its erasure)
/* warn */ Some(123).isInstanceOf[Option[String]]
^
-unchecked2.scala:7: error: non-variable type argument List[String] in type Option[List[String]] is unchecked since it is eliminated by erasure
+unchecked2.scala:7: error: fruitless type test: a Some[Int] can never be a Option[List[String]] (but still might match its erasure)
/* warn */ Some(123).isInstanceOf[Option[List[String]]]
^
-unchecked2.scala:8: error: non-variable type argument List[Int => String] in type Option[List[Int => String]] is unchecked since it is eliminated by erasure
+unchecked2.scala:8: error: fruitless type test: a Some[Int] can never be a Option[List[Int => String]] (but still might match its erasure)
/* warn */ Some(123).isInstanceOf[Option[List[Int => String]]]
^
-unchecked2.scala:9: error: non-variable type argument (String, Double) in type Option[(String, Double)] is unchecked since it is eliminated by erasure
+unchecked2.scala:9: error: fruitless type test: a Some[Int] can never be a Option[(String, Double)] (but still might match its erasure)
/* warn */ Some(123).isInstanceOf[Option[(String, Double)]]
^
-unchecked2.scala:10: error: non-variable type argument String => Double in type Option[String => Double] is unchecked since it is eliminated by erasure
+unchecked2.scala:10: error: fruitless type test: a Some[Int] can never be a Option[String => Double] (but still might match its erasure)
/* warn */ Some(123).isInstanceOf[Option[String => Double]]
^
unchecked2.scala:14: error: non-variable type argument List[String] in type Option[List[String]] is unchecked since it is eliminated by erasure