Skip to content

Commit

Permalink
Merge pull request #10657 from dwijnand/fruitless-type-test
Browse files Browse the repository at this point in the history
Fix lack of incompatible type warnings
  • Loading branch information
lrytz committed Jan 12, 2024
2 parents e59b420 + d53a4db commit 288bc58
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 5 deletions.
10 changes: 5 additions & 5 deletions src/compiler/scala/tools/nsc/typechecker/Checkable.scala
Expand Up @@ -223,7 +223,7 @@ trait Checkable {
* additional conditions holds:
* - either A or B is effectively final
* - neither A nor B is a trait (i.e. both are actual classes, not eligible for mixin)
* - both A and B are sealed/final, and every possible pairing of their children is irreconcilable
* - either A or B is sealed/final, and every possible pairing of their children (or themselves) is irreconcilable
*
* The last two conditions of the last possibility (that the symbols are not of
* classes being compiled in the current run) are because this currently runs too early,
Expand All @@ -241,15 +241,15 @@ trait Checkable {
)
// Are all children of these symbols pairwise irreconcilable?
def allChildrenAreIrreconcilable(sym1: Symbol, sym2: Symbol) = {
val sc1 = sym1.sealedChildren
val sc2 = sym2.sealedChildren
val sc1 = if (isSealedOrFinal(sym1)) sym1.sealedChildren else Set(sym1)
val sc2 = if (isSealedOrFinal(sym2)) sym2.sealedChildren else Set(sym2)
sc1.forall(c1 => sc2.forall(c2 => areIrreconcilableAsParents(c1, c2)))
}
areUnrelatedClasses(sym1, sym2) && (
isEffectivelyFinal(sym1) // initialization important
|| isEffectivelyFinal(sym2)
|| !sym1.isTrait && !sym2.isTrait
|| isSealedOrFinal(sym1) && isSealedOrFinal(sym2) && allChildrenAreIrreconcilable(sym1, sym2) && (isRecheck || !currentRun.compiles(sym1) && !currentRun.compiles(sym2))
|| (isSealedOrFinal(sym1) || isSealedOrFinal(sym2)) && allChildrenAreIrreconcilable(sym1, sym2) && (isRecheck || !currentRun.compiles(sym1) && !currentRun.compiles(sym2))
)
}
private def isSealedOrFinal(sym: Symbol) = sym.isSealed || sym.isFinal
Expand Down Expand Up @@ -370,7 +370,7 @@ trait Checkable {
def isSealedOrFinal(sym: Symbol) = sym.isSealed || sym.isFinal
val Xsym = X.typeSymbol
val Psym = P.typeSymbol
if (isSealedOrFinal(Xsym) && isSealedOrFinal(Psym) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) {
if ((isSealedOrFinal(Xsym) || isSealedOrFinal(Psym)) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) {
debuglog(s"deferred recheckFruitless($X, $P)")
context.unit.addPostTyperCheck(() => recheckFruitless())
}
Expand Down
12 changes: 12 additions & 0 deletions test/files/neg/patmat-sealed-reachable.check
@@ -0,0 +1,12 @@
patmat-sealed-reachable.scala:14: warning: fruitless type test: a value of type Option[Int] cannot also be a SomeClass
def b(c: Option[Int]) = c match { case _: SomeClass =>; case _ => }
^
patmat-sealed-reachable.scala:13: warning: fruitless type test: a value of type Option[Int] cannot also be a SealedTrait
def a(c: Option[Int]) = c match { case _: SealedTrait =>; case _ => }
^
patmat-sealed-reachable.scala:15: warning: fruitless type test: a value of type Option[Int] cannot also be a UnsealedTrait
def c(c: Option[Int]) = c match { case _: UnsealedTrait =>; case _ => }
^
error: No warnings can be incurred under -Werror.
3 warnings
1 error
18 changes: 18 additions & 0 deletions test/files/neg/patmat-sealed-reachable.scala
@@ -0,0 +1,18 @@
// scalac: -Werror

// aka t12438.scala

sealed trait SealedTrait
class SomeClass
trait UnsealedTrait

sealed abstract class O
class O1 extends O

class Test {
def a(c: Option[Int]) = c match { case _: SealedTrait =>; case _ => }
def b(c: Option[Int]) = c match { case _: SomeClass =>; case _ => }
def c(c: Option[Int]) = c match { case _: UnsealedTrait =>; case _ => }
// O1 is not final , so there could be a value of type O1 with UnsealedTrait
def nowarn(c: O) = c match { case _: UnsealedTrait =>; case _ => }
}
6 changes: 6 additions & 0 deletions test/files/neg/t12304.check
@@ -0,0 +1,6 @@
t12304.scala:10: warning: fruitless type test: a value of type Foo cannot also be a Bar
m.collect { case (_, bar: Bar) =>}
^
error: No warnings can be incurred under -Werror.
1 warning
1 error
12 changes: 12 additions & 0 deletions test/files/neg/t12304.scala
@@ -0,0 +1,12 @@
// scalac: -Werror

// variant of pos/t12304
// that does warn as desired

class Foo; class Bar
class Test {
def t1: Unit = {
val m = Map(1 -> new Foo)
m.collect { case (_, bar: Bar) =>}
}
}
15 changes: 15 additions & 0 deletions test/files/pos/t12304.scala
@@ -0,0 +1,15 @@
// scalac: -Werror

class Foo
class Test {
def t1: Unit = {
val m: Map[String, Map[String, Foo]] = Map("outer" -> Map("inner" -> new Foo))
m.collect { case (_, foo: Foo) => "This should be type error" }
// no:
// class Foo isn't final
// Map is an unsealed trait
// so it's possible to define:
// class Bar extends Foo with Map[...]
// so the compiler is right not to warn
}
}

0 comments on commit 288bc58

Please sign in to comment.