@@ -708,6 +708,42 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
708
708
else text
709
709
}
710
710
711
+ /** Whether the counterexample is satisfiable */
712
+ def satisfiable (sp : Space ): Boolean = {
713
+ def genConstraint (space : Space ): List [(Type , Type )] = space match {
714
+ case Prod (tp, unappTp, unappSym, ss, _) =>
715
+ val tps = signature(unappTp, unappSym, ss.length)
716
+ ss.zip(tps).flatMap {
717
+ case (sp : Prod , _) => genConstraint(sp)
718
+ case (Typ (tp1, _), tp2) => tp1 -> tp2 :: Nil
719
+ // case _ => ??? // impossible
720
+ }
721
+ case Typ (_, _) => Nil
722
+ // case _ => ??? // impossible
723
+ }
724
+
725
+ def checkConstraint (constrs : List [(Type , Type )]): Boolean = {
726
+ implicit val ctx1 = ctx.fresh.setNewTyperState()
727
+ val tvarMap = collection.mutable.Map .empty[Symbol , TypeVar ]
728
+ val typeParamMap = new TypeMap () {
729
+ override def apply (tp : Type ): Type = tp match {
730
+ case tref : TypeRef if tref.symbol.is(TypeParam ) =>
731
+ if (tvarMap.contains(tref.symbol)) tvarMap(tref.symbol)
732
+ else {
733
+ val tvar = newTypeVar(tref.underlying.bounds)
734
+ tvarMap(tref.symbol) = tvar
735
+ tvar
736
+ }
737
+ case tp => mapOver(tp)
738
+ }
739
+ }
740
+
741
+ constrs.foldLeft(true ) { case (acc, (tp1, tp2)) => acc && typeParamMap(tp1) <:< typeParamMap(tp2) }
742
+ }
743
+
744
+ checkConstraint(genConstraint(sp))
745
+ }
746
+
711
747
/** Display spaces */
712
748
def show (s : Space ): String = {
713
749
def params (tp : Type ): List [Type ] = tp.classSymbol.primaryConstructor.info.firstParamTypes
@@ -775,6 +811,15 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
775
811
res
776
812
}
777
813
814
+ def checkGADT (tp : Type ): Boolean = {
815
+ new TypeAccumulator [Boolean ] {
816
+ override def apply (b : Boolean , tp : Type ): Boolean = tp match {
817
+ case tref : TypeRef if tref.symbol.is(TypeParam ) => true
818
+ case tp => b || foldOver(b, tp)
819
+ }
820
+ }.apply(false , tp)
821
+ }
822
+
778
823
def checkExhaustivity (_match : Match ): Unit = {
779
824
val Match (sel, cases) = _match
780
825
val selTyp = sel.tpe.widen.dealias
@@ -785,10 +830,15 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
785
830
debug.println(s " ${x.pat.show} ====> ${show(space)}" )
786
831
space
787
832
}).reduce((a, b) => Or (List (a, b)))
788
- val uncovered = simplify(minus(Typ (selTyp, true ), patternSpace), aggressive = true )
789
833
790
- if (uncovered != Empty )
791
- ctx.warning(PatternMatchExhaustivity (show(uncovered)), sel.pos)
834
+ val checkGADTSAT = checkGADT(selTyp)
835
+
836
+ val uncovered =
837
+ flatten(simplify(minus(Typ (selTyp, true ), patternSpace), aggressive = true ))
838
+ .filter(s => s != Empty && (! checkGADTSAT || satisfiable(s)))
839
+
840
+ if (uncovered.nonEmpty)
841
+ ctx.warning(PatternMatchExhaustivity (show(Or (uncovered))), sel.pos)
792
842
}
793
843
794
844
def checkRedundancy (_match : Match ): Unit = {
0 commit comments