@@ -708,6 +708,38 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
708708 else text
709709 }
710710
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+
711743 /** Display spaces */
712744 def show (s : Space ): String = {
713745 def params (tp : Type ): List [Type ] = tp.classSymbol.primaryConstructor.info.firstParamTypes
@@ -775,6 +807,16 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
775807 res
776808 }
777809
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+
778820 def checkExhaustivity (_match : Match ): Unit = {
779821 val Match (sel, cases) = _match
780822 val selTyp = sel.tpe.widen.dealias
@@ -785,10 +827,15 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
785827 debug.println(s " ${x.pat.show} ====> ${show(space)}" )
786828 space
787829 }).reduce((a, b) => Or (List (a, b)))
788- val uncovered = simplify(minus(Typ (selTyp, true ), patternSpace), aggressive = true )
789830
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)
792839 }
793840
794841 def checkRedundancy (_match : Match ): Unit = {
0 commit comments