@@ -76,7 +76,6 @@ trait Checkable {
7676
7777 import global ._
7878 import definitions ._
79- import CheckabilityChecker .{ isNeverSubType , isNeverSubClass }
8079
8180 /** The applied type of class 'to' after inferring anything
8281 * possible from the knowledge that 'to' must also be of the
@@ -155,7 +154,7 @@ trait Checkable {
155154 scrut <:< pattTpWild
156155 }
157156
158- private class CheckabilityChecker (val X : Type , val P : Type ) {
157+ private class CheckabilityChecker (val X : Type , val P : Type , isRecheck : Boolean = false ) {
159158 def Xsym = X .typeSymbol
160159 def Psym = P .typeSymbol
161160 def PErased = {
@@ -166,7 +165,6 @@ trait Checkable {
166165 }
167166 def XR = if (Xsym == AnyClass ) PErased else propagateKnownTypes(X , Psym )
168167
169-
170168 // sadly the spec says (new java.lang.Boolean(true)).isInstanceOf[scala.Boolean]
171169 def P1 = scrutConformsToPatternType(X , P )
172170 def P2 = ! Psym .isPrimitiveValueClass && isNeverSubType(X , P )
@@ -215,11 +213,7 @@ trait Checkable {
215213 case TypeRef (_, sym, _) if sym.isAbstractType => " abstract type " + sym.name
216214 case tp => " non-variable type argument " + tp
217215 }
218- }
219216
220- /** X, P, [P1], etc. are all explained at the top of the file.
221- */
222- private object CheckabilityChecker {
223217 /** Are these symbols classes with no subclass relationship? */
224218 def areUnrelatedClasses (sym1 : Symbol , sym2 : Symbol ) = (
225219 sym1.isClass
@@ -242,23 +236,21 @@ trait Checkable {
242236 * - neither A nor B is a trait (i.e. both are actual classes, not eligible for mixin)
243237 * - both A and B are sealed/final, and every possible pairing of their children is irreconcilable
244238 *
245- * TODO: the last two conditions of the last possibility (that the symbols are not of
239+ * The last two conditions of the last possibility (that the symbols are not of
246240 * classes being compiled in the current run) are because this currently runs too early,
247241 * and .children returns Nil for sealed classes because their children will not be
248- * populated until typer. It was too difficult to move things around for the moment,
249- * so I will consult with moors about the optimal time to be doing this.
242+ * populated until typer. As a workaround, in this case, this check is performed a second
243+ * time at the end of typer. #6537, #12414
250244 */
251245 def areIrreconcilableAsParents (sym1 : Symbol , sym2 : Symbol ): Boolean = areUnrelatedClasses(sym1, sym2) && (
252246 isEffectivelyFinal(sym1) // initialization important
253247 || isEffectivelyFinal(sym2)
254248 || ! sym1.isTrait && ! sym2.isTrait
255- || isSealedOrFinal(sym1) && isSealedOrFinal(sym2) && allChildrenAreIrreconcilable(sym1, sym2) && ! currentRun.compiles(sym1) && ! currentRun.compiles(sym2)
249+ || isSealedOrFinal(sym1) && isSealedOrFinal(sym2) && allChildrenAreIrreconcilable(sym1, sym2) && (isRecheck || ! currentRun.compiles(sym1) && ! currentRun.compiles(sym2) )
256250 )
257251 private def isSealedOrFinal (sym : Symbol ) = sym.isSealed || sym.isFinal
258- private def isEffectivelyFinal (sym : Symbol ): Boolean = (
259- // initialization important
260- sym.initialize.isEffectivelyFinalOrNotOverridden
261- )
252+ // initialization important
253+ private def isEffectivelyFinal (sym : Symbol ): Boolean = sym.initialize.isEffectivelyFinalOrNotOverridden
262254
263255 def isNeverSubClass (sym1 : Symbol , sym2 : Symbol ) = areIrreconcilableAsParents(sym1, sym2)
264256
@@ -278,7 +270,7 @@ trait Checkable {
278270 case _ =>
279271 false
280272 }
281- // Important to dealias at any entry point (this is the only one at this writing.)
273+ // Important to dealias at any entry point (this is the only one at this writing but cf isNeverSubClass .)
282274 def isNeverSubType (tp1 : Type , tp2 : Type ): Boolean = /* logResult(s"isNeverSubType($tp1, $tp2)")*/ ((tp1.dealias, tp2.dealias) match {
283275 case (TypeRef (_, sym1, args1), TypeRef (_, sym2, args2)) =>
284276 isNeverSubClass(sym1, sym2) || {
@@ -311,13 +303,11 @@ trait Checkable {
311303 *
312304 * Instead of the canRemedy flag, annotate uncheckable types that have become checkable because of the availability of a class tag?
313305 */
314- def checkCheckable (tree : Tree , P0 : Type , X0 : Type , inPattern : Boolean , canRemedy : Boolean = false ): Unit = {
315- if (uncheckedOk(P0 )) return
316- def where = if (inPattern) " pattern " else " "
317-
318- if (P0 .typeSymbol == SingletonClass )
306+ def checkCheckable (tree : Tree , P0 : Type , X0 : Type , inPattern : Boolean , canRemedy : Boolean = false ): Unit = if (! uncheckedOk(P0 )) {
307+ if (P0 .typeSymbol == SingletonClass )
319308 context.warning(tree.pos, s " fruitless type test: every non-null value will be a Singleton dynamically " , WarningCategory .Other )
320309 else {
310+ def where = if (inPattern) " pattern " else " "
321311 // singleton types not considered here, dealias the pattern for SI-XXXX
322312 val P = P0 .dealiasWiden
323313 val X = X0 .widen
@@ -341,24 +331,38 @@ trait Checkable {
341331 if (checker.result == RuntimeCheckable )
342332 log(checker.summaryString)
343333
344- if (checker.neverMatches) {
345- val addendum = if (checker .neverSubClass) " " else " (but still might match its erasure)"
334+ def neverMatchesWarning ( result : CheckabilityChecker ) = {
335+ val addendum = if (result .neverSubClass) " " else " (but still might match its erasure)"
346336 context.warning(tree.pos, s " fruitless type test: a value of type $X cannot also be a $PString$addendum" , WarningCategory .Other )
347337 }
338+ if (checker.neverMatches)
339+ neverMatchesWarning(checker)
348340 else if (checker.isUncheckable) {
349341 val msg = (
350342 if (checker.uncheckableType =:= P ) s " abstract type $where$PString"
351343 else s " ${checker.uncheckableMessage} in type $where$PString"
352344 )
353345 context.warning(tree.pos, s " $msg is unchecked since it is eliminated by erasure " , WarningCategory .Unchecked )
354346 }
347+ else if (checker.result == RuntimeCheckable ) {
348+ // register deferred checking for sealed types in current run
349+ @ `inline` def Xsym = X .typeSymbol
350+ @ `inline` def Psym = P .typeSymbol
351+ @ `inline` def isSealedOrFinal (sym : Symbol ) = sym.isSealed || sym.isFinal
352+ def recheckFruitless (): Unit = {
353+ val rechecker = new CheckabilityChecker (X , P , isRecheck = true )
354+ if (rechecker.neverMatches) neverMatchesWarning(rechecker)
355+ }
356+ if (isSealedOrFinal(Xsym ) && isSealedOrFinal(Psym ) && (currentRun.compiles(Xsym ) || currentRun.compiles(Psym )))
357+ context.unit.toCheck += (() => recheckFruitless())
358+ }
355359 }
356360 }
357361 }
358362 }
359363}
360364
361- private [typechecker] final class Checkability (val value : Int ) extends AnyVal { }
365+ private [typechecker] final class Checkability (val value : Int ) extends AnyVal
362366private [typechecker] object Checkability {
363367 val StaticallyTrue = new Checkability (0 )
364368 val StaticallyFalse = new Checkability (1 )
0 commit comments