@@ -278,21 +278,8 @@ trait SpaceLogic {
278
278
}
279
279
}
280
280
281
- object SpaceEngine {
282
- private sealed trait Implementability {
283
- def show (implicit ctx : Context ) = this match {
284
- case SubclassOf (classSyms) => s " SubclassOf( ${classSyms.map(_.show)}) "
285
- case other => other.toString
286
- }
287
- }
288
- private case object ClassOrTrait extends Implementability
289
- private case class SubclassOf (classSyms : List [Symbol ]) extends Implementability
290
- private case object Unimplementable extends Implementability
291
- }
292
-
293
281
/** Scala implementation of space logic */
294
282
class SpaceEngine (implicit ctx : Context ) extends SpaceLogic {
295
- import SpaceEngine ._
296
283
import tpd ._
297
284
298
285
private val scalaSomeClass = ctx.requiredClass(" scala.Some" )
@@ -301,77 +288,15 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
301
288
private val scalaNilType = ctx.requiredModuleRef(" scala.collection.immutable.Nil" )
302
289
private val scalaConsType = ctx.requiredClassRef(" scala.collection.immutable.::" )
303
290
304
- /** Checks if it's possible to create a trait/class which is a subtype of `tp`.
305
- *
306
- * - doesn't handle member collisions (will not declare a type unimplementable because of one)
307
- * - expects that neither Any nor Object reach it
308
- * (this is currently true due to both isSubType and and/or type simplification)
309
- *
310
- * See [[intersectUnrelatedAtomicTypes ]].
311
- */
312
- private def implementability (tp : Type ): Implementability = tp.dealias match {
313
- case AndType (tp1, tp2) =>
314
- (implementability(tp1), implementability(tp2)) match {
315
- case (Unimplementable , _) | (_, Unimplementable ) => Unimplementable
316
- case (SubclassOf (classSyms1), SubclassOf (classSyms2)) =>
317
- (for {
318
- sym1 <- classSyms1
319
- sym2 <- classSyms2
320
- result <-
321
- if (sym1 isSubClass sym2) List (sym1)
322
- else if (sym2 isSubClass sym1) List (sym2)
323
- else Nil
324
- } yield result) match {
325
- case Nil => Unimplementable
326
- case lst => SubclassOf (lst)
327
- }
328
- case (ClassOrTrait , ClassOrTrait ) => ClassOrTrait
329
- case (SubclassOf (clss), _) => SubclassOf (clss)
330
- case (_, SubclassOf (clss)) => SubclassOf (clss)
331
- }
332
- case OrType (tp1, tp2) =>
333
- (implementability(tp1), implementability(tp2)) match {
334
- case (ClassOrTrait , _) | (_, ClassOrTrait ) => ClassOrTrait
335
- case (SubclassOf (classSyms1), SubclassOf (classSyms2)) =>
336
- SubclassOf (classSyms1 ::: classSyms2)
337
- case (SubclassOf (classSyms), _) => SubclassOf (classSyms)
338
- case (_, SubclassOf (classSyms)) => SubclassOf (classSyms)
339
- case _ => Unimplementable
340
- }
341
- case _ : SingletonType =>
342
- // singleton types have no instantiable subtypes
343
- Unimplementable
344
- case tp : RefinedType =>
345
- // refinement itself is not considered - it would at most make
346
- // a type unimplementable because of a member collision
347
- implementability(tp.parent)
348
- case other =>
349
- val classSym = other.classSymbol
350
- if (classSym.exists) {
351
- if (classSym is Final ) Unimplementable
352
- else if (classSym is Trait ) ClassOrTrait
353
- else SubclassOf (List (classSym))
354
- } else {
355
- // if no class symbol exists, conservatively say that anything
356
- // can implement `tp`
357
- ClassOrTrait
358
- }
359
- }
360
-
361
291
override def intersectUnrelatedAtomicTypes (tp1 : Type , tp2 : Type ) = {
362
292
val and = AndType (tp1, tp2)
363
293
// Precondition: !(tp1 <:< tp2) && !(tp2 <:< tp1)
364
294
// Then, no leaf of the and-type tree `and` is a subtype of `and`.
365
- // Then, to create a value of type `and` you must instantiate a trait (class/module)
366
- // which is a subtype of all the leaves of `and`.
367
- val imp = implementability(and)
295
+ val res = inhabited(and)
368
296
369
- debug.println(s " atomic intersection: ${and.show} ~ ${imp.show }" )
297
+ debug.println(s " atomic intersection: ${and.show} = ${res }" )
370
298
371
- imp match {
372
- case Unimplementable => Empty
373
- case _ => Typ (and, true )
374
- }
299
+ if (res) Typ (and, true ) else Empty
375
300
}
376
301
377
302
/* Whether the extractor is irrefutable */
@@ -574,21 +499,92 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
574
499
575
500
/** Can this type be inhabited by a value?
576
501
*
577
- * Intersection between singleton types and other types is always empty
578
- * the singleton type is not a subtype of the other type.
579
- * See patmat/i3573.scala for an example.
502
+ * Check is based on the following facts:
503
+ *
504
+ * - single inheritance of classes
505
+ * - final class cannot be extended
506
+ * - intersection of a singleton type with another irrelevant type (patmat/i3574.scala)
507
+ *
580
508
*/
581
- def inhabited (tpe : Type )(implicit ctx : Context ): Boolean = {
582
- val emptySingletonIntersection = new ExistsAccumulator ({
583
- case AndType (s : SingletonType , t) =>
584
- ! (s <:< t)
585
- case AndType (t, s : SingletonType ) =>
586
- ! (s <:< t)
587
- case x =>
588
- false
589
- })
590
-
591
- ! emptySingletonIntersection(false , tpe)
509
+ def inhabited (tp : Type )(implicit ctx : Context ): Boolean = {
510
+ // convert top-level type shape into "conjunctive normal form"
511
+ def cnf (tp : Type ): Type = tp match {
512
+ case AndType (OrType (l, r), tp) =>
513
+ OrType (cnf(AndType (l, tp)), cnf(AndType (r, tp)))
514
+ case AndType (tp, o : OrType ) =>
515
+ cnf(AndType (o, tp))
516
+ case AndType (l, r) =>
517
+ val l1 = cnf(l)
518
+ val r1 = cnf(r)
519
+ if (l1.ne(l) || r1.ne(r)) cnf(AndType (l1, r1))
520
+ else AndType (l1, r1)
521
+ case OrType (l, r) =>
522
+ OrType (cnf(l), cnf(r))
523
+ case tp @ RefinedType (OrType (tp1, tp2), _, _) =>
524
+ OrType (
525
+ cnf(tp.derivedRefinedType(tp1, refinedName = tp.refinedName, refinedInfo = tp.refinedInfo)),
526
+ cnf(tp.derivedRefinedType(tp2, refinedName = tp.refinedName, refinedInfo = tp.refinedInfo))
527
+ )
528
+ case tp : RefinedType =>
529
+ val parent1 = cnf(tp.parent)
530
+ val tp1 = tp.derivedRefinedType(parent1, refinedName = tp.refinedName, refinedInfo = tp.refinedInfo)
531
+
532
+ if (parent1.ne(tp.parent)) cnf(tp1) else tp1
533
+ case tp : TypeAlias =>
534
+ cnf(tp.alias)
535
+ case _ =>
536
+ tp
537
+ }
538
+
539
+ def isSingleton (tp : Type ): Boolean = tp.dealias match {
540
+ case AndType (l, r) => isSingleton(l) || isSingleton(r)
541
+ case OrType (l, r) => isSingleton(l) && isSingleton(r)
542
+ case tp => tp.isSingleton
543
+ }
544
+
545
+ def superType (tp : Type ): Type = tp match {
546
+ case tp : TypeProxy => tp.superType
547
+ case OrType (tp1, tp2) => OrType (superType(tp1), superType(tp2))
548
+ case AndType (tp1, tp2) => AndType (superType(tp1), superType(tp2))
549
+ case _ => tp
550
+ }
551
+
552
+ def recur (tp : Type ): Boolean = tp.dealias match {
553
+ case AndType (tp1, tp2) =>
554
+ recur(tp1) && recur(tp2) && {
555
+ val bases1 = tp1.widenDealias.classSymbols
556
+ val bases2 = tp2.widenDealias.classSymbols
557
+
558
+ debug.println(s " bases of ${tp1.show}: " + bases1)
559
+ debug.println(s " bases of ${tp2.show}: " + bases2)
560
+
561
+ val noClassConflict =
562
+ bases1.forall(sym1 => sym1.is(Trait ) || bases2.forall(sym2 => sym2.is(Trait ) || sym1.isSubClass(sym2))) ||
563
+ bases1.forall(sym1 => sym1.is(Trait ) || bases2.forall(sym2 => sym2.is(Trait ) || sym2.isSubClass(sym1)))
564
+
565
+ debug.println(s " class conflict for ${tp.show}? " + ! noClassConflict)
566
+
567
+ noClassConflict &&
568
+ (! isSingleton(tp1) || tp1 <:< tp2) &&
569
+ (! isSingleton(tp2) || tp2 <:< tp1) &&
570
+ (! bases1.exists(_ is Final ) || tp1 <:< superType(tp2)) &&
571
+ (! bases2.exists(_ is Final ) || tp2 <:< superType(tp1))
572
+ }
573
+ case OrType (tp1, tp2) =>
574
+ recur(tp1) || recur(tp2)
575
+ case tp : RefinedType =>
576
+ recur(tp.parent)
577
+ case tp : TypeRef =>
578
+ recur(tp.prefix) && ! (tp.classSymbol.is(AbstractFinal ))
579
+ case _ =>
580
+ true
581
+ }
582
+
583
+ val res = recur(cnf(tp))
584
+
585
+ debug.println(s " ${tp.show} inhabited? " + res)
586
+
587
+ res
592
588
}
593
589
594
590
/** Instantiate type `tp1` to be a subtype of `tp2`
@@ -708,8 +704,10 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
708
704
/** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */
709
705
def canDecompose (tp : Type ): Boolean = {
710
706
val dealiasedTp = tp.dealias
711
- val res = tp.classSymbol.is(allOf(Abstract , Sealed )) ||
712
- tp.classSymbol.is(allOf(Trait , Sealed )) ||
707
+ val res =
708
+ (tp.classSymbol.is(Sealed ) &&
709
+ tp.classSymbol.is(AbstractOrTrait ) &&
710
+ tp.classSymbol.children.nonEmpty ) ||
713
711
dealiasedTp.isInstanceOf [OrType ] ||
714
712
(dealiasedTp.isInstanceOf [AndType ] && {
715
713
val and = dealiasedTp.asInstanceOf [AndType ]
@@ -905,24 +903,25 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
905
903
def checkRedundancy (_match : Match ): Unit = {
906
904
val Match (sel, cases) = _match
907
905
// ignore selector type for now
908
- // val selTyp = sel.tpe.widen.dealias
909
-
910
- if (cases.length == 1 ) return
906
+ val selTyp = sel.tpe.widen.dealias
911
907
912
- // starts from the second, the first can't be redundant
913
- (1 until cases.length).foreach { i =>
908
+ (0 until cases.length).foreach { i =>
914
909
// in redundancy check, take guard as false in order to soundly approximate
915
- val prevs = cases.take(i).map { x =>
916
- if (x.guard.isEmpty) project(x.pat)
917
- else Empty
918
- }.reduce((a, b) => Or (List (a, b)))
910
+ val prevs =
911
+ if (i == 0 )
912
+ Empty
913
+ else
914
+ cases.take(i).map { x =>
915
+ if (x.guard.isEmpty) project(x.pat)
916
+ else Empty
917
+ }.reduce((a, b) => Or (List (a, b)))
919
918
920
919
val curr = project(cases(i).pat)
921
920
922
921
debug.println(s " ---------------reachable? ${show(curr)}" )
923
922
debug.println(s " prev: ${show(prevs)}" )
924
923
925
- if (isSubspace(curr, prevs)) {
924
+ if (isSubspace(intersect( curr, Typ (selTyp, false )) , prevs)) {
926
925
ctx.warning(MatchCaseUnreachable (), cases(i).body.pos)
927
926
}
928
927
}
0 commit comments