From 696dcdfcdb40ee3dbd2ba63f8281b87cde787a00 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 25 Feb 2013 17:58:11 +0100 Subject: SI-7126 Account for the alias types that don't dealias. After this change: qbin/scalac -Ydebug test/files/pos/t7126.scala 2>&1 | grep warning warning: dropExistential did not progress dealiasing Test.this.T[Test.this.T], see SI-7126 one warning found T[T]? Really? The true bug lies somewhere else; the comments of the ticket illuminate the general areas of concern. --- test/files/pos/t7126.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/files/pos/t7126.scala (limited to 'test') diff --git a/test/files/pos/t7126.scala b/test/files/pos/t7126.scala new file mode 100644 index 0000000000..6720511e08 --- /dev/null +++ b/test/files/pos/t7126.scala @@ -0,0 +1,11 @@ +import language._ + +object Test { + type T = Any + boom(???): Option[T] // SOE + def boom[CC[U]](t : CC[T]): Option[CC[T]] = None + + // okay + foo(???): Option[Any] + def foo[CC[U]](t : CC[Any]): Option[CC[Any]] = None +} \ No newline at end of file -- cgit v1.2.3 From 0303e64efa8ee223c0767d7be8d2875a62b88aea Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 25 Feb 2013 21:54:09 +0100 Subject: SI-7183 Disable unreachability for withFilter matches. This avoids spurious unreachable warnings on code that the user didn't write. The parser desugars for-comprehensions such as: for (A(a) <- List(new A)) yield a To: List(new A()).withFilter(((check$ifrefutable$2) => check$ifrefutable$2: @scala.unhecked match { case A((a @ _)) => true case _ => false }) ) But, if `A.unapply` returns `Some[_]`, the last case is dead code. (Matching against a regular case class *would* fall through in the caes of a null scrutinee.) In SI-6902, we enabled unreachability warnings, even if the scrutinee was annotated as @unchecked. That was consistent with the 2.9.2 behaviour, it was only disabled temporarily (actually, accidentally) in 2.10.0. But, the old pattern matcher didn't warn about this code. This commit makes the pattern matcher recognise the special scrutinee based on its name and disables both exhaustivity *and* unreachability analysis. To do so, the we generalize the boolean flag `unchecked` to the class `Suppression`. --- .../tools/nsc/typechecker/PatternMatching.scala | 42 ++++++++++++++-------- test/files/pos/t7183.flags | 1 + test/files/pos/t7183.scala | 13 +++++++ 3 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 test/files/pos/t7183.flags create mode 100644 test/files/pos/t7183.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index f7579ad249..87fe3fb3f6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -936,7 +936,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // the making of the trees /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// trait TreeMakers extends TypedSubstitution { self: CodegenCore => - def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): (List[List[TreeMaker]], List[Tree]) = + def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, supression: Suppression): (List[List[TreeMaker]], List[Tree]) = (cases, Nil) def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree], unchecked: Boolean): Option[Tree] = @@ -1408,18 +1408,24 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def matchFailGen = (matchFailGenOverride orElse Some(CODE.MATCHERROR(_: Tree))) patmatDebug("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}"))) - val (unchecked, requireSwitch) = - if (settings.XnoPatmatAnalysis.value) (true, false) + val (suppression, requireSwitch): (Suppression, Boolean) = + if (settings.XnoPatmatAnalysis.value) (Suppression.NoSuppresion, false) else scrut match { - case Typed(_, tpt) => - (tpt.tpe hasAnnotation UncheckedClass, - // matches with two or fewer cases need not apply for switchiness (if-then-else will do) - treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0) + case Typed(tree, tpt) => + val suppressExhaustive = tpt.tpe hasAnnotation UncheckedClass + val supressUnreachable = tree match { + case Ident(name) if name startsWith nme.CHECK_IF_REFUTABLE_STRING => true // SI-7183 don't warn for withFilter's that turn out to be irrefutable. + case _ => false + } + val suppression = Suppression(suppressExhaustive, supressUnreachable) + // matches with two or fewer cases need not apply for switchiness (if-then-else will do) + val requireSwitch = treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0 + (suppression, requireSwitch) case _ => - (false, false) + (Suppression.NoSuppresion, false) } - emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, unchecked).getOrElse{ + emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{ if (requireSwitch) typer.context.unit.warning(scrut.pos, "could not emit switch for @switch annotated match") if (casesNoSubstOnly nonEmpty) { @@ -1436,7 +1442,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL }) None else matchFailGen - val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt, unchecked) + val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt, suppression) val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases map combineExtractors, synthCatchAll) @@ -3831,11 +3837,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL with OptimizedCodegen with SymbolicMatchAnalysis with DPLLSolver { self: TreeMakers => - override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): (List[List[TreeMaker]], List[Tree]) = { - unreachableCase(prevBinder, cases, pt) foreach { caseIndex => - reportUnreachable(cases(caseIndex).last.pos) + override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, suppression: Suppression): (List[List[TreeMaker]], List[Tree]) = { + if (!suppression.unreachable) { + unreachableCase(prevBinder, cases, pt) foreach { caseIndex => + reportUnreachable(cases(caseIndex).last.pos) + } } - if (!unchecked) { + if (!suppression.exhaustive) { val counterExamples = exhaustive(prevBinder, cases, pt) if (counterExamples.nonEmpty) reportMissingCases(prevBinder.pos, counterExamples) @@ -3860,3 +3868,9 @@ object PatternMatchingStats { val patmatAnaExhaust = Statistics.newSubTimer (" of which in exhaustivity", patmatNanos) val patmatAnaReach = Statistics.newSubTimer (" of which in unreachability", patmatNanos) } + +final case class Suppression(exhaustive: Boolean, unreachable: Boolean) + +object Suppression { + val NoSuppresion = Suppression(false, false) +} \ No newline at end of file diff --git a/test/files/pos/t7183.flags b/test/files/pos/t7183.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t7183.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/pos/t7183.scala b/test/files/pos/t7183.scala new file mode 100644 index 0000000000..7647c1634b --- /dev/null +++ b/test/files/pos/t7183.scala @@ -0,0 +1,13 @@ +class A +object A { + def unapply(a: A): Some[A] = Some(a) // Change return type to Option[A] and the warning is gone +} + +object Test { + for (A(a) <- List(new A)) yield a // spurious dead code warning. +} + +// List(new A()).withFilter(((check$ifrefutable$2) => check$ifrefutable$2: @scala.unchecked match { +// case A((a @ _)) => true +// case _ => false // this is dead code, but it's compiler generated. +// })) -- cgit v1.2.3 From 09130d55fd6ab0431e37254ecf62bad10bb72c22 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 1 Mar 2013 17:05:12 -0800 Subject: [nomaster] SI-7195 minor version mustn't introduce warnings We want 2.10.1 to be a drop-in replacement for 2.10.0, so we can't start warning where we weren't warning in 2.10.0. See SI-5954 (#1882, #2079) for when it was an implementation restriction, which was then weakened to a warning. It's now hidden behind -Ydebug. --- .../scala/tools/nsc/typechecker/Typers.scala | 4 +- test/files/neg/t5954.check | 16 -------- test/files/neg/t5954.flags | 1 - test/files/neg/t5954.scala | 46 ---------------------- 4 files changed, 3 insertions(+), 64 deletions(-) delete mode 100644 test/files/neg/t5954.check delete mode 100644 test/files/neg/t5954.flags delete mode 100644 test/files/neg/t5954.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 4fb68fc2a9..6e9b3b3fcd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1823,7 +1823,9 @@ trait Typers extends Modes with Adaptations with Tags { def pkgObjectWarning(m : Symbol, mdef : ModuleDef, restricted : String) = { val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString - context.warning(if (m.pos.isDefined) m.pos else mdef.pos, s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") + val pos = if (m.pos.isDefined) m.pos else mdef.pos + debugwarn(s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") + debugwarn(pos.lineContent + (if (pos.isDefined) " " * (pos.column - 1) + "^" else "")) } } diff --git a/test/files/neg/t5954.check b/test/files/neg/t5954.check deleted file mode 100644 index ed10658b24..0000000000 --- a/test/files/neg/t5954.check +++ /dev/null @@ -1,16 +0,0 @@ -t5954.scala:36: error: class D should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - case class D() - ^ -t5954.scala:35: error: object C should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - object C - ^ -t5954.scala:34: error: trait C should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - trait C - ^ -t5954.scala:33: error: object B should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - object B - ^ -t5954.scala:32: error: class B should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - class B - ^ -5 errors found diff --git a/test/files/neg/t5954.flags b/test/files/neg/t5954.flags deleted file mode 100644 index 85d8eb2ba2..0000000000 --- a/test/files/neg/t5954.flags +++ /dev/null @@ -1 +0,0 @@ --Xfatal-warnings diff --git a/test/files/neg/t5954.scala b/test/files/neg/t5954.scala deleted file mode 100644 index 3ccb5ed3ff..0000000000 --- a/test/files/neg/t5954.scala +++ /dev/null @@ -1,46 +0,0 @@ -// if you ever think you've fixed the underlying reason for the warning -// imposed by SI-5954, then here's a test that should pass with two "succes"es -// -//import scala.tools.partest._ -// -//object Test extends DirectTest { -// def code = ??? -// -// def problemCode = """ -// package object A { -// class B -// object B -// case class C() -// } -// """ -// -// def compileProblemCode() = { -// val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") -// compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(problemCode) -// } -// -// def show() : Unit = { -// for (i <- 0 until 2) { -// compileProblemCode() -// println(s"success ${i + 1}") -// } -// } -//} - -package object A { - // these should be prevented by the implementation restriction - class B - object B - trait C - object C - case class D() - // all the rest of these should be ok - class E - object F - val g = "omg" - var h = "wtf" - def i = "lol" - type j = String - class K(val k : Int) extends AnyVal - implicit class L(val l : Int) -} -- cgit v1.2.3 From 2cf6c5d7e5cfbd60958eac0a545f5c2978a8e5cd Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 1 Mar 2013 18:23:57 -0800 Subject: [port] SI-7183 Disable unreachability for withFilter matches. This is a forward port of #2168 (originally for 2.10.1, but the pattern matcher has since been refactored in 2.10.x.) --- .../tools/nsc/transform/patmat/MatchAnalysis.scala | 10 ++++--- .../nsc/transform/patmat/MatchTreeMaking.scala | 33 ++++++++++++++-------- .../nsc/transform/patmat/PatternMatching.scala | 2 +- test/files/pos/t7183.flags | 1 + test/files/pos/t7183.scala | 13 +++++++++ 5 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 test/files/pos/t7183.flags create mode 100644 test/files/pos/t7183.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 74b932e173..d9f93f27b6 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -691,11 +691,13 @@ trait MatchAnalysis extends MatchApproximation { VariableAssignment(scrutVar).toCounterExample() } - def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): Unit = { - unreachableCase(prevBinder, cases, pt) foreach { caseIndex => - reportUnreachable(cases(caseIndex).last.pos) + def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, suppression: Suppression): Unit = { + if (!suppression.unreachable) { + unreachableCase(prevBinder, cases, pt) foreach { caseIndex => + reportUnreachable(cases(caseIndex).last.pos) + } } - if (!unchecked) { + if (!suppression.exhaustive) { val counterExamples = exhaustive(prevBinder, cases, pt) if (counterExamples.nonEmpty) reportMissingCases(prevBinder.pos, counterExamples) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index 51f21780b5..202f3444f8 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -23,16 +23,21 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { import global.{Tree, Type, Symbol, CaseDef, atPos, settings, Select, Block, ThisType, SingleType, NoPrefix, NoType, needsOuterTest, ConstantType, Literal, Constant, gen, This, EmptyTree, map2, NoSymbol, Traverser, - Function, Typed, treeInfo, TypeRef, DefTree} + Function, Typed, treeInfo, TypeRef, DefTree, Ident, nme} import global.definitions.{SomeClass, AnyRefClass, UncheckedClass, BooleanClass} + final case class Suppression(exhaustive: Boolean, unreachable: Boolean) + object Suppression { + val NoSuppression = Suppression(false, false) + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // the making of the trees /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// trait TreeMakers extends TypedSubstitution with CodegenCore { def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) - def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): Unit + def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, suppression: Suppression): Unit def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree], unchecked: Boolean): Option[Tree] = None @@ -522,18 +527,24 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def matchFailGen = (matchFailGenOverride orElse Some(CODE.MATCHERROR(_: Tree))) debug.patmat("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}"))) - val (unchecked, requireSwitch) = - if (settings.XnoPatmatAnalysis.value) (true, false) + val (suppression, requireSwitch): (Suppression, Boolean) = + if (settings.XnoPatmatAnalysis.value) (Suppression.NoSuppression, false) else scrut match { - case Typed(_, tpt) => - (tpt.tpe hasAnnotation UncheckedClass, - // matches with two or fewer cases need not apply for switchiness (if-then-else will do) - treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0) + case Typed(tree, tpt) => + val suppressExhaustive = tpt.tpe hasAnnotation UncheckedClass + val supressUnreachable = tree match { + case Ident(name) if name startsWith nme.CHECK_IF_REFUTABLE_STRING => true // SI-7183 don't warn for withFilter's that turn out to be irrefutable. + case _ => false + } + val suppression = Suppression(suppressExhaustive, supressUnreachable) + // matches with two or fewer cases need not apply for switchiness (if-then-else will do) + val requireSwitch = treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0 + (suppression, requireSwitch) case _ => - (false, false) + (Suppression.NoSuppression, false) } - emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, unchecked).getOrElse{ + emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{ if (requireSwitch) typer.context.unit.warning(scrut.pos, "could not emit switch for @switch annotated match") if (casesNoSubstOnly nonEmpty) { @@ -550,7 +561,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { }) None else matchFailGen - analyzeCases(scrutSym, casesNoSubstOnly, pt, unchecked) + analyzeCases(scrutSym, casesNoSubstOnly, pt, suppression) val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index f8d8044bdb..df4e699620 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -81,7 +81,7 @@ trait PatternMatching extends Transform with TypingTransformers class PureMatchTranslator(val typer: analyzer.Typer, val matchStrategy: Tree) extends MatchTranslator with PureCodegen { def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type) = (cases, Nil) - def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): Unit = {} + def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, suppression: Suppression): Unit = {} } class OptimizingMatchTranslator(val typer: analyzer.Typer) extends MatchTranslator diff --git a/test/files/pos/t7183.flags b/test/files/pos/t7183.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t7183.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/pos/t7183.scala b/test/files/pos/t7183.scala new file mode 100644 index 0000000000..7647c1634b --- /dev/null +++ b/test/files/pos/t7183.scala @@ -0,0 +1,13 @@ +class A +object A { + def unapply(a: A): Some[A] = Some(a) // Change return type to Option[A] and the warning is gone +} + +object Test { + for (A(a) <- List(new A)) yield a // spurious dead code warning. +} + +// List(new A()).withFilter(((check$ifrefutable$2) => check$ifrefutable$2: @scala.unchecked match { +// case A((a @ _)) => true +// case _ => false // this is dead code, but it's compiler generated. +// })) -- cgit v1.2.3