summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Checkable.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Checkable.scala67
1 files changed, 46 insertions, 21 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
index d30b5c2601..94f8f509fc 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
@@ -6,12 +6,8 @@
package scala.tools.nsc
package typechecker
-import scala.collection.{ mutable, immutable }
-import scala.collection.mutable.ListBuffer
-import scala.util.control.ControlThrowable
-import symtab.Flags._
-import scala.annotation.tailrec
import Checkability._
+import scala.language.postfixOps
/** On pattern matcher checkability:
*
@@ -66,6 +62,9 @@ trait Checkable {
bases foreach { bc =>
val tps1 = (from baseType bc).typeArgs
val tps2 = (tvarType baseType bc).typeArgs
+ if (tps1.size != tps2.size)
+ devWarning(s"Unequally sized type arg lists in propagateKnownTypes($from, $to): ($tps1, $tps2)")
+
(tps1, tps2).zipped foreach (_ =:= _)
// Alternate, variance respecting formulation causes
// neg/unchecked3.scala to fail (abstract types). TODO -
@@ -82,7 +81,7 @@ trait Checkable {
val resArgs = tparams zip tvars map {
case (_, tvar) if tvar.instValid => tvar.constr.inst
- case (tparam, _) => tparam.tpe
+ case (tparam, _) => tparam.tpeHK
}
appliedType(to, resArgs: _*)
}
@@ -112,7 +111,7 @@ trait Checkable {
private class CheckabilityChecker(val X: Type, val P: Type) {
def Xsym = X.typeSymbol
def Psym = P.typeSymbol
- def XR = propagateKnownTypes(X, Psym)
+ def XR = if (Xsym == AnyClass) classExistentialType(Psym) else propagateKnownTypes(X, Psym)
// sadly the spec says (new java.lang.Boolean(true)).isInstanceOf[scala.Boolean]
def P1 = X matchesPattern P
def P2 = !Psym.isPrimitiveValueClass && isNeverSubType(X, P)
@@ -134,7 +133,7 @@ trait Checkable {
else if (P3) RuntimeCheckable
else if (uncheckableType == NoType) {
// Avoid warning (except ourselves) if we can't pinpoint the uncheckable type
- debugwarn("Checkability checker says 'Uncheckable', but uncheckable type cannot be found:\n" + summaryString)
+ debuglog("Checkability checker says 'Uncheckable', but uncheckable type cannot be found:\n" + summaryString)
CheckabilityError
}
else Uncheckable
@@ -154,6 +153,7 @@ trait Checkable {
def neverSubClass = isNeverSubClass(Xsym, Psym)
def neverMatches = result == StaticallyFalse
def isUncheckable = result == Uncheckable
+ def isCheckable = !isUncheckable
def uncheckableMessage = uncheckableType match {
case NoType => "something"
case tp @ RefinedType(_, _) => "refinement " + tp
@@ -195,19 +195,27 @@ trait Checkable {
* so I will consult with moors about the optimal time to be doing this.
*/
def areIrreconcilableAsParents(sym1: Symbol, sym2: Symbol): Boolean = areUnrelatedClasses(sym1, sym2) && (
- sym1.initialize.isEffectivelyFinal // initialization important
- || sym2.initialize.isEffectivelyFinal
+ isEffectivelyFinal(sym1) // initialization important
+ || isEffectivelyFinal(sym2)
|| !sym1.isTrait && !sym2.isTrait
|| sym1.isSealed && sym2.isSealed && allChildrenAreIrreconcilable(sym1, sym2) && !currentRun.compiles(sym1) && !currentRun.compiles(sym2)
)
+ private def isEffectivelyFinal(sym: Symbol): Boolean = (
+ // initialization important
+ sym.initialize.isEffectivelyFinal || (
+ settings.future && isTupleSymbol(sym) // SI-7294 step into the future and treat TupleN as final.
+ )
+ )
+
def isNeverSubClass(sym1: Symbol, sym2: Symbol) = areIrreconcilableAsParents(sym1, sym2)
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)
- else isNeverSameType(t1, t2)
- }
+ def isNeverSubArg(t1: Type, t2: Type, variance: Variance) = (
+ if (variance.isInvariant) isNeverSameType(t1, t2)
+ else if (variance.isCovariant) isNeverSubType(t2, t1)
+ else if (variance.isContravariant) isNeverSubType(t1, t2)
+ else false
+ )
exists3(tps1, tps2, tparams map (_.variance))(isNeverSubArg)
}
private def isNeverSameType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match {
@@ -232,6 +240,17 @@ trait Checkable {
trait InferCheckable {
self: Inferencer =>
+ def isUncheckable(P0: Type) = !isCheckable(P0)
+
+ def isCheckable(P0: Type): Boolean = (
+ uncheckedOk(P0) || (P0.widen match {
+ case TypeRef(_, NothingClass | NullClass | AnyValClass, _) => false
+ case RefinedType(_, decls) if !decls.isEmpty => false
+ case RefinedType(parents, _) => parents forall isCheckable
+ case p => new CheckabilityChecker(AnyTpe, p) isCheckable
+ })
+ )
+
/** TODO: much better error positions.
* Kind of stuck right now because they just pass us the one tree.
* TODO: Eliminate inPattern, canRemedy, which have no place here.
@@ -240,10 +259,12 @@ trait Checkable {
if (uncheckedOk(P0)) return
def where = if (inPattern) "pattern " else ""
- // singleton types not considered here
- val P = P0.widen
+ // singleton types not considered here, dealias the pattern for SI-XXXX
+ val P = P0.dealiasWiden
val X = X0.widen
+ def PString = if (P eq P0) P.toString else s"$P (the underlying of $P0)"
+
P match {
// Prohibit top-level type tests for these, but they are ok nested (e.g. case Foldable[Nothing] => ... )
case TypeRef(_, NothingClass | NullClass | AnyValClass, _) =>
@@ -254,17 +275,21 @@ trait Checkable {
// Matching on types like case _: AnyRef { def bippy: Int } => doesn't work -- yet.
case RefinedType(_, decls) if !decls.isEmpty =>
getContext.unit.warning(tree.pos, s"a pattern match on a refinement type is unchecked")
+ case RefinedType(parents, _) =>
+ parents foreach (p => checkCheckable(tree, p, X, inPattern, canRemedy))
case _ =>
val checker = new CheckabilityChecker(X, P)
- log(checker.summaryString)
+ if (checker.result == RuntimeCheckable)
+ log(checker.summaryString)
+
if (checker.neverMatches) {
val addendum = if (checker.neverSubClass) "" else " (but still might match its erasure)"
- getContext.unit.warning(tree.pos, s"fruitless type test: a value of type $X cannot also be a $P$addendum")
+ getContext.unit.warning(tree.pos, s"fruitless type test: a value of type $X cannot also be a $PString$addendum")
}
else if (checker.isUncheckable) {
val msg = (
- if (checker.uncheckableType =:= P) s"abstract type $where$P"
- else s"${checker.uncheckableMessage} in type $where$P"
+ if (checker.uncheckableType =:= P) s"abstract type $where$PString"
+ else s"${checker.uncheckableMessage} in type $where$PString"
)
getContext.unit.warning(tree.pos, s"$msg is unchecked since it is eliminated by erasure")
}