diff options
author | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2012-09-28 04:46:02 -0700 |
---|---|---|
committer | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2012-09-28 04:46:02 -0700 |
commit | 57db28c55c3610f508b07940f7077cb73932418f (patch) | |
tree | 70b62aa01670cfc4b0877327a96e9d434f152418 | |
parent | 0614d2f512ad7b1b3885f81d9e6e779f447a6511 (diff) | |
parent | 211c9620ba83de143ea4776f55a3e0c4de11d002 (diff) | |
download | scala-57db28c55c3610f508b07940f7077cb73932418f.tar.gz scala-57db28c55c3610f508b07940f7077cb73932418f.tar.bz2 scala-57db28c55c3610f508b07940f7077cb73932418f.zip |
Merge pull request #1399 from paulp/210-unchecked
Much better unchecked warnings.
43 files changed, 865 insertions, 157 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index eaee39d7e6..a7da857429 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1750,7 +1750,7 @@ self => in.nextToken() if (in.token == SUBTYPE || in.token == SUPERTYPE) wildcardType(start) else atPos(start) { Bind(tpnme.WILDCARD, EmptyTree) } - case IDENTIFIER if treeInfo.isVariableName(in.name) => + case IDENTIFIER if nme.isVariableName(in.name) => atPos(start) { Bind(identForType(), EmptyTree) } case _ => typ() diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala index af77d3fe3f..40e520076a 100644 --- a/src/compiler/scala/tools/nsc/matching/Patterns.scala +++ b/src/compiler/scala/tools/nsc/matching/Patterns.scala @@ -21,7 +21,7 @@ trait Patterns extends ast.TreeDSL { import definitions._ import CODE._ import Debug._ - import treeInfo.{ unbind, isStar, isVarPattern, isVariableName } + import treeInfo.{ unbind, isStar, isVarPattern } type PatternMatch = MatchMatrix#PatternMatch private type PatternVar = MatrixContext#PatternVar @@ -366,7 +366,7 @@ trait Patterns extends ast.TreeDSL { lazy val Select(qualifier, name) = select def pathSegments = getPathSegments(tree) def backticked: Option[String] = qualifier match { - case _: This if isVariableName(name) => Some("`%s`".format(name)) + case _: This if nme.isVariableName(name) => Some("`%s`".format(name)) case _ => None } override def covers(sym: Symbol) = newMatchesPattern(sym, tree.tpe) @@ -388,11 +388,11 @@ trait Patterns extends ast.TreeDSL { lazy val UnApply(unfn, args) = tree lazy val Apply(fn, _) = unfn lazy val MethodType(List(arg, _*), _) = fn.tpe - + // Covers if the symbol matches the unapply method's argument type, // and the return type of the unapply is Some. override def covers(sym: Symbol) = newMatchesPattern(sym, arg.tpe) - + // TODO: for alwaysCovers: // fn.tpe.finalResultType.typeSymbol == SomeClass @@ -451,7 +451,7 @@ trait Patterns extends ast.TreeDSL { (sym.tpe.baseTypeSeq exists (_ matchesPattern pattp)) } } - + def sym = tree.symbol def tpe = tree.tpe def isEmpty = tree.isEmpty @@ -496,4 +496,4 @@ trait Patterns extends ast.TreeDSL { } } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 5a3db26e30..d3a5cebea0 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -185,7 +185,7 @@ abstract class UnCurry extends InfoTransform * try { * body * } catch { - * case ex: NonLocalReturnControl[_] => + * case ex: NonLocalReturnControl[T @unchecked] => * if (ex.key().eq(key)) ex.value() * else throw ex * } @@ -195,7 +195,8 @@ abstract class UnCurry extends InfoTransform localTyper typed { val extpe = nonLocalReturnExceptionType(meth.tpe.finalResultType) val ex = meth.newValue(nme.ex, body.pos) setInfo extpe - val pat = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(meth.tpe.finalResultType)) + val argType = meth.tpe.finalResultType withAnnotation (AnnotationInfo marker UncheckedClass.tpe) + val pat = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(argType)) val rhs = ( IF ((ex DOT nme.key)() OBJ_EQ Ident(key)) THEN ((ex DOT nme.value)()) diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala new file mode 100644 index 0000000000..7e15cf91a7 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -0,0 +1,284 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2012 LAMP/EPFL + * @author Paul Phillips + */ + +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._ + +/** On pattern matcher checkability: + * + * Consider a pattern match of this form: (x: X) match { case _: P => } + * + * There are four possibilities to consider: + * [P1] X will always conform to P + * [P2] x will never conform to P + * [P3] X <: P if some runtime test is true + * [P4] X cannot be checked against P + * + * The first two cases correspond to those when there is enough + * static information to say X <: P or that (x ∈ X) ⇒ (x ∉ P). + * The fourth case includes unknown abstract types or structural + * refinements appearing within a pattern. + * + * The third case is the interesting one. We designate another type, XR, + * which is essentially the intersection of X and |P|, where |P| is + * the erasure of P. If XR <: P, then no warning is emitted. + * + * Examples of how this info is put to use: + * sealed trait A[T] ; class B[T] extends A[T] + * def f(x: B[Int]) = x match { case _: A[Int] if true => } + * def g(x: A[Int]) = x match { case _: B[Int] => } + * + * `f` requires no warning because X=B[Int], P=A[Int], and B[Int] <:< A[Int]. + * `g` requires no warning because X=A[Int], P=B[Int], XR=B[Int], and B[Int] <:< B[Int]. + * XR=B[Int] because a value of type A[Int] which is tested to be a B can + * only be a B[Int], due to the definition of B (B[T] extends A[T].) + * + * This is something like asSeenFrom, only rather than asking what a type looks + * like from the point of view of one of its base classes, we ask what it looks + * like from the point of view of one of its subclasses. + */ +trait Checkable { + self: Analyzer => + + import global._ + import definitions._ + import CheckabilityChecker.{ isNeverSubType, isNeverSubClass } + + /** The applied type of class 'to' after inferring anything + * possible from the knowledge that 'to' must also be of the + * type given in 'from'. + */ + def propagateKnownTypes(from: Type, to: Symbol): Type = { + def tparams = to.typeParams + val tvars = tparams map (p => TypeVar(p)) + val tvarType = appliedType(to, tvars: _*) + val bases = from.baseClasses filter (to.baseClasses contains _) + + bases foreach { bc => + 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 { + case (_, tvar) if tvar.instValid => tvar.constr.inst + case (tparam, _) => tparam.tpe + } + appliedType(to, resArgs: _*) + } + + private def isUnwarnableTypeArgSymbol(sym: Symbol) = ( + sym.isTypeParameter // dummy + || (sym.name.toTermName == nme.WILDCARD) // _ + || nme.isVariableName(sym.name) // type variable + ) + private def isUnwarnableTypeArg(arg: Type) = ( + 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 { + case RefinedType(parents, _) => parents flatMap typeArgsInTopLevelType + case TypeRef(_, ArrayClass, arg :: Nil) => typeArgsInTopLevelType(arg) + case TypeRef(pre, sym, args) => typeArgsInTopLevelType(pre) ++ args + case ExistentialType(tparams, underlying) => tparams.map(_.tpe) ++ typeArgsInTopLevelType(underlying) + case _ => Nil + } + tps filterNot isUnwarnableTypeArg + } + + private class CheckabilityChecker(val X: Type, val P: Type) { + def Xsym = X.typeSymbol + def Psym = P.typeSymbol + def XR = 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) + def P3 = isNonRefinementClassType(P) && (XR matchesPattern P) + def P4 = !(P1 || P2 || P3) + + def summaryString = f""" + |Checking checkability of (x: $X) against pattern $P + |[P1] $P1%-6s X <: P // $X <: $P + |[P2] $P2%-6s x ∉ P // (x ∈ $X) ⇒ (x ∉ $P) + |[P3] $P3%-6s XR <: P // $XR <: $P + |[P4] $P4%-6s None of the above // !(P1 || P2 || P3) + """.stripMargin.trim + + val result = ( + if (X.isErroneous || P.isErroneous) CheckabilityError + else if (P1) StaticallyTrue + else if (P2) StaticallyFalse + 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) + CheckabilityError + } + else Uncheckable + ) + lazy val uncheckableType = if (Psym.isAbstractType) P else { + val possibles = typeArgsInTopLevelType(P).toSet + val opt = possibles find { targ => + // Create a derived type with every possibly uncheckable type replaced + // with a WildcardType, except for 'targ'. If !(XR <: derived) then + // 'targ' is uncheckable. + val derived = P map (tp => if (possibles(tp) && !(tp =:= targ)) WildcardType else tp) + !(XR <:< derived) + } + opt getOrElse NoType + } + + def neverSubClass = isNeverSubClass(Xsym, Psym) + def neverMatches = result == StaticallyFalse + def isUncheckable = result == Uncheckable + def uncheckableMessage = uncheckableType match { + case NoType => "something" + case tp @ RefinedType(_, _) => "refinement " + tp + case TypeRef(_, sym, _) if sym.isAbstractType => "abstract type " + sym.name + case tp => "non-variable type argument " + tp + } + } + + /** X, P, [P1], etc. are all explained at the top of the file. + */ + private object CheckabilityChecker { + /** A knowable class is one which is either effectively final + * itself, or sealed with only knowable children. + */ + def isKnowable(sym: Symbol): Boolean = /*logResult(s"isKnowable($sym)")*/( + sym.initialize.isEffectivelyFinal // pesky .initialize requirement, or we receive lies about isSealed + || sym.isSealed && (sym.children forall isKnowable) + ) + def knownSubclasses(sym: Symbol): List[Symbol] = /*logResult(s"knownSubclasses($sym)")*/(sym :: { + if (sym.isSealed) sym.children.toList flatMap knownSubclasses + else Nil + }) + def excludable(s1: Symbol, s2: Symbol) = /*logResult(s"excludable($s1, $s2)")*/( + isKnowable(s1) + && !(s2 isSubClass s1) + && knownSubclasses(s1).forall(child => !(child isSubClass s2)) + ) + + /** Given classes A and B, can it be shown that nothing which is + * an A will ever be a subclass of something which is a B? This + * entails not only showing that !(A isSubClass B) but that the + * same is true of all their subclasses. Restated for symmetry: + * the same value cannot be a member of both A and B. + * + * 1) A must not be a subclass of B, nor B of A (the trivial check) + * 2) One of A or B must be completely knowable (see isKnowable) + * 3) Assuming A is knowable, the proposition is true if + * !(A' isSubClass B) for all A', where A' is a subclass of A. + * + * Due to symmetry, the last condition applies as well in reverse. + */ + def isNeverSubClass(sym1: Symbol, sym2: Symbol) = /*logResult(s"isNeverSubClass($sym1, $sym2)")*/( + sym1.isClass + && sym2.isClass + && (excludable(sym1, sym2) || excludable(sym2, sym1)) + ) + 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) + } + exists3(tps1, tps2, tparams map (_.variance))(isNeverSubArg) + } + private def isNeverSameType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { + case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) => + ( isNeverSubClass(sym1, sym2) + || isNeverSubClass(sym2, sym1) + || ((sym1 == sym2) && isNeverSubArgs(args1, args2, sym1.typeParams)) + ) + case _ => + false + } + // Important to dealias at any entry point (this is the only one at this writing.) + 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 { + self: Inferencer => + + /** 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. + */ + 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 + val P = P0.widen + val X = X0.widen + + P match { + // Prohibit top-level type tests for these, but they are ok nested (e.g. case Foldable[Nothing] => ... ) + case TypeRef(_, NothingClass | NullClass | AnyValClass, _) => + InferErrorGen.TypePatternOrIsInstanceTestError(tree, P) + // 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) + 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") + } + else if (checker.isUncheckable) { + val msg = ( + if (checker.uncheckableType =:= P) s"abstract type $where$P" + else s"${checker.uncheckableMessage} in type $where$P" + ) + getContext.unit.warning(tree.pos, s"$msg is unchecked since it is eliminated by erasure") + } + } + } + } +} + +private[typechecker] final class Checkability(val value: Int) extends AnyVal { } +private[typechecker] object Checkability { + val StaticallyTrue = new Checkability(0) + val StaticallyFalse = new Checkability(1) + val RuntimeCheckable = new Checkability(2) + val Uncheckable = new Checkability(3) + val CheckabilityError = new Checkability(4) +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 22077303a4..0c3dcb9342 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -17,7 +17,7 @@ import scala.annotation.tailrec * @author Martin Odersky * @version 1.0 */ -trait Infer { +trait Infer extends Checkable { self: Analyzer => import global._ @@ -254,7 +254,7 @@ trait Infer { private val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) /** The context-dependent inferencer part */ - class Inferencer(context: Context) extends InferencerContextErrors { + class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable { import InferErrorGen._ /* -- Error Messages --------------------------------------------------- */ @@ -1341,99 +1341,6 @@ trait Infer { check(tp, Nil) } - // if top-level abstract types can be checked using a classtag extractor, don't warn about them - def checkCheckable(tree: Tree, typeToTest: Type, typeEnsured: Type, inPattern: Boolean, canRemedy: Boolean = false) = { - log(s"checkCheckable($tree, $typeToTest, $typeEnsured, inPattern = $inPattern, canRemedy = $canRemedy") - - sealed abstract class TypeConformance(check: (Type, Type) => Boolean) { - def apply(t1: Type, t2: Type): Boolean = check(t1, t2) && { - log(s"Skipping unchecked for statically verifiable condition $t1 ${this} $t2") - true - } - } - // I tried to use varianceInType to track the variance implications - // but I could not make it work. - case object =:= extends TypeConformance(_ =:= _) - case object <:< extends TypeConformance(_ <:< _) - case object >:> extends TypeConformance((t1, t2) => t2 <:< t1) - case object =!= extends TypeConformance((t1, t2) => false) - - var bound: List[Symbol] = Nil - var warningMessages: List[String] = Nil - - def isLocalBinding(sym: Symbol) = ( - sym.isAbstractType && ( - (bound contains sym) - || (sym.name == tpnme.WILDCARD) - || { - val e = context.scope.lookupEntry(sym.name) - (e ne null) && e.sym == sym && !e.sym.isTypeParameterOrSkolem && e.owner == context.scope - } - ) - ) - def check(tp0: Type, pt: Type, conformance: TypeConformance): Boolean = { - val tp = tp0.normalize - // Set the warning message to be issued when the top-level call fails. - def warn(what: String): Boolean = { - warningMessages ::= what - false - } - def checkArg(param: Symbol, arg: Type) = { - def conforms = ( - if (param.isCovariant) <:< - else if (param.isContravariant) >:> - else =:= - ) - (arg hasAnnotation UncheckedClass) || { - arg.withoutAnnotations match { - case TypeRef(_, sym, args) => - ( isLocalBinding(sym) - || arg.typeSymbol.isTypeParameterOrSkolem - || (sym.name == tpnme.WILDCARD) // avoid spurious warnings on HK types - || check(arg, param.tpeHK, conforms) - || warn("non-variable type argument " + arg) - ) - case _ => - warn("non-variable type argument " + arg) - } - } - } - - // Checking if pt (the expected type of the pattern, and the type - // we are guaranteed) conforms to tp (the type expressed in the pattern's - // type test.) If it does, then even if the type being checked for appears - // to be uncheckable, it is not a warning situation, because it is indeed - // checked: not at runtime, but statically. - conformance.apply(pt, tp) || (tp match { - case SingleType(pre, _) => check(pre, pt, =:=) - case ExistentialType(quantified, tp1) => bound :::= quantified ; check(tp1, pt, <:<) - case ThisType(_) | NoPrefix => true - case RefinedType(parents, decls) if decls.isEmpty => parents forall (p => check(p, pt, <:<)) - case RefinedType(_, _) => warn("refinement " + tp) - case TypeRef(_, ArrayClass, arg :: Nil) => check(arg, NoType, =!=) - case TypeRef(_, NonLocalReturnControlClass, _) => true // no way to suppress unchecked warnings on try/catch - // we only use the extractor for top-level type tests, type arguments remain unchecked - case TypeRef(_, sym, _) if sym.isAbstractType => isLocalBinding(sym) || canRemedy || warn("abstract type " + tp) - case TypeRef(_, _, Nil) => false // leaf node - case TypeRef(pre, sym, args) => forall2(sym.typeParams, args)(checkArg) && check(pre, pt.prefix, =:=) - case _ => warn("type " + tp) - }) - } - typeToTest match { - // Prohibit top-level type tests for these, but they are - // acceptable nested (e.g. case Foldable[Nothing] => ... ) - case TypeRef(_, NothingClass | NullClass | AnyValClass, _) => - TypePatternOrIsInstanceTestError(tree, typeToTest) - case _ => - def where = ( if (inPattern) "pattern " else "" ) + typeToTest - if (check(typeToTest, typeEnsured, =:=)) () - // Note that this is a regular warning, not an uncheckedWarning, - // which is now the province of such notifications as "pattern matcher - // exceeded its analysis budget." - else warningMessages foreach (m => - context.unit.warning(tree.pos, s"$m in type $where is unchecked since it is eliminated by erasure")) - } - } /** Type intersection of simple type tp1 with general type tp2. * The result eliminates some redundancies. diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index fa7e756e36..c25b6c3726 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -253,12 +253,6 @@ trait Typers extends Modes with Adaptations with Tags { result } } - def isNonRefinementClassType(tpe: Type) = tpe match { - case SingleType(_, sym) => sym.isModuleClass - case TypeRef(_, sym, _) => sym.isClass && !sym.isRefinementClass - case ErrorType => true - case _ => false - } private def errorNotClass(tpt: Tree, found: Type) = { ClassTypeRequiredError(tpt, found); false } private def errorNotStable(tpt: Tree, found: Type) = { TypeNotAStablePrefixError(tpt, found); false } @@ -3768,9 +3762,13 @@ trait Typers extends Modes with Adaptations with Tags { if (fun.symbol == Predef_classOf) typedClassOf(tree, args.head, true) else { - if (!isPastTyper && fun.symbol == Any_isInstanceOf && !targs.isEmpty) - checkCheckable(tree, targs.head, AnyClass.tpe, inPattern = false) - + if (!isPastTyper && fun.symbol == Any_isInstanceOf && targs.nonEmpty) { + val scrutineeType = fun match { + case Select(qual, _) => qual.tpe + case _ => AnyClass.tpe + } + checkCheckable(tree, targs.head, scrutineeType, inPattern = false) + } val resultpe = restpe.instantiateTypeParams(tparams, targs) //@M substitution in instantiateParams needs to be careful! //@M example: class Foo[a] { def foo[m[x]]: m[a] = error("") } (new Foo[Int]).foo[List] : List[Int] @@ -3780,7 +3778,8 @@ trait Typers extends Modes with Adaptations with Tags { //println("instantiating type params "+restpe+" "+tparams+" "+targs+" = "+resultpe) treeCopy.TypeApply(tree, fun, args) setType resultpe } - } else { + } + else { TypedApplyWrongNumberOfTpeParametersError(tree, fun) } case ErrorType => diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 2cdfb05e77..eacbf6a0cc 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -346,6 +346,16 @@ trait StdNames { def isSingletonName(name: Name) = name endsWith SINGLETON_SUFFIX def isModuleName(name: Name) = name endsWith MODULE_SUFFIX_NAME + /** Is name a variable name? */ + def isVariableName(name: Name): Boolean = { + val first = name.startChar + ( ((first.isLower && first.isLetter) || first == '_') + && (name != nme.false_) + && (name != nme.true_) + && (name != nme.null_) + ) + } + def isDeprecatedIdentifierName(name: Name) = name.toTermName match { case nme.`then` | nme.`macro` => true case _ => false diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 6ef4c3f660..68decc27f5 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -247,7 +247,7 @@ abstract class TreeInfo { /** Is tree a variable pattern? */ def isVarPattern(pat: Tree): Boolean = pat match { - case x: Ident => !x.isBackquoted && isVariableName(x.name) + case x: Ident => !x.isBackquoted && nme.isVariableName(x.name) case _ => false } def isDeprecatedIdentifier(tree: Tree): Boolean = tree match { @@ -312,14 +312,6 @@ abstract class TreeInfo { /** Is name a left-associative operator? */ def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.endChar != ':') - private val reserved = Set[Name](nme.false_, nme.true_, nme.null_) - - /** Is name a variable name? */ - def isVariableName(name: Name): Boolean = { - val first = name.startChar - ((first.isLower && first.isLetter) || first == '_') && !reserved(name) - } - /** Is tree a `this` node which belongs to `enclClass`? */ def isSelf(tree: Tree, enclClass: Symbol): Boolean = tree match { case This(_) => tree.symbol == enclClass diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index fd5694b599..6b274467fc 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -5732,6 +5732,13 @@ trait Types extends api.Types { self: SymbolTable => case _ => false } + def isNonRefinementClassType(tpe: Type) = tpe match { + case SingleType(_, sym) => sym.isModuleClass + case TypeRef(_, sym, _) => sym.isClass && !sym.isRefinementClass + case ErrorType => true + case _ => false + } + // @assume tp1.isHigherKinded || tp2.isHigherKinded def isHKSubType0(tp1: Type, tp2: Type, depth: Int): Boolean = ( tp1.typeSymbol == NothingClass diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index 14b5d3003d..201b4dfe0a 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -175,6 +175,20 @@ trait Collections { } false } + final def exists3[A, B, C](xs1: List[A], xs2: List[B], xs3: List[C])(f: (A, B, C) => Boolean): Boolean = { + var ys1 = xs1 + var ys2 = xs2 + var ys3 = xs3 + while (!ys1.isEmpty && !ys2.isEmpty && !ys3.isEmpty) { + if (f(ys1.head, ys2.head, ys3.head)) + return true + + ys1 = ys1.tail + ys2 = ys2.tail + ys3 = ys3.tail + } + false + } final def forall2[A, B](xs1: List[A], xs2: List[B])(f: (A, B) => Boolean): Boolean = { var ys1 = xs1 var ys2 = xs2 diff --git a/test/files/neg/patmat-type-check.check b/test/files/neg/patmat-type-check.check index e045841ce1..721217c314 100644 --- a/test/files/neg/patmat-type-check.check +++ b/test/files/neg/patmat-type-check.check @@ -1,3 +1,12 @@ +patmat-type-check.scala:11: warning: fruitless type test: a value of type Test.Bop4[T] cannot also be a Seq[A] + def s3[T](x: Bop4[T]) = x match { case Seq('b', 'o', 'b') => true } + ^ +patmat-type-check.scala:15: warning: fruitless type test: a value of type Test.Bop5[_$1,T1,T2] cannot also be a Seq[A] + def s4[T1, T2](x: Bop5[_, T1, T2]) = x match { case Seq('b', 'o', 'b') => true } + ^ +patmat-type-check.scala:19: warning: fruitless type test: a value of type Test.Bop3[T] cannot also be a Seq[A] + def f4[T](x: Bop3[T]) = x match { case Seq('b', 'o', 'b') => true } + ^ patmat-type-check.scala:22: error: scrutinee is incompatible with pattern type; found : Seq[A] required: String @@ -18,4 +27,5 @@ patmat-type-check.scala:30: error: scrutinee is incompatible with pattern type; required: Test.Bop3[Char] def f4[T](x: Bop3[Char]) = x match { case Seq('b', 'o', 'b') => true } // fail ^ +three warnings found four errors found diff --git a/test/files/neg/t1872.check b/test/files/neg/t1872.check index ef84ef79e0..c5dc2a8080 100644 --- a/test/files/neg/t1872.check +++ b/test/files/neg/t1872.check @@ -1,4 +1,8 @@ +t1872.scala:3: warning: fruitless type test: a value of type Int cannot also be a scala.util.Random + def f(x: Int) = x.isInstanceOf[util.Random] + ^ t1872.scala:3: error: isInstanceOf cannot test if value types are references. def f(x: Int) = x.isInstanceOf[util.Random] ^ +one warning found one error found diff --git a/test/files/neg/t4302.check b/test/files/neg/t4302.check index 327425acb0..450d28bbc5 100644 --- a/test/files/neg/t4302.check +++ b/test/files/neg/t4302.check @@ -1,4 +1,4 @@ -t4302.scala:2: error: abstract type T in type T is unchecked since it is eliminated by erasure +t4302.scala:2: error: abstract type T is unchecked since it is eliminated by erasure def hasMatch[T](x: AnyRef) = x.isInstanceOf[T] ^ one error found diff --git a/test/files/neg/t5762.check b/test/files/neg/t5762.check new file mode 100644 index 0000000000..10064032aa --- /dev/null +++ b/test/files/neg/t5762.check @@ -0,0 +1,13 @@ +t5762.scala:6: error: non-variable type argument Int in type pattern D[Int] is unchecked since it is eliminated by erasure + case _: D[Int] if bippy => 1 + ^ +t5762.scala:7: error: non-variable type argument String in type pattern D[String] is unchecked since it is eliminated by erasure + case _: D[String] => 2 + ^ +t5762.scala:20: error: non-variable type argument D[Int] in type pattern D[D[Int]] is unchecked since it is eliminated by erasure + case _: D[D[Int]] if bippy => 1 + ^ +t5762.scala:21: error: non-variable type argument D[String] in type pattern D[D[String]] is unchecked since it is eliminated by erasure + case _: D[D[String]] => 2 + ^ +four errors found diff --git a/test/files/neg/t5762.flags b/test/files/neg/t5762.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/t5762.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/t5762.scala b/test/files/neg/t5762.scala new file mode 100644 index 0000000000..fb73552b12 --- /dev/null +++ b/test/files/neg/t5762.scala @@ -0,0 +1,24 @@ +class D[-A] + +object Test { + var bippy: Boolean = true + def f1(x: D[Int with String]) = x match { + case _: D[Int] if bippy => 1 + case _: D[String] => 2 + } + // Correctly warns: + // + // a.scala:5: warning: non variable type-argument Int in type pattern D[Int] is unchecked since it is eliminated by erasure + // case _: D[Int] => 1 + // ^ + // a.scala:6: warning: non variable type-argument String in type pattern D[String] is unchecked since it is eliminated by erasure + // case _: D[String] => 2 + // ^ + // two warnings found + + def f2(x: D[D[Int] with D[String]]) = x match { + case _: D[D[Int]] if bippy => 1 + case _: D[D[String]] => 2 + } + // No warnings! +} diff --git a/test/files/neg/unchecked-abstract.check b/test/files/neg/unchecked-abstract.check new file mode 100644 index 0000000000..dc7a8d93d0 --- /dev/null +++ b/test/files/neg/unchecked-abstract.check @@ -0,0 +1,25 @@ +unchecked-abstract.scala:16: error: abstract type H in type Con[M.this.H] is unchecked since it is eliminated by erasure + /* warn */ println(x.isInstanceOf[Con[H]]) + ^ +unchecked-abstract.scala:21: error: abstract type H in type Con[M.this.H] is unchecked since it is eliminated by erasure + /* warn */ println(x.isInstanceOf[Con[H]]) + ^ +unchecked-abstract.scala:27: error: abstract type T in type Inv[M.this.T] is unchecked since it is eliminated by erasure + /* warn */ println(x.isInstanceOf[Inv[T]]) + ^ +unchecked-abstract.scala:28: error: abstract type L in type Inv[M.this.L] is unchecked since it is eliminated by erasure + /* warn */ println(x.isInstanceOf[Inv[L]]) + ^ +unchecked-abstract.scala:31: error: abstract type H in type Inv[M.this.H] is unchecked since it is eliminated by erasure + /* warn */ println(x.isInstanceOf[Inv[H]]) + ^ +unchecked-abstract.scala:33: error: abstract type L in type Inv[M.this.L] is unchecked since it is eliminated by erasure + /* warn */ println(x.isInstanceOf[Inv[L]]) + ^ +unchecked-abstract.scala:36: error: abstract type H in type Inv[M.this.H] is unchecked since it is eliminated by erasure + /* warn */ println(x.isInstanceOf[Inv[H]]) + ^ +unchecked-abstract.scala:37: error: abstract type T in type Inv[M.this.T] is unchecked since it is eliminated by erasure + /* warn */ println(x.isInstanceOf[Inv[T]]) + ^ +8 errors found diff --git a/test/files/neg/unchecked-abstract.flags b/test/files/neg/unchecked-abstract.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/unchecked-abstract.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/unchecked-abstract.scala b/test/files/neg/unchecked-abstract.scala new file mode 100644 index 0000000000..5b915755f4 --- /dev/null +++ b/test/files/neg/unchecked-abstract.scala @@ -0,0 +1,93 @@ +trait Con[-X] +trait Inv[X] +trait Cov[+X] + +abstract class M { + type H + type L <: H + type T >: L <: H + + def h1(x: Con[H]) = { + /* nowarn */ println(x.isInstanceOf[Con[H]]) + /* nowarn */ println(x.isInstanceOf[Con[T]]) + /* nowarn */ println(x.isInstanceOf[Con[L]]) + } + def h2(x: Con[T]) = { + /* warn */ println(x.isInstanceOf[Con[H]]) + /* nowarn */ println(x.isInstanceOf[Con[T]]) + /* nowarn */ println(x.isInstanceOf[Con[L]]) + } + def h3(x: Con[L]) = { + /* warn */ println(x.isInstanceOf[Con[H]]) + /* warn */ println(x.isInstanceOf[Con[T]]) + /* nowarn */ println(x.isInstanceOf[Con[L]]) + } + def h4(x: Inv[H]) = { + /* nowarn */ println(x.isInstanceOf[Inv[H]]) + /* warn */ println(x.isInstanceOf[Inv[T]]) + /* warn */ println(x.isInstanceOf[Inv[L]]) + } + def h5(x: Inv[T]) = { + /* warn */ println(x.isInstanceOf[Inv[H]]) + /* nowarn */ println(x.isInstanceOf[Inv[T]]) + /* warn */ println(x.isInstanceOf[Inv[L]]) + } + def h6(x: Inv[L]) = { + /* warn */ println(x.isInstanceOf[Inv[H]]) + /* warn */ println(x.isInstanceOf[Inv[T]]) + /* nowarn */ println(x.isInstanceOf[Inv[L]]) + } + def h7(x: Cov[H]) = { + /* nowarn */ println(x.isInstanceOf[Cov[H]]) + /* warn */ println(x.isInstanceOf[Cov[T]]) + /* warn */ println(x.isInstanceOf[Cov[L]]) + } + def h8(x: Cov[T]) = { + /* nowarn */ println(x.isInstanceOf[Cov[H]]) + /* nowarn */ println(x.isInstanceOf[Cov[T]]) + /* warn */ println(x.isInstanceOf[Cov[L]]) + } + def h9(x: Cov[L]) = { + /* nowarn */ println(x.isInstanceOf[Cov[H]]) + /* nowarn */ println(x.isInstanceOf[Cov[T]]) + /* nowarn */ println(x.isInstanceOf[Cov[L]]) + } +} + +object Test extends M { + type H = Any + type T = Int + type L = Nothing + + val conh = new Con[H] { } + val cont = new Con[T] { } + val conl = new Con[L] { } + + val invh = new Inv[H] { } + val invt = new Inv[T] { } + val invl = new Inv[L] { } + + val covh = new Cov[H] { } + val covt = new Cov[T] { } + val covl = new Cov[L] { } + + def main(args: Array[String]): Unit = { + h1(conh) + h2(conh) + h2(cont) + h3(conh) + h3(cont) + h3(conl) + + h4(invh) + h5(invt) + h6(invl) + + h7(covh) + h7(covt) + h7(covl) + h8(covt) + h8(covl) + h9(covl) + } +} diff --git a/test/files/neg/unchecked-impossible.check b/test/files/neg/unchecked-impossible.check new file mode 100644 index 0000000000..0ab371dbaa --- /dev/null +++ b/test/files/neg/unchecked-impossible.check @@ -0,0 +1,4 @@ +unchecked-impossible.scala:5: error: fruitless type test: a value of type T2[Int,Int] cannot also be a Seq[A] + case Seq(x) => + ^ +one error found diff --git a/test/files/neg/unchecked-impossible.flags b/test/files/neg/unchecked-impossible.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/unchecked-impossible.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/unchecked-impossible.scala b/test/files/neg/unchecked-impossible.scala new file mode 100644 index 0000000000..985a2d0b08 --- /dev/null +++ b/test/files/neg/unchecked-impossible.scala @@ -0,0 +1,16 @@ +final case class T2[+A, +B](a: A, b: B) + +class A { + def f1 = T2(1, 2) match { + case Seq(x) => + case _ => + } + def f2 = T2(1, 2) match { + case _: T2[Int, Int] => /* nowarn */ + case _ => + } + def f3 = T2(1, 2) match { + case _: T2[_, Int] => /* nowarn */ + case _ => + } +} diff --git a/test/files/neg/unchecked-knowable.check b/test/files/neg/unchecked-knowable.check new file mode 100644 index 0000000000..3a6ef994b5 --- /dev/null +++ b/test/files/neg/unchecked-knowable.check @@ -0,0 +1,4 @@ +unchecked-knowable.scala:17: error: fruitless type test: a value of type Bippy cannot also be a A1 + /* warn */ (new Bippy).isInstanceOf[A1] + ^ +one error found diff --git a/test/files/neg/unchecked-knowable.flags b/test/files/neg/unchecked-knowable.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/unchecked-knowable.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/unchecked-knowable.scala b/test/files/neg/unchecked-knowable.scala new file mode 100644 index 0000000000..667b47f504 --- /dev/null +++ b/test/files/neg/unchecked-knowable.scala @@ -0,0 +1,20 @@ +/** Knowable - only final leaves */ +sealed abstract class A1 +sealed abstract class A2 extends A1 +final class A3 extends A1 +final class A4 extends A2 + +/** Unknowable */ +sealed abstract class B1 +sealed abstract class B2 extends B1 +final class B3 extends B1 +trait B4 extends B2 + +class Bippy +trait Dingus + +class A { + /* warn */ (new Bippy).isInstanceOf[A1] + /* nowarn */ (new Bippy).isInstanceOf[B1] + /* nowarn */ ((new Bippy): Any).isInstanceOf[A1] +} 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 + } +} diff --git a/test/files/neg/unchecked.check b/test/files/neg/unchecked.check index 34a11db1a0..2883b716c9 100644 --- a/test/files/neg/unchecked.check +++ b/test/files/neg/unchecked.check @@ -2,7 +2,7 @@ unchecked.scala:18: error: non-variable type argument String in type pattern Ite case xs: Iterable[String] => xs.head // unchecked ^ unchecked.scala:22: error: non-variable type argument Any in type pattern Set[Any] is unchecked since it is eliminated by erasure - case xs: Set[Any] => xs.head // unchecked + case xs: Set[Any] => xs.head // unchecked ^ unchecked.scala:26: error: non-variable type argument Any in type pattern Map[Any,Any] is unchecked since it is eliminated by erasure case xs: Map[Any, Any] => xs.head // unchecked diff --git a/test/files/neg/unchecked.scala b/test/files/neg/unchecked.scala index b50cdf9d7a..e491b253ba 100644 --- a/test/files/neg/unchecked.scala +++ b/test/files/neg/unchecked.scala @@ -19,8 +19,8 @@ object Test { case _ => 0 } def f3(x: Any) = x match { - case xs: Set[Any] => xs.head // unchecked - case _ => 0 + case xs: Set[Any] => xs.head // unchecked + case _ => 0 } def f4(x: Any) = x match { case xs: Map[Any, Any] => xs.head // unchecked diff --git a/test/files/neg/unchecked2.check b/test/files/neg/unchecked2.check index e37865928e..68fdfa82ac 100644 --- a/test/files/neg/unchecked2.check +++ b/test/files/neg/unchecked2.check @@ -1,19 +1,43 @@ -unchecked2.scala:2: error: non-variable type argument Int in type Option[Int] is unchecked since it is eliminated by erasure - Some(123).isInstanceOf[Option[Int]] - ^ -unchecked2.scala:3: error: non-variable type argument String in type Option[String] is unchecked since it is eliminated by erasure - Some(123).isInstanceOf[Option[String]] - ^ -unchecked2.scala:4: error: non-variable type argument List[String] in type Option[List[String]] is unchecked since it is eliminated by erasure - Some(123).isInstanceOf[Option[List[String]]] - ^ -unchecked2.scala:5: error: non-variable type argument List[Int => String] in type Option[List[Int => String]] is unchecked since it is eliminated by erasure - Some(123).isInstanceOf[Option[List[Int => String]]] - ^ -unchecked2.scala:6: error: non-variable type argument (String, Double) in type Option[(String, Double)] is unchecked since it is eliminated by erasure - Some(123).isInstanceOf[Option[(String, Double)]] - ^ -unchecked2.scala:7: error: non-variable type argument String => Double in type Option[String => Double] is unchecked since it is eliminated by erasure - Some(123).isInstanceOf[Option[String => Double]] - ^ -6 errors found +unchecked2.scala:4: error: fruitless type test: a value of type Some[List[Int]] cannot also 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: fruitless type test: a value of type Some[Int] cannot also be a Option[String] (but still might match its erasure) + /* warn */ Some(123).isInstanceOf[Option[String]] + ^ +unchecked2.scala:7: error: fruitless type test: a value of type Some[Int] cannot also be a Option[List[String]] (but still might match its erasure) + /* warn */ Some(123).isInstanceOf[Option[List[String]]] + ^ +unchecked2.scala:8: error: fruitless type test: a value of type Some[Int] cannot also be a Option[List[Int => String]] (but still might match its erasure) + /* warn */ Some(123).isInstanceOf[Option[List[Int => String]]] + ^ +unchecked2.scala:9: error: fruitless type test: a value of type Some[Int] cannot also be a Option[(String, Double)] (but still might match its erasure) + /* warn */ Some(123).isInstanceOf[Option[(String, Double)]] + ^ +unchecked2.scala:10: error: fruitless type test: a value of type Some[Int] cannot also 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 + /* warn */ (Some(List(1)): Any).isInstanceOf[Option[List[String]]] + ^ +unchecked2.scala:15: error: non-variable type argument Int in type Option[Int] is unchecked since it is eliminated by erasure + /* warn */ (Some(123): Any).isInstanceOf[Option[Int]] + ^ +unchecked2.scala:16: error: non-variable type argument String in type Option[String] is unchecked since it is eliminated by erasure + /* warn */ (Some(123): Any).isInstanceOf[Option[String]] + ^ +unchecked2.scala:17: error: non-variable type argument List[String] in type Option[List[String]] is unchecked since it is eliminated by erasure + /* warn */ (Some(123): Any).isInstanceOf[Option[List[String]]] + ^ +unchecked2.scala:18: error: non-variable type argument List[Int => String] in type Option[List[Int => String]] is unchecked since it is eliminated by erasure + /* warn */ (Some(123): Any).isInstanceOf[Option[List[Int => String]]] + ^ +unchecked2.scala:19: error: non-variable type argument (String, Double) in type Option[(String, Double)] is unchecked since it is eliminated by erasure + /* warn */ (Some(123): Any).isInstanceOf[Option[(String, Double)]] + ^ +unchecked2.scala:20: error: non-variable type argument String => Double in type Option[String => Double] is unchecked since it is eliminated by erasure + /* warn */ (Some(123): Any).isInstanceOf[Option[String => Double]] + ^ +14 errors found diff --git a/test/files/neg/unchecked2.scala b/test/files/neg/unchecked2.scala index a2e757e1dc..616b05aad8 100644 --- a/test/files/neg/unchecked2.scala +++ b/test/files/neg/unchecked2.scala @@ -1,8 +1,33 @@ object Test { - Some(123).isInstanceOf[Option[Int]] - Some(123).isInstanceOf[Option[String]] - Some(123).isInstanceOf[Option[List[String]]] - Some(123).isInstanceOf[Option[List[Int => String]]] - Some(123).isInstanceOf[Option[(String, Double)]] - Some(123).isInstanceOf[Option[String => Double]] + // These warn because it can be statically shown they won't match. + + /* warn */ Some(List(1)).isInstanceOf[Option[List[String]]] + /* warn */ Some(123).isInstanceOf[Option[Option[_]]] + /* warn */ Some(123).isInstanceOf[Option[String]] + /* warn */ Some(123).isInstanceOf[Option[List[String]]] + /* warn */ Some(123).isInstanceOf[Option[List[Int => String]]] + /* warn */ Some(123).isInstanceOf[Option[(String, Double)]] + /* warn */ Some(123).isInstanceOf[Option[String => Double]] + + // These warn because you can't check at runtime. + + /* warn */ (Some(List(1)): Any).isInstanceOf[Option[List[String]]] + /* warn */ (Some(123): Any).isInstanceOf[Option[Int]] + /* warn */ (Some(123): Any).isInstanceOf[Option[String]] + /* warn */ (Some(123): Any).isInstanceOf[Option[List[String]]] + /* warn */ (Some(123): Any).isInstanceOf[Option[List[Int => String]]] + /* warn */ (Some(123): Any).isInstanceOf[Option[(String, Double)]] + /* warn */ (Some(123): Any).isInstanceOf[Option[String => Double]] + + // These don't warn. + + /* nowarn */ Some(List(1)).isInstanceOf[Option[List[Int]]] + /* nowarn */ Some(123).isInstanceOf[Option[Int]] + /* nowarn */ Some(123).isInstanceOf[Some[Int]] + /* nowarn */ Some(123).isInstanceOf[AnyRef] + + /* nowarn */ (Some(List(1)): Any).isInstanceOf[Option[_]] + /* nowarn */ (Some(123): Any).isInstanceOf[Option[_]] + /* nowarn */ (Some(123): Any).isInstanceOf[Some[_]] + /* nowarn */ (Some(123): Any).isInstanceOf[AnyRef] } diff --git a/test/files/neg/unchecked3.check b/test/files/neg/unchecked3.check new file mode 100644 index 0000000000..f4f0c74257 --- /dev/null +++ b/test/files/neg/unchecked3.check @@ -0,0 +1,37 @@ +unchecked3.scala:24: error: non-variable type argument Double in type pattern E1[Double] is unchecked since it is eliminated by erasure + /* warn */ def peerTypes2(x: B1[Int]) = x match { case _: E1[Double] => true } + ^ +unchecked3.scala:25: error: non-variable type argument Double in type pattern F1[Double] is unchecked since it is eliminated by erasure + /* warn */ def peerTypes3(x: B1[_]) = x match { case _: F1[Double] => true } + ^ +unchecked3.scala:28: error: non-variable type argument Int in type pattern A2[Int] is unchecked since it is eliminated by erasure + /* warn */ def twotypes1[T](x: B2[T, Int]) = x match { case _: A2[Int] => true } + ^ +unchecked3.scala:32: error: non-variable type argument Int in type pattern B2[_,Int] is unchecked since it is eliminated by erasure + /* warn */ def twotypes5[T](x: A2[T]) = x match { case _: B2[_, Int] => true } + ^ +unchecked3.scala:40: error: non-variable type argument String in type pattern Array[List[String]] is unchecked since it is eliminated by erasure + /* warn */ case _: Array[List[String]] => () + ^ +unchecked3.scala:43: error: non-variable type argument String in type pattern Array[Array[List[String]]] is unchecked since it is eliminated by erasure + /* warn */ case _: Array[Array[List[String]]] => () + ^ +unchecked3.scala:50: error: non-variable type argument String in type pattern Array[List[String]] is unchecked since it is eliminated by erasure + /* warn */ case _: Array[List[String]] => () + ^ +unchecked3.scala:53: error: non-variable type argument String in type pattern Array[Array[List[String]]] is unchecked since it is eliminated by erasure + /* warn */ case _: Array[Array[List[String]]] => () + ^ +unchecked3.scala:60: error: non-variable type argument String in type pattern Array[List[String]] is unchecked since it is eliminated by erasure + /* warn */ case _: Array[List[String]] => () + ^ +unchecked3.scala:62: error: non-variable type argument Array[String] in type pattern Array[List[Array[String]]] is unchecked since it is eliminated by erasure + /* warn */ case _: Array[List[Array[String]]] => () + ^ +unchecked3.scala:63: error: non-variable type argument String in type pattern Array[Array[List[String]]] is unchecked since it is eliminated by erasure + /* warn */ case _: Array[Array[List[String]]] => () + ^ +unchecked3.scala:75: error: abstract type A in type pattern Set[Q.this.A] is unchecked since it is eliminated by erasure + /* warn */ case xs: Set[A] => xs.head + ^ +12 errors found diff --git a/test/files/neg/unchecked3.flags b/test/files/neg/unchecked3.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/unchecked3.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/unchecked3.scala b/test/files/neg/unchecked3.scala new file mode 100644 index 0000000000..7b8c13e8f8 --- /dev/null +++ b/test/files/neg/unchecked3.scala @@ -0,0 +1,83 @@ +sealed trait A2[T1] +final class B2[T1, T2] extends A2[T1] + +sealed trait A[T] +final class B[T] extends A[T] + +sealed trait A1[T] +trait B1[T] extends A1[T] +trait C1[T] extends A1[T] +trait D1[T] extends A1[Int] +trait E1[T] extends B1[Int] +trait F1[T] extends B1[T] + +object MiscUnchecked { + /* nowarn */ def knownType1(x: A[Int]) = x match { case _: B[Int] if true => 1 } + /* nowarn */ def knownType2(x: B[Int]) = x match { case _: A[Int] if true => 1 } + /* nowarn */ def tparamLeakage1(x: Any) = x match { case Array() => 1 } + /* nowarn */ def tparamLeakage2(x: Any) = x match { case List() => 1 } + + // E1[Double] implies B1[Int], but B1[Int] does not imply E1[Double], even if .isInstanceOf[E1[_]] + // F1[Int] implies B1[Int], and B1[Int] implies F1[Int] + + /* nowarn */ def peerTypes1(x: B1[Int]) = x match { case _: C1[Int] => true } + /* warn */ def peerTypes2(x: B1[Int]) = x match { case _: E1[Double] => true } + /* warn */ def peerTypes3(x: B1[_]) = x match { case _: F1[Double] => true } + /* nowarn */ def peerTypes4(x: B1[Int]) = x match { case _: F1[Int] => true } + + /* warn */ def twotypes1[T](x: B2[T, Int]) = x match { case _: A2[Int] => true } + /* nowarn */ def twotypes2[T](x: B2[Int, T]) = x match { case _: A2[Int] => true } + /* nowarn */ def twotypes3(x: A2[Int]) = x match { case _: B2[Int, _] => true } + /* nowarn */ def twotypes4[T](x: A2[T]) = x match { case _: B2[T, _] => true } + /* warn */ def twotypes5[T](x: A2[T]) = x match { case _: B2[_, Int] => true } +} + +object Arrays { + def f1(x: Any) = x match { + /* nowarn */ case _: Array[Int] => () + /* nowarn */ case _: Array[Boolean] => () + /* nowarn */ case _: Array[String] => () + /* warn */ case _: Array[List[String]] => () + /* nowarn */ case _: Array[Array[String]] => () + /* nowarn */ case _: Array[Array[Array[String]]] => () + /* warn */ case _: Array[Array[List[String]]] => () + } + + def f2(x: Array[_]) = x match { + /* nowarn */ case _: Array[Int] => () + /* nowarn */ case _: Array[Boolean] => () + /* nowarn */ case _: Array[String] => () + /* warn */ case _: Array[List[String]] => () + /* nowarn */ case _: Array[Array[String]] => () + /* nowarn */ case _: Array[Array[Array[String]]] => () + /* warn */ case _: Array[Array[List[String]]] => () + } + + def f3[T](x: Array[T]) = x match { + /* nowarn */ case _: Array[Int] => () + /* nowarn */ case _: Array[Boolean] => () + /* nowarn */ case _: Array[String] => () + /* warn */ case _: Array[List[String]] => () + /* nowarn */ case _: Array[Array[String]] => () + /* warn */ case _: Array[List[Array[String]]] => () + /* warn */ case _: Array[Array[List[String]]] => () + } +} + +object Matching { + class Q { + type A + type B <: A + + def f(xs: Traversable[B]) = xs match { + /* nowarn */ case xs: List[A] => xs.head + /* nowarn */ case xs: Seq[B] => xs.head + /* warn */ case xs: Set[A] => xs.head + } + def f2[T <: B](xs: Traversable[T]) = xs match { + /* nowarn */ case xs: List[B with T] => xs.head + /* nowarn */ case xs: Seq[A] => xs.head + /* nowarn */ case xs: Set[T] => xs.head + } + } +} diff --git a/test/files/pos/t1107/O.scala b/test/files/pos/t1107b/O.scala index aa605a6d09..aa605a6d09 100644 --- a/test/files/pos/t1107/O.scala +++ b/test/files/pos/t1107b/O.scala diff --git a/test/files/pos/t1107/T.scala b/test/files/pos/t1107b/T.scala index 1f3712d529..1f3712d529 100644 --- a/test/files/pos/t1107/T.scala +++ b/test/files/pos/t1107b/T.scala diff --git a/test/files/pos/t1203/J.java b/test/files/pos/t1203b/J.java index 7fae118e04..7fae118e04 100644 --- a/test/files/pos/t1203/J.java +++ b/test/files/pos/t1203b/J.java diff --git a/test/files/pos/t1203/S.scala b/test/files/pos/t1203b/S.scala index 68eac4bf6d..68eac4bf6d 100644 --- a/test/files/pos/t1203/S.scala +++ b/test/files/pos/t1203b/S.scala diff --git a/test/files/pos/t6275.flags b/test/files/pos/t6275.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/pos/t6275.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/pos/t6275.scala b/test/files/pos/t6275.scala new file mode 100644 index 0000000000..6b5ec7dceb --- /dev/null +++ b/test/files/pos/t6275.scala @@ -0,0 +1,11 @@ + +sealed trait A[T] +final class B[T] extends A[T] + +object ParsedAxis { + type BI = B[Int] + + def f1(a: A[Int]) = a match { case b: B[Int] => 3 } + def f2(a: A[Int]) = a match { case b: BI => 3 } + def f3(a: A[Int]) = a match { case b: B[t] => 3 } +} diff --git a/test/files/run/t576.scala b/test/files/run/t576.scala index dc09d8dc98..756a241572 100644 --- a/test/files/run/t576.scala +++ b/test/files/run/t576.scala @@ -12,7 +12,7 @@ object Dingus { object Test { val x1 = new A val x2 = new A - + val x3 = new { self => override def equals(other : Any) = other match { case that: self.type => true @@ -20,7 +20,7 @@ object Test { } } val x4 = new { self => - def f(x: Any) = x match { + def f(x: Any): Int = x match { case _: x1.type => 1 case _: x2.type => 2 case _: x3.type => 3 @@ -35,11 +35,11 @@ object Test { assert(x1 != x2) assert(x1 != ()) assert(x2 != x1) - + assert(x3 == x3) assert(x3 != x2) assert(x2 != x3) - + List(x1, x2, x3, x4, Dingus) map x4.f foreach println } -}
\ No newline at end of file +} diff --git a/test/pending/pos/exhaust_2.scala b/test/pending/pos/exhaust_2.scala new file mode 100644 index 0000000000..4f4e47c43b --- /dev/null +++ b/test/pending/pos/exhaust_2.scala @@ -0,0 +1,54 @@ +object ExhaustivityWarnBugReportMinimal { + //sealed is needed for the warning + sealed trait FoundNode[T]/*presence of parameters is irrelevant*/ + // This also causes a warning: + // sealed abstract class FoundNode[T]/*presence of parameters is irrelevant*/ + case class FoundFilter[T](/*presence of parameters is irrelevant*/) extends FoundNode[T] + case class FoundTypeCase[T](/*presence of parameters is irrelevant*/) extends FoundNode[T] + val f: Some[_] = ??? + f match { + case x: Some[t] => //no warning + } + //With these variants, no warnings: + //val v: (Some[Int], FoundNode[_]) = (???, ???) + //val v: (Some[AnyRef], FoundNode[_]) = (???, ???) + //val v: (Some[String], FoundNode[_]) = (???, ???) + + val v: (Some[_], FoundNode[_]) = (???, ???) + //Warning here: + v match { + case (x: Some[t], _: FoundNode[_]) => + } + v match { + case (x: Some[t], _) => + } + + v match { + case (x: Some[_], _) => + } + case class Foo[T]() + + val vp: (Foo[_], FoundNode[_]) = (???, ???) + vp match { + case (x: Foo[_], _) => + } + + //No warning here: + v match { + case (Some(y), _) => + } + + v match { + case (x, _) => + } + + val v2: (Some[_], Int) = (???, ???) + v2 match { + case (x: Some[t], _) => + } + + val v3: (Option[_], FoundNode[_]) = (???, ???) + v match { + case (x: Option[_], _) => + } +} |