diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Checkable.scala | 24 | ||||
-rw-r--r-- | test/files/neg/unchecked-refinement.check | 13 | ||||
-rw-r--r-- | test/files/neg/unchecked-refinement.flags | 1 | ||||
-rw-r--r-- | test/files/neg/unchecked-refinement.scala | 27 |
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 + } +} |