Skip to content

Commit 288bc58

Browse files
authored
Merge pull request scala#10657 from dwijnand/fruitless-type-test
Fix lack of incompatible type warnings
2 parents e59b420 + d53a4db commit 288bc58

File tree

6 files changed

+68
-5
lines changed

6 files changed

+68
-5
lines changed

src/compiler/scala/tools/nsc/typechecker/Checkable.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ trait Checkable {
223223
* additional conditions holds:
224224
* - either A or B is effectively final
225225
* - neither A nor B is a trait (i.e. both are actual classes, not eligible for mixin)
226-
* - both A and B are sealed/final, and every possible pairing of their children is irreconcilable
226+
* - either A or B is sealed/final, and every possible pairing of their children (or themselves) is irreconcilable
227227
*
228228
* The last two conditions of the last possibility (that the symbols are not of
229229
* classes being compiled in the current run) are because this currently runs too early,
@@ -241,15 +241,15 @@ trait Checkable {
241241
)
242242
// Are all children of these symbols pairwise irreconcilable?
243243
def allChildrenAreIrreconcilable(sym1: Symbol, sym2: Symbol) = {
244-
val sc1 = sym1.sealedChildren
245-
val sc2 = sym2.sealedChildren
244+
val sc1 = if (isSealedOrFinal(sym1)) sym1.sealedChildren else Set(sym1)
245+
val sc2 = if (isSealedOrFinal(sym2)) sym2.sealedChildren else Set(sym2)
246246
sc1.forall(c1 => sc2.forall(c2 => areIrreconcilableAsParents(c1, c2)))
247247
}
248248
areUnrelatedClasses(sym1, sym2) && (
249249
isEffectivelyFinal(sym1) // initialization important
250250
|| isEffectivelyFinal(sym2)
251251
|| !sym1.isTrait && !sym2.isTrait
252-
|| isSealedOrFinal(sym1) && isSealedOrFinal(sym2) && allChildrenAreIrreconcilable(sym1, sym2) && (isRecheck || !currentRun.compiles(sym1) && !currentRun.compiles(sym2))
252+
|| (isSealedOrFinal(sym1) || isSealedOrFinal(sym2)) && allChildrenAreIrreconcilable(sym1, sym2) && (isRecheck || !currentRun.compiles(sym1) && !currentRun.compiles(sym2))
253253
)
254254
}
255255
private def isSealedOrFinal(sym: Symbol) = sym.isSealed || sym.isFinal
@@ -370,7 +370,7 @@ trait Checkable {
370370
def isSealedOrFinal(sym: Symbol) = sym.isSealed || sym.isFinal
371371
val Xsym = X.typeSymbol
372372
val Psym = P.typeSymbol
373-
if (isSealedOrFinal(Xsym) && isSealedOrFinal(Psym) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) {
373+
if ((isSealedOrFinal(Xsym) || isSealedOrFinal(Psym)) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) {
374374
debuglog(s"deferred recheckFruitless($X, $P)")
375375
context.unit.addPostTyperCheck(() => recheckFruitless())
376376
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
patmat-sealed-reachable.scala:14: warning: fruitless type test: a value of type Option[Int] cannot also be a SomeClass
2+
def b(c: Option[Int]) = c match { case _: SomeClass =>; case _ => }
3+
^
4+
patmat-sealed-reachable.scala:13: warning: fruitless type test: a value of type Option[Int] cannot also be a SealedTrait
5+
def a(c: Option[Int]) = c match { case _: SealedTrait =>; case _ => }
6+
^
7+
patmat-sealed-reachable.scala:15: warning: fruitless type test: a value of type Option[Int] cannot also be a UnsealedTrait
8+
def c(c: Option[Int]) = c match { case _: UnsealedTrait =>; case _ => }
9+
^
10+
error: No warnings can be incurred under -Werror.
11+
3 warnings
12+
1 error
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// scalac: -Werror
2+
3+
// aka t12438.scala
4+
5+
sealed trait SealedTrait
6+
class SomeClass
7+
trait UnsealedTrait
8+
9+
sealed abstract class O
10+
class O1 extends O
11+
12+
class Test {
13+
def a(c: Option[Int]) = c match { case _: SealedTrait =>; case _ => }
14+
def b(c: Option[Int]) = c match { case _: SomeClass =>; case _ => }
15+
def c(c: Option[Int]) = c match { case _: UnsealedTrait =>; case _ => }
16+
// O1 is not final , so there could be a value of type O1 with UnsealedTrait
17+
def nowarn(c: O) = c match { case _: UnsealedTrait =>; case _ => }
18+
}

test/files/neg/t12304.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
t12304.scala:10: warning: fruitless type test: a value of type Foo cannot also be a Bar
2+
m.collect { case (_, bar: Bar) =>}
3+
^
4+
error: No warnings can be incurred under -Werror.
5+
1 warning
6+
1 error

test/files/neg/t12304.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// scalac: -Werror
2+
3+
// variant of pos/t12304
4+
// that does warn as desired
5+
6+
class Foo; class Bar
7+
class Test {
8+
def t1: Unit = {
9+
val m = Map(1 -> new Foo)
10+
m.collect { case (_, bar: Bar) =>}
11+
}
12+
}

test/files/pos/t12304.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// scalac: -Werror
2+
3+
class Foo
4+
class Test {
5+
def t1: Unit = {
6+
val m: Map[String, Map[String, Foo]] = Map("outer" -> Map("inner" -> new Foo))
7+
m.collect { case (_, foo: Foo) => "This should be type error" }
8+
// no:
9+
// class Foo isn't final
10+
// Map is an unsealed trait
11+
// so it's possible to define:
12+
// class Bar extends Foo with Map[...]
13+
// so the compiler is right not to warn
14+
}
15+
}

0 commit comments

Comments
 (0)