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