@@ -708,6 +708,42 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
708708 else text
709709 }
710710
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+
711747 /** Display spaces */
712748 def show (s : Space ): String = {
713749 def params (tp : Type ): List [Type ] = tp.classSymbol.primaryConstructor.info.firstParamTypes
@@ -775,6 +811,15 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
775811 res
776812 }
777813
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+
778823 def checkExhaustivity (_match : Match ): Unit = {
779824 val Match (sel, cases) = _match
780825 val selTyp = sel.tpe.widen.dealias
@@ -785,10 +830,15 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
785830 debug.println(s " ${x.pat.show} ====> ${show(space)}" )
786831 space
787832 }).reduce((a, b) => Or (List (a, b)))
788- val uncovered = simplify(minus(Typ (selTyp, true ), patternSpace), aggressive = true )
789833
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)
792842 }
793843
794844 def checkRedundancy (_match : Match ): Unit = {
0 commit comments