Take account of GADTs when reporting patterm-match overlap
authorsimonpj@microsoft.com <unknown>
Wed, 22 Jul 2009 05:09:33 +0000 (05:09 +0000)
committersimonpj@microsoft.com <unknown>
Wed, 22 Jul 2009 05:09:33 +0000 (05:09 +0000)
When matching against a GADT, some of the constructors may be impossible.
For example
data T a where
          T1 :: T Int
          T2 :: T Bool
          T3 :: T a

        f :: T Int -> Int
        f T1 = 3
        f T3 = 4

Here, does not have any missing cases, despite omittting T2, because
T2 :: T Bool.

This patch teaches the overlap checker about GADTs, which happily
turned out to be rather easy even though the overlap checker needs
a serious rewrite.

compiler/deSugar/Check.lhs
compiler/typecheck/TcTyClsDecls.lhs

index c5b13eb..6244b37 100644 (file)
@@ -26,6 +26,8 @@ import Name
 import TysWiredIn
 import PrelNames
 import TyCon
+import Type
+import Unify( dataConCannotMatch )
 import SrcLoc
 import UniqSet
 import Util
@@ -446,12 +448,13 @@ mb_neg (Just _) v = -v
 get_unused_cons :: [Pat Id] -> [DataCon]
 get_unused_cons used_cons = ASSERT( not (null used_cons) ) unused_cons
      where
-       (ConPatOut { pat_con = l_con }) = head used_cons
-       ty_con         = dataConTyCon (unLoc l_con)     -- Newtype observable
-       all_cons        = tyConDataCons ty_con
-       used_cons_as_id = map (\ (ConPatOut{ pat_con = L _ d}) -> d) used_cons
-       unused_cons     = uniqSetToList
-                        (mkUniqSet all_cons `minusUniqSet` mkUniqSet used_cons_as_id) 
+       used_set :: UniqSet DataCon
+       used_set = mkUniqSet [d | ConPatOut{ pat_con = L _ d} <- used_cons]
+       (ConPatOut { pat_ty = ty }) = head used_cons
+       Just (ty_con, inst_tys) = splitTyConApp_maybe ty
+       unused_cons = filterOut is_used (tyConDataCons ty_con)
+       is_used con = con `elementOfUniqSet` used_set
+                    || dataConCannotMatch inst_tys con
 
 all_vars :: [Pat Id] -> Bool
 all_vars []             = True
index 0e59f01..049276d 100644 (file)
@@ -1257,11 +1257,18 @@ mkRecSelBind (tycon, sel_name)
     -- Add catch-all default case unless the case is exhaustive
     -- We do this explicitly so that we get a nice error message that
     -- mentions this particular record selector
-    deflt | length cons_w_field == length all_cons = []
+    deflt | not (any is_unused all_cons) = []
          | otherwise = [mkSimpleMatch [nlWildPat] 
                            (nlHsApp (nlHsVar (getName rEC_SEL_ERROR_ID))
                                     (nlHsLit msg_lit))]
 
+       -- Do not add a default case unless there are unmatched
+       -- constructors.  We must take account of GADTs, else we
+       -- get overlap warning messages from the pattern-match checker
+    is_unused con = not (con `elem` cons_w_field 
+                        || dataConCannotMatch inst_tys con)
+    inst_tys = tyConAppArgs data_ty
+
     unit_rhs = L loc $ ExplicitTuple [] Boxed
     msg_lit = HsStringPrim $ mkFastString $ 
               occNameString (getOccName sel_name)