summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-09-27 10:20:45 -0700
committerPaul Phillips <paulp@improving.org>2012-09-27 12:06:09 -0700
commit211c9620ba83de143ea4776f55a3e0c4de11d002 (patch)
tree512aee7cb40f51ff689b06c0224729bef56cb89f
parent690e4892297cb6bd1cfb1b6266036969f6aa17fe (diff)
downloadscala-211c9620ba83de143ea4776f55a3e0c4de11d002.tar.gz
scala-211c9620ba83de143ea4776f55a3e0c4de11d002.tar.bz2
scala-211c9620ba83de143ea4776f55a3e0c4de11d002.zip
Added logic and tests for unchecked refinements.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Checkable.scala24
-rw-r--r--test/files/neg/unchecked-refinement.check13
-rw-r--r--test/files/neg/unchecked-refinement.flags1
-rw-r--r--test/files/neg/unchecked-refinement.scala27
4 files changed, 61 insertions, 4 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
index 2be08d12ea..7e15cf91a7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
@@ -67,6 +67,17 @@ trait Checkable {
val tps1 = (from baseType bc).typeArgs
val tps2 = (tvarType baseType bc).typeArgs
(tps1, tps2).zipped foreach (_ =:= _)
+ // Alternate, variance respecting formulation causes
+ // neg/unchecked3.scala to fail (abstract types). TODO -
+ // figure it out. It seems there is more work to do if I
+ // allow for variance, because the constraints accumulate
+ // as bounds and "tvar.instValid" is false.
+ //
+ // foreach3(tps1, tps2, bc.typeParams)((tp1, tp2, tparam) =>
+ // if (tparam.initialize.isCovariant) tp1 <:< tp2
+ // else if (tparam.isContravariant) tp2 <:< tp1
+ // else tp1 =:= tp2
+ // )
}
val resArgs = tparams zip tvars map {
@@ -77,14 +88,15 @@ trait Checkable {
}
private def isUnwarnableTypeArgSymbol(sym: Symbol) = (
- sym.isTypeParameterOrSkolem // dummy
+ sym.isTypeParameter // dummy
|| (sym.name.toTermName == nme.WILDCARD) // _
|| nme.isVariableName(sym.name) // type variable
)
private def isUnwarnableTypeArg(arg: Type) = (
- isUnwarnableTypeArgSymbol(arg.typeSymbolDirect) // has to be direct: see pos/t1439
- || (arg hasAnnotation UncheckedClass) // @unchecked T
+ uncheckedOk(arg) // @unchecked T
+ || isUnwarnableTypeArgSymbol(arg.typeSymbolDirect) // has to be direct: see pos/t1439
)
+ private def uncheckedOk(tp: Type) = tp hasAnnotation UncheckedClass
private def typeArgsInTopLevelType(tp: Type): List[Type] = {
val tps = tp match {
@@ -104,7 +116,7 @@ trait Checkable {
// sadly the spec says (new java.lang.Boolean(true)).isInstanceOf[scala.Boolean]
def P1 = X matchesPattern P
def P2 = !Psym.isPrimitiveValueClass && isNeverSubType(X, P)
- def P3 = Psym.isClass && (XR matchesPattern P)
+ def P3 = isNonRefinementClassType(P) && (XR matchesPattern P)
def P4 = !(P1 || P2 || P3)
def summaryString = f"""
@@ -226,6 +238,7 @@ trait Checkable {
* TODO: Eliminate inPattern, canRemedy, which have no place here.
*/
def checkCheckable(tree: Tree, P0: Type, X0: Type, inPattern: Boolean, canRemedy: Boolean = false) {
+ if (uncheckedOk(P0)) return
def where = if (inPattern) "pattern " else ""
// singleton types not considered here
@@ -239,6 +252,9 @@ trait Checkable {
// If top-level abstract types can be checked using a classtag extractor, don't warn about them
case TypeRef(_, sym, _) if sym.isAbstractType && canRemedy =>
;
+ // 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 _ =>
val checker = new CheckabilityChecker(X, P)
log(checker.summaryString)
diff --git a/test/files/neg/unchecked-refinement.check b/test/files/neg/unchecked-refinement.check
new file mode 100644
index 0000000000..d81517464f
--- /dev/null
+++ b/test/files/neg/unchecked-refinement.check
@@ -0,0 +1,13 @@
+unchecked-refinement.scala:17: error: abstract type U in type pattern Foo[U,U,V] is unchecked since it is eliminated by erasure
+ /* warn */ case _: Foo[U, U, V] if b => ()
+ ^
+unchecked-refinement.scala:19: error: non-variable type argument Any in type pattern Foo[Any,U,V] is unchecked since it is eliminated by erasure
+ /* warn */ case _: Foo[Any, U, V] if b => ()
+ ^
+unchecked-refinement.scala:23: error: a pattern match on a refinement type is unchecked
+ /* nowarn - todo */ case x: AnyRef { def bippy: Int } if b => x.bippy // this could/should do an instance check and not warn
+ ^
+unchecked-refinement.scala:24: error: a pattern match on a refinement type is unchecked
+ /* nowarn - todo */ case x: AnyRef { def size: Int } if b => x.size // this could/should do a static conformance test and not warn
+ ^
+four errors found
diff --git a/test/files/neg/unchecked-refinement.flags b/test/files/neg/unchecked-refinement.flags
new file mode 100644
index 0000000000..85d8eb2ba2
--- /dev/null
+++ b/test/files/neg/unchecked-refinement.flags
@@ -0,0 +1 @@
+-Xfatal-warnings
diff --git a/test/files/neg/unchecked-refinement.scala b/test/files/neg/unchecked-refinement.scala
new file mode 100644
index 0000000000..79ed7f13c1
--- /dev/null
+++ b/test/files/neg/unchecked-refinement.scala
@@ -0,0 +1,27 @@
+// a.scala
+// Thu Sep 27 09:42:16 PDT 2012
+
+trait Bar[-T1, T2, +T3] { }
+trait Foo[-T1, T2, +T3] extends Bar[T1, T2, T3]
+
+class A {
+ var b = true
+
+ def f1(x: Foo[Int, Int, Int]) = x match {
+ /* nowarn */ case _: Foo[Nothing, Int, Any] => true
+ }
+ def f2[T, U, V](x: Foo[T, U, V]) = x match {
+ /* nowarn */ case _: Foo[Nothing, U, Any] => true
+ }
+ def f3[T, U, V](x: Foo[T, U, V]) = x match {
+ /* warn */ case _: Foo[U, U, V] if b => ()
+ /* nowarn */ case _: Foo[Nothing, U, V] if b => ()
+ /* warn */ case _: Foo[Any, U, V] if b => ()
+ }
+
+ def f4(xs: List[Int]) = xs match {
+ /* nowarn - todo */ case x: AnyRef { def bippy: Int } if b => x.bippy // this could/should do an instance check and not warn
+ /* nowarn - todo */ case x: AnyRef { def size: Int } if b => x.size // this could/should do a static conformance test and not warn
+ /* nowarn */ case x: ((AnyRef { def size: Int }) @unchecked) if b => x.size
+ }
+}