summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Pickering <matthewtpickering@gmail.com>2017-01-18 13:25:30 +0000
committerMatthew Pickering <matthewtpickering@gmail.com>2017-01-26 00:22:46 +0000
commit1a3f1eebf81952accb6340252816211c7d391300 (patch)
tree03fbe6fac6518c3da73282266833941d76b34736
parent078c21140d4f27e586c9fa893d4ac94d28d6013c (diff)
downloadhaskell-1a3f1eebf81952accb6340252816211c7d391300.tar.gz
COMPLETE pragmas for enhanced pattern exhaustiveness checking
This patch adds a new pragma so that users can specify `COMPLETE` sets of `ConLike`s in order to sate the pattern match checker. A function which matches on all the patterns in a complete grouping will not cause the exhaustiveness checker to emit warnings. ``` pattern P :: () pattern P = () {-# COMPLETE P #-} foo P = () ``` This example would previously have caused the checker to warn that all cases were not matched even though matching on `P` is sufficient to make `foo` covering. With the addition of the pragma, the compiler will recognise that matching on `P` alone is enough and not emit any warnings. Reviewers: goldfire, gkaracha, alanz, austin, bgamari Reviewed By: alanz Subscribers: lelf, nomeata, gkaracha, thomie Differential Revision: https://phabricator.haskell.org/D2669 GHC Trac Issues: #8779
-rw-r--r--compiler/basicTypes/ConLike.hs5
-rw-r--r--compiler/deSugar/Check.hs276
-rw-r--r--compiler/deSugar/Desugar.hs14
-rw-r--r--compiler/deSugar/DsMeta.hs1
-rw-r--r--compiler/deSugar/DsMonad.hs26
-rw-r--r--compiler/deSugar/PmExpr.hs57
-rw-r--r--compiler/deSugar/TmOracle.hs13
-rw-r--r--compiler/hsSyn/HsBinds.hs13
-rw-r--r--compiler/iface/IfaceSyn.hs10
-rw-r--r--compiler/iface/MkIface.hs20
-rw-r--r--compiler/iface/TcIface.hs31
-rw-r--r--compiler/main/HscTypes.hs42
-rw-r--r--compiler/main/TidyPgm.hs5
-rw-r--r--compiler/parser/Lexer.x7
-rw-r--r--compiler/parser/Parser.y13
-rw-r--r--compiler/parser/RdrHsSyn.hs2
-rw-r--r--compiler/rename/RnBinds.hs10
-rw-r--r--compiler/typecheck/TcBinds.hs107
-rw-r--r--compiler/typecheck/TcRnDriver.hs2
-rw-r--r--compiler/typecheck/TcRnMonad.hs3
-rw-r--r--compiler/typecheck/TcRnTypes.hs24
-rw-r--r--compiler/vectorise/Vectorise/Monad.hs2
-rw-r--r--docs/users_guide/glasgow_exts.rst78
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/Completesig03.hs7
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/Completesig03.stderr2
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/Completesig03A.hs5
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/Makefile3
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/all.T15
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig01.hs20
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig02.hs10
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig02.stderr4
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig04.hs3
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig04.stderr4
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig05.hs14
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig06.hs29
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig06.stderr29
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig07.hs24
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig07.stderr11
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig08.hs30
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig09.hs13
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig10.hs33
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig10.stderr4
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig11.hs14
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig11.stderr4
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig12.hs22
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig13.hs19
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig14.hs11
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig14.stderr4
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig15.hs12
-rw-r--r--testsuite/tests/pmcheck/complete_sigs/completesig15.stderr4
-rw-r--r--testsuite/tests/pmcheck/should_compile/all.T2
51 files changed, 959 insertions, 154 deletions
diff --git a/compiler/basicTypes/ConLike.hs b/compiler/basicTypes/ConLike.hs
index b8bab31297..aa6a362f68 100644
--- a/compiler/basicTypes/ConLike.hs
+++ b/compiler/basicTypes/ConLike.hs
@@ -21,6 +21,7 @@ module ConLike (
, conLikeResTy
, conLikeFieldType
, conLikesWithFields
+ , conLikeIsInfix
) where
#include "HsVersions.h"
@@ -185,3 +186,7 @@ conLikesWithFields :: [ConLike] -> [FieldLabelString] -> [ConLike]
conLikesWithFields con_likes lbls = filter has_flds con_likes
where has_flds dc = all (has_fld dc) lbls
has_fld dc lbl = any (\ fl -> flLabel fl == lbl) (conLikeFieldLabels dc)
+
+conLikeIsInfix :: ConLike -> Bool
+conLikeIsInfix (RealDataCon dc) = dataConIsInfix dc
+conLikeIsInfix (PatSynCon ps) = patSynIsInfix ps
diff --git a/compiler/deSugar/Check.hs b/compiler/deSugar/Check.hs
index 04ba5681b0..80f7fa50e3 100644
--- a/compiler/deSugar/Check.hs
+++ b/compiler/deSugar/Check.hs
@@ -5,6 +5,7 @@ Pattern Matching Coverage Checking.
-}
{-# LANGUAGE CPP, GADTs, DataKinds, KindSignatures #-}
+{-# LANGUAGE TupleSections #-}
module Check (
-- Checking and printing
@@ -23,7 +24,6 @@ import HsSyn
import TcHsSyn
import Id
import ConLike
-import DataCon
import Name
import FamInstEnv
import TysWiredIn
@@ -32,6 +32,8 @@ import SrcLoc
import Util
import Outputable
import FastString
+import DataCon
+import HscTypes (CompleteMatch(..))
import DsMonad
import TcSimplify (tcCheckSatisfiability)
@@ -49,8 +51,9 @@ import Control.Monad (forM, when, forM_)
import Coercion
import TcEvidence
import IOEnv
+import Data.Monoid ( Monoid(mappend) )
-import ListT (ListT(..), fold)
+import ListT (ListT(..), fold, select)
{-
This module checks pattern matches for:
@@ -87,12 +90,39 @@ type PmM a = ListT DsM a
liftD :: DsM a -> PmM a
liftD m = ListT $ \sk fk -> m >>= \a -> sk a fk
-
-myRunListT :: PmM a -> DsM [a]
-myRunListT pm = fold pm go (return [])
+-- Pick the first match complete covered match or otherwise the "best" match.
+-- The best match is the one with the least uncovered clauses, ties broken
+-- by the number of inaccessible clauses followed by number of redudant
+-- clauses
+getResult :: PmM PmResult -> DsM PmResult
+getResult ls = do
+ res <- fold ls goM (pure Nothing)
+ case res of
+ Nothing -> panic "getResult is empty"
+ Just a -> return a
where
- go a mas =
- mas >>= \as -> return (a:as)
+ goM :: PmResult -> DsM (Maybe PmResult) -> DsM (Maybe PmResult)
+ goM mpm dpm = do
+ pmr <- dpm
+ return $ go pmr mpm
+ -- Careful not to force unecessary results
+ go :: Maybe PmResult -> PmResult -> Maybe PmResult
+ go Nothing rs = Just rs
+ go old@(Just (PmResult prov rs us is)) new
+ | null us && null rs && null is = old
+ | otherwise =
+ let PmResult prov' rs' us' is' = new
+ lr = length rs
+ lr' = length rs'
+ li = length is
+ li' = length is'
+ in case compare (length us) (length us')
+ `mappend` (compare li li')
+ `mappend` (compare lr lr')
+ `mappend` (compare prov prov') of
+ GT -> Just new
+ EQ -> Just new
+ LT -> old
data PatTy = PAT | VA -- Used only as a kind, to index PmPat
@@ -100,7 +130,7 @@ data PatTy = PAT | VA -- Used only as a kind, to index PmPat
-- the number of p1..pn that are not Guards
data PmPat :: PatTy -> * where
- PmCon :: { pm_con_con :: DataCon
+ PmCon :: { pm_con_con :: ConLike
, pm_con_arg_tys :: [Type]
, pm_con_tvs :: [TyVar]
, pm_con_dicts :: [EvVar]
@@ -171,20 +201,42 @@ instance Monoid Diverged where
_ `mappend` Diverged = Diverged
NotDiverged `mappend` NotDiverged = NotDiverged
+-- | When we learned that a given match group is complete
+data Provenance =
+ FromBuiltin -- ^ From the original definition of the type
+ -- constructor.
+ | FromComplete -- ^ From a user-provided @COMPLETE@ pragma
+ deriving (Show, Eq, Ord)
+
+instance Outputable Provenance where
+ ppr = text . show
+
+instance Monoid Provenance where
+ mempty = FromBuiltin
+ FromComplete `mappend` _ = FromComplete
+ _ `mappend` FromComplete = FromComplete
+ _ `mappend` _ = FromBuiltin
+
data PartialResult = PartialResult {
- presultCovered :: Covered
+ presultProvenence :: Provenance
+ -- keep track of provenance because we don't want
+ -- to warn about redundant matches if the result
+ -- is contaiminated with a COMPLETE pragma
+ , presultCovered :: Covered
, presultUncovered :: Uncovered
, presultDivergent :: Diverged }
instance Outputable PartialResult where
- ppr (PartialResult c vsa d) = text "PartialResult" <+> ppr c
+ ppr (PartialResult prov c vsa d)
+ = text "PartialResult" <+> ppr prov <+> ppr c
<+> ppr d <+> ppr vsa
instance Monoid PartialResult where
- mempty = PartialResult mempty [] mempty
- (PartialResult cs1 vsa1 ds1)
- `mappend` (PartialResult cs2 vsa2 ds2)
- = PartialResult (cs1 `mappend` cs2)
+ mempty = PartialResult mempty mempty [] mempty
+ (PartialResult prov1 cs1 vsa1 ds1)
+ `mappend` (PartialResult prov2 cs2 vsa2 ds2)
+ = PartialResult (prov1 `mappend` prov2)
+ (cs1 `mappend` cs2)
(vsa1 `mappend` vsa2)
(ds1 `mappend` ds2)
@@ -197,7 +249,8 @@ instance Monoid PartialResult where
-- * Clauses with inaccessible RHS
data PmResult =
PmResult {
- pmresultRedundant :: [Located [LPat Id]]
+ pmresultProvenance :: Provenance
+ , pmresultRedundant :: [Located [LPat Id]]
, pmresultUncovered :: Uncovered
, pmresultInaccessible :: [Located [LPat Id]] }
@@ -213,7 +266,7 @@ data PmResult =
checkSingle :: DynFlags -> DsMatchContext -> Id -> Pat Id -> DsM ()
checkSingle dflags ctxt@(DsMatchContext _ locn) var p = do
tracePmD "checkSingle" (vcat [ppr ctxt, ppr var, ppr p])
- mb_pm_res <- tryM (head <$> myRunListT (checkSingle' locn var p))
+ mb_pm_res <- tryM (getResult (checkSingle' locn var p))
case mb_pm_res of
Left _ -> warnPmIters dflags ctxt
Right res -> dsPmWarn dflags ctxt res
@@ -226,11 +279,12 @@ checkSingle' locn var p = do
clause <- liftD $ translatePat fam_insts p
missing <- mkInitialUncovered [var]
tracePm "checkSingle: missing" (vcat (map pprValVecDebug missing))
- PartialResult cs us ds <- runMany (pmcheckI clause []) missing -- no guards
+ -- no guards
+ PartialResult prov cs us ds <- runMany (pmcheckI clause []) missing
return $ case (cs,ds) of
- (Covered, _ ) -> PmResult [] us [] -- useful
- (NotCovered, NotDiverged) -> PmResult m us [] -- redundant
- (NotCovered, Diverged ) -> PmResult [] us m -- inaccessible rhs
+ (Covered, _ ) -> PmResult prov [] us [] -- useful
+ (NotCovered, NotDiverged) -> PmResult prov m us [] -- redundant
+ (NotCovered, Diverged ) -> PmResult prov [] us m -- inaccessible rhs
where m = [L locn [L locn p]]
-- | Check a matchgroup (case, functions, etc.)
@@ -242,7 +296,7 @@ checkMatches dflags ctxt vars matches = do
, text "Matches:"])
2
(vcat (map ppr matches)))
- mb_pm_res <- tryM (head <$> myRunListT (checkMatches' vars matches))
+ mb_pm_res <- tryM (getResult (checkMatches' vars matches))
case mb_pm_res of
Left _ -> warnPmIters dflags ctxt
Right res -> dsPmWarn dflags ctxt res
@@ -250,29 +304,37 @@ checkMatches dflags ctxt vars matches = do
-- | Check a matchgroup (case, functions, etc.)
checkMatches' :: [Id] -> [LMatch Id (LHsExpr Id)] -> PmM PmResult
checkMatches' vars matches
- | null matches = return $ PmResult [] [] []
+ | null matches = return $ PmResult FromBuiltin [] [] []
| otherwise = do
liftD resetPmIterDs -- set the iter-no to zero
missing <- mkInitialUncovered vars
tracePm "checkMatches: missing" (vcat (map pprValVecDebug missing))
- (rs,us,ds) <- go matches missing
- return $ PmResult (map hsLMatchToLPats rs) us (map hsLMatchToLPats ds)
+ (prov, rs,us,ds) <- go matches missing
+ return
+ $ PmResult prov (map hsLMatchToLPats rs) us (map hsLMatchToLPats ds)
where
go :: [LMatch Id (LHsExpr Id)] -> Uncovered
- -> PmM ([LMatch Id (LHsExpr Id)] , Uncovered , [LMatch Id (LHsExpr Id)])
- go [] missing = return ([], missing, [])
+ -> PmM (Provenance
+ , [LMatch Id (LHsExpr Id)]
+ , Uncovered
+ , [LMatch Id (LHsExpr Id)])
+ go [] missing = return (mempty, [], missing, [])
go (m:ms) missing = do
tracePm "checMatches': go" (ppr m $$ ppr missing)
fam_insts <- liftD dsGetFamInstEnvs
(clause, guards) <- liftD $ translateMatch fam_insts m
- r@(PartialResult cs missing' ds)
+ r@(PartialResult prov cs missing' ds)
<- runMany (pmcheckI clause guards) missing
tracePm "checMatches': go: res" (ppr r)
- (rs, final_u, is) <- go ms missing'
+ (ms_prov, rs, final_u, is) <- go ms missing'
+ let final_prov = prov `mappend` ms_prov
return $ case (cs, ds) of
- (Covered, _ ) -> ( rs, final_u, is) -- useful
- (NotCovered, NotDiverged) -> (m:rs, final_u, is) -- redundant
- (NotCovered, Diverged ) -> ( rs, final_u, m:is) -- inaccessible
+ -- useful
+ (Covered, _ ) -> (final_prov, rs, final_u, is)
+ -- redundant
+ (NotCovered, NotDiverged) -> (final_prov, m:rs, final_u,is)
+ -- inaccessible
+ (NotCovered, Diverged ) -> (final_prov, rs, final_u, m:is)
hsLMatchToLPats :: LMatch id body -> Located [LPat id]
hsLMatchToLPats (L l (Match _ pats _ _)) = L l pats
@@ -288,7 +350,7 @@ checkMatches' vars matches
-- -----------------------------------------------------------------------
-- * Utilities
-nullaryConPattern :: DataCon -> Pattern
+nullaryConPattern :: ConLike -> Pattern
-- Nullary data constructor and nullary type constructor
nullaryConPattern con =
PmCon { pm_con_con = con, pm_con_arg_tys = []
@@ -296,7 +358,7 @@ nullaryConPattern con =
{-# INLINE nullaryConPattern #-}
truePattern :: Pattern
-truePattern = nullaryConPattern trueDataCon
+truePattern = nullaryConPattern (RealDataCon trueDataCon)
{-# INLINE truePattern #-}
-- | A fake guard pattern (True <- _) used to represent cases we cannot handle
@@ -307,7 +369,7 @@ fake_pat = PmGrd { pm_grd_pv = [truePattern]
-- | Check whether a guard pattern is generated by the checker (unhandled)
isFakeGuard :: [Pattern] -> PmExpr -> Bool
-isFakeGuard [PmCon { pm_con_con = c }] (PmExprOther EWildPat)
+isFakeGuard [PmCon { pm_con_con = RealDataCon c }] (PmExprOther EWildPat)
| c == trueDataCon = True
| otherwise = False
isFakeGuard _pats _e = False
@@ -318,7 +380,7 @@ mkCanFailPmPat ty = do
var <- mkPmVar ty
return [var, fake_pat]
-vanillaConPattern :: DataCon -> [Type] -> PatVec -> Pattern
+vanillaConPattern :: ConLike -> [Type] -> PatVec -> Pattern
-- ADT constructor pattern => no existentials, no local constraints
vanillaConPattern con arg_tys args =
PmCon { pm_con_con = con, pm_con_arg_tys = arg_tys
@@ -328,13 +390,13 @@ vanillaConPattern con arg_tys args =
-- | Create an empty list pattern of a given type
nilPattern :: Type -> Pattern
nilPattern ty =
- PmCon { pm_con_con = nilDataCon, pm_con_arg_tys = [ty]
+ PmCon { pm_con_con = RealDataCon nilDataCon, pm_con_arg_tys = [ty]
, pm_con_tvs = [], pm_con_dicts = []
, pm_con_args = [] }
{-# INLINE nilPattern #-}
mkListPatVec :: Type -> PatVec -> PatVec -> PatVec
-mkListPatVec ty xs ys = [PmCon { pm_con_con = consDataCon
+mkListPatVec ty xs ys = [PmCon { pm_con_con = RealDataCon consDataCon
, pm_con_arg_tys = [ty]
, pm_con_tvs = [], pm_con_dicts = []
, pm_con_args = xs++ys }]
@@ -410,26 +472,21 @@ translatePat fam_insts pat = case pat of
-- See Note [Guards and Approximation]
| otherwise -> mkCanFailPmPat pat_ty
- ConPatOut { pat_con = L _ (PatSynCon _) } ->
- -- Pattern synonyms have a "matcher"
- -- (see Note [Pattern synonym representation] in PatSyn.hs
- -- We should be able to transform (P x y)
- -- to v (Just (x, y) <- matchP v (\x y -> Just (x,y)) Nothing
- -- That is, a combination of a variable pattern and a guard
- -- But there are complications with GADTs etc, and this isn't done yet
- mkCanFailPmPat (hsPatType pat)
-
- ConPatOut { pat_con = L _ (RealDataCon con)
+ ConPatOut { pat_con = L _ con
, pat_arg_tys = arg_tys
, pat_tvs = ex_tvs
, pat_dicts = dicts
, pat_args = ps } -> do
- args <- translateConPatVec fam_insts arg_tys ex_tvs con ps
- return [PmCon { pm_con_con = con
- , pm_con_arg_tys = arg_tys
- , pm_con_tvs = ex_tvs
- , pm_con_dicts = dicts
- , pm_con_args = args }]
+ groups <- allCompleteMatches con arg_tys
+ case groups of
+ [] -> mkCanFailPmPat (conLikeResTy con arg_tys)
+ _ -> do
+ args <- translateConPatVec fam_insts arg_tys ex_tvs con ps
+ return [PmCon { pm_con_con = con
+ , pm_con_arg_tys = arg_tys
+ , pm_con_tvs = ex_tvs
+ , pm_con_dicts = dicts
+ , pm_con_args = args }]
NPat (L _ ol) mb_neg _eq ty -> translateNPat fam_insts ol mb_neg ty
@@ -442,17 +499,17 @@ translatePat fam_insts pat = case pat of
PArrPat ps ty -> do
tidy_ps <- translatePatVec fam_insts (map unLoc ps)
- let fake_con = parrFakeCon (length ps)
+ let fake_con = RealDataCon (parrFakeCon (length ps))
return [vanillaConPattern fake_con [ty] (concat tidy_ps)]
TuplePat ps boxity tys -> do
tidy_ps <- translatePatVec fam_insts (map unLoc ps)
- let tuple_con = tupleDataCon boxity (length ps)
+ let tuple_con = RealDataCon (tupleDataCon boxity (length ps))
return [vanillaConPattern tuple_con tys (concat tidy_ps)]
SumPat p alt arity ty -> do
tidy_p <- translatePat fam_insts (unLoc p)
- let sum_con = sumDataCon alt arity
+ let sum_con = RealDataCon (sumDataCon alt arity)
return [vanillaConPattern sum_con ty tidy_p]
-- --------------------------------------------------------------------------
@@ -486,7 +543,7 @@ translatePatVec fam_insts pats = mapM (translatePat fam_insts) pats
-- | Translate a constructor pattern
translateConPatVec :: FamInstEnvs -> [Type] -> [TyVar]
- -> DataCon -> HsConPatDetails Id -> DsM PatVec
+ -> ConLike -> HsConPatDetails Id -> DsM PatVec
translateConPatVec fam_insts _univ_tys _ex_tvs _ (PrefixCon ps)
= concat <$> translatePatVec fam_insts (map unLoc ps)
translateConPatVec fam_insts _univ_tys _ex_tvs _ (InfixCon p1 p2)
@@ -525,10 +582,10 @@ translateConPatVec fam_insts univ_tys ex_tvs c (RecCon (HsRecFields fs _))
return (arg_var_pats ++ guards)
where
-- The actual argument types (instantiated)
- arg_tys = dataConInstOrigArgTys c (univ_tys ++ mkTyVarTys ex_tvs)
+ arg_tys = conLikeInstOrigArgTys c (univ_tys ++ mkTyVarTys ex_tvs)
-- Some label information
- orig_lbls = map flSelector $ dataConFieldLabels c
+ orig_lbls = map flSelector $ conLikeFieldLabels c
matched_pats = [ (getName (unLoc (hsRecFieldId x)), unLoc (hsRecFieldArg x))
| L _ x <- fs]
matched_lbls = [ name | (name, _pat) <- matched_pats ]
@@ -579,7 +636,7 @@ translateGuards fam_insts guards = do
shouldKeep :: Pattern -> Bool
shouldKeep p
| PmVar {} <- p = True
- | PmCon {} <- p = length (allConstructors (pm_con_con p)) == 1
+ | PmCon {} <- p = singleConstructor (pm_con_con p)
&& all shouldKeep (pm_con_args p)
shouldKeep (PmGrd pv e)
| all shouldKeep pv = True
@@ -590,7 +647,7 @@ translateGuards fam_insts guards = do
cantFailPattern :: Pattern -> Bool
cantFailPattern p
| PmVar {} <- p = True
- | PmCon {} <- p = length (allConstructors (pm_con_con p)) == 1
+ | PmCon {} <- p = singleConstructor (pm_con_con p)
&& all cantFailPattern (pm_con_args p)
cantFailPattern (PmGrd pv _e)
= all cantFailPattern pv
@@ -739,7 +796,7 @@ families is not really efficient.
-- of the first (or the single -WHEREVER IT IS- valid to use?) pattern
pmPatType :: PmPat p -> Type
pmPatType (PmCon { pm_con_con = con, pm_con_arg_tys = tys })
- = mkTyConApp (dataConTyCon con) tys
+ = conLikeResTy con tys
pmPatType (PmVar { pm_var_id = x }) = idType x
pmPatType (PmLit { pm_lit_lit = l }) = pmLitType l
pmPatType (PmNLit { pm_lit_id = x }) = idType x
@@ -749,7 +806,7 @@ pmPatType (PmGrd { pm_grd_pv = pv })
-- | Generate a value abstraction for a given constructor (generate
-- fresh variables of the appropriate type for arguments)
-mkOneConFull :: Id -> DataCon -> DsM (ValAbs, ComplexEq, Bag EvVar)
+mkOneConFull :: Id -> ConLike -> DsM (ValAbs, ComplexEq, Bag EvVar)
-- * x :: T tys, where T is an algebraic data type
-- NB: in the case of a data familiy, T is the *representation* TyCon
-- e.g. data instance T (a,b) = T1 a b
@@ -766,12 +823,12 @@ mkOneConFull :: Id -> DataCon -> DsM (ValAbs, ComplexEq, Bag EvVar)
-- ComplexEq: x ~ K y1..yn
-- [EvVar]: Q
mkOneConFull x con = do
- let -- res_ty == TyConApp (dataConTyCon cabs_con) cabs_arg_tys
+ let -- res_ty == TyConApp (ConLikeTyCon cabs_con) cabs_arg_tys
res_ty = idType x
- (univ_tvs, ex_tvs, eq_spec, thetas, arg_tys, _) = dataConFullSig con
- data_tc = dataConTyCon con -- The representation TyCon
+ (univ_tvs, ex_tvs, eq_spec, thetas, _req_theta , arg_tys, _)
+ = conLikeFullSig con
tc_args = case splitTyConApp_maybe res_ty of
- Just (tc, tys) -> ASSERT( tc == data_tc ) tys
+ Just (_, tys) -> tys
Nothing -> pprPanic "mkOneConFull: Not TyConApp:" (ppr res_ty)
subst1 = zipTvSubst univ_tvs tc_args
@@ -866,9 +923,38 @@ coercePmPat (PmCon { pm_con_con = con, pm_con_arg_tys = arg_tys
, pm_con_args = coercePatVec args }]
coercePmPat (PmGrd {}) = [] -- drop the guards
--- | Get all constructors in the family (including given)
-allConstructors :: DataCon -> [DataCon]
-allConstructors = tyConDataCons . dataConTyCon
+-- | Check whether a data constructor is the only way to construct
+-- a data type.
+singleConstructor :: ConLike -> Bool
+singleConstructor (RealDataCon dc) =
+ case tyConDataCons (dataConTyCon dc) of
+ [_] -> True
+ _ -> False
+singleConstructor _ = False
+
+-- | For a given conlike, finds all the sets of patterns which could
+-- be relevant to that conlike by consulting the result type.
+--
+-- These come from two places.
+-- 1. From data constructors defined with the result type constructor.
+-- 2. From `COMPLETE` pragmas which have the same type as the result
+-- type constructor.
+allCompleteMatches :: ConLike -> [Type] -> DsM [(Provenance, [ConLike])]
+allCompleteMatches cl tys = do
+ let fam = case cl of
+ RealDataCon dc ->
+ [(FromBuiltin, map RealDataCon (tyConDataCons (dataConTyCon dc)))]
+ PatSynCon _ -> []
+
+
+ from_pragma <- map ((FromComplete,) . completeMatch) <$>
+ case splitTyConApp_maybe (conLikeResTy cl tys) of
+ Just (tc, _) -> dsGetCompleteMatches tc
+ Nothing -> return []
+
+ let final_groups = fam ++ from_pragma
+ tracePmD "allCompleteMatches" (ppr final_groups)
+ return final_groups
-- -----------------------------------------------------------------------
-- * Types and constraints
@@ -962,11 +1048,8 @@ Main functions are:
-- value set abstraction, but calling it on every vector and the combining the
-- results.
runMany :: (ValVec -> PmM PartialResult) -> (Uncovered -> PmM PartialResult)
-runMany _ [] = return $ PartialResult mempty mempty mempty
-runMany pm (m:ms) = do
- (PartialResult c v d) <- pm m
- (PartialResult cs vs ds) <- runMany pm ms
- return (PartialResult (c `mappend` cs) (v `mappend` vs) (d `mappend` ds))
+runMany _ [] = return mempty
+runMany pm (m:ms) = mappend <$> pm m <*> runMany pm ms
{-# INLINE runMany #-}
-- | Generate the initial uncovered set. It initializes the
@@ -1005,7 +1088,8 @@ pmcheckGuardsI gvs vva = liftD incrCheckPmIterDs >> pmcheckGuards gvs vva
-- | Increase the counter for elapsed algorithm iterations, check that the
-- limit is not exceeded and call `pmcheckHd`
-pmcheckHdI :: Pattern -> PatVec -> [PatVec] -> ValAbs -> ValVec -> PmM PartialResult
+pmcheckHdI :: Pattern -> PatVec -> [PatVec] -> ValAbs -> ValVec
+ -> PmM PartialResult
pmcheckHdI p ps guards va vva = do
n <- liftD incrCheckPmIterDs
tracePm "pmCheckHdI" (ppr n <> colon <+> pprPmPatDebug p
@@ -1050,14 +1134,18 @@ pmcheck (p:ps) guards (ValVec (va:vva) delta)
pmcheckGuards :: [PatVec] -> ValVec -> PmM PartialResult
pmcheckGuards [] vva = return (usimple [vva])
pmcheckGuards (gv:gvs) vva = do
- (PartialResult cs vsa ds) <- pmcheckI gv [] vva
- (PartialResult css vsas dss) <- runMany (pmcheckGuardsI gvs) vsa
- return $ PartialResult (cs `mappend` css) vsas (ds `mappend` dss)
+ (PartialResult prov1 cs vsa ds) <- pmcheckI gv [] vva
+ (PartialResult prov2 css vsas dss) <- runMany (pmcheckGuardsI gvs) vsa
+ return $ PartialResult (prov1 `mappend` prov2)
+ (cs `mappend` css)
+ vsas
+ (ds `mappend` dss)
-- | Worker function: Implements all cases described in the paper for all three
-- functions (`covered`, `uncovered` and `divergent`) apart from the `Guard`
-- cases which are handled by `pmcheck`
-pmcheckHd :: Pattern -> PatVec -> [PatVec] -> ValAbs -> ValVec -> PmM PartialResult
+pmcheckHd :: Pattern -> PatVec -> [PatVec] -> ValAbs -> ValVec
+ -> PmM PartialResult
-- Var
pmcheckHd (PmVar x) ps guards va (ValVec vva delta)
@@ -1081,9 +1169,12 @@ pmcheckHd (PmLit l1) ps guards (va@(PmLit l2)) vva =
False -> return $ ucon va (usimple [vva])
-- ConVar
-pmcheckHd (p@(PmCon { pm_con_con = con })) ps guards
+pmcheckHd (p@(PmCon { pm_con_con = con, pm_con_arg_tys = tys }))
+ ps guards
(PmVar x) (ValVec vva delta) = do
- cons_cs <- mapM (liftD . mkOneConFull x) (allConstructors con)
+ (prov, complete_match) <- select =<< liftD (allCompleteMatches con tys)
+
+ cons_cs <- mapM (liftD . mkOneConFull x) complete_match
inst_vsa <- flip concatMapM cons_cs $ \(va, tm_ct, ty_cs) -> do
let ty_state = ty_cs `unionBags` delta_ty_cs delta -- not actually a state
@@ -1093,8 +1184,9 @@ pmcheckHd (p@(PmCon { pm_con_con = con })) ps guards
(True, Just tm_state) -> [ValVec (va:vva) (MkDelta ty_state tm_state)]
_ty_or_tm_failed -> []
- force_if (canDiverge (idName x) (delta_tm_cs delta)) <$>
- runMany (pmcheckI (p:ps) guards) inst_vsa
+ set_provenance prov .
+ force_if (canDiverge (idName x) (delta_tm_cs delta)) <$>
+ runMany (pmcheckI (p:ps) guards) inst_vsa
-- LitVar
pmcheckHd (p@(PmLit l)) ps guards (PmVar x) (ValVec vva delta)
@@ -1187,10 +1279,10 @@ ucon va = updateVsa upd
-- value vector abstractions of length `(a+n)`, pass the first `n` value
-- abstractions to the constructor (Hence, the resulting value vector
-- abstractions will have length `n+1`)
-kcon :: DataCon -> [Type] -> [TyVar] -> [EvVar]
+kcon :: ConLike -> [Type] -> [TyVar] -> [EvVar]
-> PartialResult -> PartialResult
kcon con arg_tys ex_tvs dicts
- = let n = dataConSourceArity con
+ = let n = conLikeArity con
upd vsa =
[ ValVec (va:vva) delta
| ValVec vva' delta <- vsa
@@ -1223,6 +1315,9 @@ force_if :: Bool -> PartialResult -> PartialResult
force_if True pres = forces pres
force_if False pres = pres
+set_provenance :: Provenance -> PartialResult -> PartialResult
+set_provenance prov pr = pr { presultProvenence = prov }
+
-- ----------------------------------------------------------------------------
-- * Propagation of term constraints inwards when checking nested matches
@@ -1360,8 +1455,8 @@ wrapUpTmState (residual, (_, subst)) = (residual, flattenPmVarEnv subst)
dsPmWarn :: DynFlags -> DsMatchContext -> PmResult -> DsM ()
dsPmWarn dflags ctx@(DsMatchContext kind loc) pm_result
= when (flag_i || flag_u) $ do
- let exists_r = flag_i && notNull redundant
- exists_i = flag_i && notNull inaccessible
+ let exists_r = flag_i && notNull redundant && onlyBuiltin
+ exists_i = flag_i && notNull inaccessible && onlyBuiltin
exists_u = flag_u && notNull uncovered
when exists_r $ forM_ redundant $ \(L l q) -> do
putSrcSpanDs l (warnDs (Reason Opt_WarnOverlappingPatterns)
@@ -1373,7 +1468,8 @@ dsPmWarn dflags ctx@(DsMatchContext kind loc) pm_result
putSrcSpanDs loc (warnDs flag_u_reason (pprEqns uncovered))
where
PmResult
- { pmresultRedundant = redundant
+ { pmresultProvenance = prov
+ , pmresultRedundant = redundant
, pmresultUncovered = uncovered
, pmresultInaccessible = inaccessible } = pm_result
@@ -1381,6 +1477,8 @@ dsPmWarn dflags ctx@(DsMatchContext kind loc) pm_result
flag_u = exhaustive dflags kind
flag_u_reason = maybe NoReason Reason (exhaustiveWarningFlag kind)
+ onlyBuiltin = prov == FromBuiltin
+
maxPatterns = maxUncoveredPatterns dflags
-- Print a single clause (for redundant/with-inaccessible-rhs)
diff --git a/compiler/deSugar/Desugar.hs b/compiler/deSugar/Desugar.hs
index 1e117b3c00..1cd7979128 100644
--- a/compiler/deSugar/Desugar.hs
+++ b/compiler/deSugar/Desugar.hs
@@ -296,7 +296,9 @@ deSugar hsc_env
tcg_tcs = tcs,
tcg_insts = insts,
tcg_fam_insts = fam_insts,
- tcg_hpc = other_hpc_info})
+ tcg_hpc = other_hpc_info,
+ tcg_complete_matches = complete_matches
+ })
= do { let dflags = hsc_dflags hsc_env
print_unqual = mkPrintUnqualified dflags rdr_env
@@ -313,8 +315,9 @@ deSugar hsc_env
then addTicksToBinds hsc_env mod mod_loc
export_set (typeEnvTyCons type_env) binds
else return (binds, hpcInfo, Nothing)
-
- ; (msgs, mb_res) <- initDs hsc_env mod rdr_env type_env fam_inst_env $
+ ; (msgs, mb_res)
+ <- initDs hsc_env mod rdr_env type_env
+ fam_inst_env complete_matches $
do { ds_ev_binds <- dsEvBinds ev_binds
; core_prs <- dsTopLHsBinds binds_cvr
; (spec_prs, spec_rules) <- dsImpSpecs imp_specs
@@ -396,7 +399,8 @@ deSugar hsc_env
mg_vect_decls = ds_vects,
mg_vect_info = noVectInfo,
mg_safe_haskell = safe_mode,
- mg_trust_pkg = imp_trust_own_pkg imports
+ mg_trust_pkg = imp_trust_own_pkg imports,
+ mg_complete_sigs = complete_matches
}
; return (msgs, Just mod_guts)
}}}}
@@ -451,7 +455,7 @@ deSugarExpr hsc_env tc_expr
-- Do desugaring
; (msgs, mb_core_expr) <- initDs hsc_env (icInteractiveModule icntxt) rdr_env
- type_env fam_inst_env $
+ type_env fam_inst_env [] $
dsLExpr tc_expr
; case mb_core_expr of
diff --git a/compiler/deSugar/DsMeta.hs b/compiler/deSugar/DsMeta.hs
index 27106a2c8b..049c226a0b 100644
--- a/compiler/deSugar/DsMeta.hs
+++ b/compiler/deSugar/DsMeta.hs
@@ -737,6 +737,7 @@ rep_sig (L loc (SpecSig nm tys ispec))
rep_sig (L loc (SpecInstSig _ ty)) = rep_specialiseInst ty loc
rep_sig (L _ (MinimalSig {})) = notHandled "MINIMAL pragmas" empty
rep_sig (L _ (SCCFunSig {})) = notHandled "SCC pragmas" empty
+rep_sig (L _ (CompleteMatchSig {})) = notHandled "CompleteMatchSig" empty
rep_ty_sig :: Name -> SrcSpan -> LHsSigType Name -> Located Name
-> DsM (SrcSpan, Core TH.DecQ)
diff --git a/compiler/deSugar/DsMonad.hs b/compiler/deSugar/DsMonad.hs
index 24cca5d8b2..f9533e391a 100644
--- a/compiler/deSugar/DsMonad.hs
+++ b/compiler/deSugar/DsMonad.hs
@@ -35,7 +35,7 @@ module DsMonad (
getDictsDs, addDictsDs, getTmCsDs, addTmCsDs,
-- Iterations for pm checking
- incrCheckPmIterDs, resetPmIterDs,
+ incrCheckPmIterDs, resetPmIterDs, dsGetCompleteMatches,
-- Warnings and errors
DsWarning, warnDs, warnIfSetDs, errDs, errDsCoreExpr,
@@ -83,6 +83,7 @@ import FastString
import Maybes
import Var (EvVar)
import qualified GHC.LanguageExtensions as LangExt
+import UniqFM ( lookupWithDefaultUFM )
import Data.IORef
import Control.Monad
@@ -152,17 +153,19 @@ type DsWarning = (SrcSpan, SDoc)
initDs :: HscEnv
-> Module -> GlobalRdrEnv -> TypeEnv -> FamInstEnv
+ -> [CompleteMatch]
-> DsM a
-> IO (Messages, Maybe a)
-- Print errors and warnings, if any arise
-initDs hsc_env mod rdr_env type_env fam_inst_env thing_inside
+initDs hsc_env mod rdr_env type_env fam_inst_env complete_matches thing_inside
= do { msg_var <- newIORef (emptyBag, emptyBag)
+ ; let all_matches = (hptCompleteSigs hsc_env) ++ complete_matches
; pm_iter_var <- newIORef 0
; let dflags = hsc_dflags hsc_env
(ds_gbl_env, ds_lcl_env) = mkDsEnvs dflags mod rdr_env type_env
fam_inst_env msg_var
- pm_iter_var
+ pm_iter_var all_matches
; either_res <- initTcRnIf 'd' hsc_env ds_gbl_env ds_lcl_env $
loadDAP $
@@ -241,8 +244,9 @@ initDsTc thing_inside
; let type_env = tcg_type_env tcg_env
rdr_env = tcg_rdr_env tcg_env
fam_inst_env = tcg_fam_inst_env tcg_env
+ complete_matches = tcg_complete_matches tcg_env
ds_envs = mkDsEnvs dflags this_mod rdr_env type_env fam_inst_env
- msg_var pm_iter_var
+ msg_var pm_iter_var complete_matches
; setEnvs ds_envs thing_inside
}
@@ -270,13 +274,15 @@ initTcDsForSolver thing_inside
thing_inside }
mkDsEnvs :: DynFlags -> Module -> GlobalRdrEnv -> TypeEnv -> FamInstEnv
- -> IORef Messages -> IORef Int -> (DsGblEnv, DsLclEnv)
-mkDsEnvs dflags mod rdr_env type_env fam_inst_env msg_var pmvar
+ -> IORef Messages -> IORef Int -> [CompleteMatch]
+ -> (DsGblEnv, DsLclEnv)
+mkDsEnvs dflags mod rdr_env type_env fam_inst_env msg_var pmvar complete_matches
= let if_genv = IfGblEnv { if_doc = text "mkDsEnvs",
if_rec_types = Just (mod, return type_env) }
if_lenv = mkIfLclEnv mod (text "GHC error in desugarer lookup in" <+> ppr mod)
False -- not boot!
real_span = realSrcLocSpan (mkRealSrcLoc (moduleNameFS (moduleName mod)) 1 1)
+ completeMatchMap = mkCompleteMatchMap complete_matches
gbl_env = DsGblEnv { ds_mod = mod
, ds_fam_inst_env = fam_inst_env
, ds_if_env = (if_genv, if_lenv)
@@ -284,6 +290,7 @@ mkDsEnvs dflags mod rdr_env type_env fam_inst_env msg_var pmvar
, ds_msgs = msg_var
, ds_dph_env = emptyGlobalRdrEnv
, ds_parr_bi = panic "DsMonad: uninitialised ds_parr_bi"
+ , ds_complete_matches = completeMatchMap
}
lcl_env = DsLclEnv { dsl_meta = emptyNameEnv
, dsl_loc = real_span
@@ -293,6 +300,7 @@ mkDsEnvs dflags mod rdr_env type_env fam_inst_env msg_var pmvar
}
in (gbl_env, lcl_env)
+
-- Attempt to load the given module and return its exported entities if successful.
--
loadModule :: SDoc -> Module -> DsM GlobalRdrEnv
@@ -608,6 +616,12 @@ dsGetFamInstEnvs
dsGetMetaEnv :: DsM (NameEnv DsMetaVal)
dsGetMetaEnv = do { env <- getLclEnv; return (dsl_meta env) }
+-- | The @COMPLETE@ pragams provided by the user for a given `TyCon`.
+dsGetCompleteMatches :: TyCon -> DsM [CompleteMatch]
+dsGetCompleteMatches tc = do
+ env <- getGblEnv
+ return $ (lookupWithDefaultUFM (ds_complete_matches env) [] tc)
+
dsLookupMetaEnv :: Name -> DsM (Maybe DsMetaVal)
dsLookupMetaEnv name = do { env <- getLclEnv; return (lookupNameEnv (dsl_meta env) name) }
diff --git a/compiler/deSugar/PmExpr.hs b/compiler/deSugar/PmExpr.hs
index e35358fba5..8c3df9689e 100644
--- a/compiler/deSugar/PmExpr.hs
+++ b/compiler/deSugar/PmExpr.hs
@@ -53,11 +53,15 @@ refer to variables that are otherwise substituted away.
-- | Lifted expressions for pattern match checking.
data PmExpr = PmExprVar Name
- | PmExprCon DataCon [PmExpr]
+ | PmExprCon ConLike [PmExpr]
| PmExprLit PmLit
| PmExprEq PmExpr PmExpr -- Syntactic equality
| PmExprOther (HsExpr Id) -- Note [PmExprOther in PmExpr]
+
+mkPmExprData :: DataCon -> [PmExpr] -> PmExpr
+mkPmExprData dc args = PmExprCon (RealDataCon dc) args
+
-- | Literals (simple and overloaded ones) for pattern match checking.
data PmLit = PmSLit HsLit -- simple
| PmOLit Bool {- is it negated? -} (HsOverLit Id) -- overloaded
@@ -148,11 +152,11 @@ toComplex (x,e) = (PmExprVar (idName x), e)
-- | Expression `True'
truePmExpr :: PmExpr
-truePmExpr = PmExprCon trueDataCon []
+truePmExpr = mkPmExprData trueDataCon []
-- | Expression `False'
falsePmExpr :: PmExpr
-falsePmExpr = PmExprCon falseDataCon []
+falsePmExpr = mkPmExprData falseDataCon []
-- ----------------------------------------------------------------------------
-- ** Predicates on PmExpr
@@ -169,17 +173,17 @@ isNegatedPmLit _other_lit = False
-- | Check whether a PmExpr is syntactically equal to term `True'.
isTruePmExpr :: PmExpr -> Bool
-isTruePmExpr (PmExprCon c []) = c == trueDataCon
+isTruePmExpr (PmExprCon c []) = c == RealDataCon trueDataCon
isTruePmExpr _other_expr = False
-- | Check whether a PmExpr is syntactically equal to term `False'.
isFalsePmExpr :: PmExpr -> Bool
-isFalsePmExpr (PmExprCon c []) = c == falseDataCon
+isFalsePmExpr (PmExprCon c []) = c == RealDataCon falseDataCon
isFalsePmExpr _other_expr = False
-- | Check whether a PmExpr is syntactically e
isNilPmExpr :: PmExpr -> Bool
-isNilPmExpr (PmExprCon c _) = c == nilDataCon
+isNilPmExpr (PmExprCon c _) = c == RealDataCon nilDataCon
isNilPmExpr _other_expr = False
-- | Check whether a PmExpr is syntactically equal to (x == y).
@@ -242,7 +246,7 @@ hsExprToPmExpr e@(NegApp _ neg_e)
hsExprToPmExpr (HsPar (L _ e)) = hsExprToPmExpr e
hsExprToPmExpr e@(ExplicitTuple ps boxity)
- | all tupArgPresent ps = PmExprCon tuple_con tuple_args
+ | all tupArgPresent ps = mkPmExprData tuple_con tuple_args
| otherwise = PmExprOther e
where
tuple_con = tupleDataCon boxity (length ps)
@@ -252,11 +256,12 @@ hsExprToPmExpr e@(ExplicitList _elem_ty mb_ol elems)
| Nothing <- mb_ol = foldr cons nil (map lhsExprToPmExpr elems)
| otherwise = PmExprOther e {- overloaded list: No PmExprApp -}
where
- cons x xs = PmExprCon consDataCon [x,xs]
- nil = PmExprCon nilDataCon []
+ cons x xs = mkPmExprData consDataCon [x,xs]
+ nil = mkPmExprData nilDataCon []
hsExprToPmExpr (ExplicitPArr _elem_ty elems)
- = PmExprCon (parrFakeCon (length elems)) (map lhsExprToPmExpr elems)
+ = mkPmExprData (parrFakeCon (length elems)) (map lhsExprToPmExpr elems)
+
-- we want this but we would have to make everything monadic :/
-- ./compiler/deSugar/DsMonad.hs:397:dsLookupDataCon :: Name -> DsM DataCon
@@ -388,30 +393,22 @@ needsParens (PmExprVar {}) = False
needsParens (PmExprLit l) = isNegatedPmLit l
needsParens (PmExprEq {}) = False -- will become a wildcard
needsParens (PmExprOther {}) = False -- will become a wildcard
-needsParens (PmExprCon c es)
+needsParens (PmExprCon (RealDataCon c) es)
| isTupleDataCon c || isPArrFakeCon c
|| isConsDataCon c || null es = False
| otherwise = True
+needsParens (PmExprCon (PatSynCon _) es) = not (null es)
pprPmExprWithParens :: PmExpr -> PmPprM SDoc
pprPmExprWithParens expr
| needsParens expr = parens <$> pprPmExpr expr
| otherwise = pprPmExpr expr
-pprPmExprCon :: DataCon -> [PmExpr] -> PmPprM SDoc
-pprPmExprCon con args
+pprPmExprCon :: ConLike -> [PmExpr] -> PmPprM SDoc
+pprPmExprCon (RealDataCon con) args
| isTupleDataCon con = mkTuple <$> mapM pprPmExpr args
| isPArrFakeCon con = mkPArr <$> mapM pprPmExpr args
| isConsDataCon con = pretty_list
- | dataConIsInfix con = case args of
- [x, y] -> do x' <- pprPmExprWithParens x
- y' <- pprPmExprWithParens y
- return (x' <+> ppr con <+> y')
- -- can it be infix but have more than two arguments?
- list -> pprPanic "pprPmExprCon:" (ppr list)
- | null args = return (ppr con)
- | otherwise = do args' <- mapM pprPmExprWithParens args
- return (fsep (ppr con : args'))
where
mkTuple, mkPArr :: [SDoc] -> SDoc
mkTuple = parens . fsep . punctuate comma
@@ -426,10 +423,22 @@ pprPmExprCon con args
list = list_elements args
list_elements [x,y]
- | PmExprCon c es <- y, nilDataCon == c = ASSERT(null es) [x,y]
- | PmExprCon c es <- y, consDataCon == c = x : list_elements es
+ | PmExprCon c es <- y, RealDataCon nilDataCon == c
+ = ASSERT(null es) [x,y]
+ | PmExprCon c es <- y, RealDataCon consDataCon == c
+ = x : list_elements es
| otherwise = [x,y]
list_elements list = pprPanic "list_elements:" (ppr list)
+pprPmExprCon cl args
+ | conLikeIsInfix cl = case args of
+ [x, y] -> do x' <- pprPmExprWithParens x
+ y' <- pprPmExprWithParens y
+ return (x' <+> ppr cl <+> y')
+ -- can it be infix but have more than two arguments?
+ list -> pprPanic "pprPmExprCon:" (ppr list)
+ | null args = return (ppr cl)
+ | otherwise = do args' <- mapM pprPmExprWithParens args
+ return (fsep (ppr cl : args'))
instance Outputable PmLit where
ppr (PmSLit l) = pmPprHsLit l
diff --git a/compiler/deSugar/TmOracle.hs b/compiler/deSugar/TmOracle.hs
index 05966cd858..64f20e2121 100644
--- a/compiler/deSugar/TmOracle.hs
+++ b/compiler/deSugar/TmOracle.hs
@@ -26,7 +26,6 @@ import PmExpr
import Id
import Name
-import TysWiredIn
import Type
import HsLit
import TcHsSyn
@@ -113,12 +112,12 @@ solveComplexEq solver_state@(standby, (unhandled, env)) eq@(e1, e2) = case eq of
(PmExprCon c1 ts1, PmExprCon c2 ts2)
| c1 == c2 -> foldlM solveComplexEq solver_state (zip ts1 ts2)
| otherwise -> Nothing
- (PmExprCon c [], PmExprEq t1 t2)
- | c == trueDataCon -> solveComplexEq solver_state (t1, t2)
- | c == falseDataCon -> Just (eq:standby, (unhandled, env))
- (PmExprEq t1 t2, PmExprCon c [])
- | c == trueDataCon -> solveComplexEq solver_state (t1, t2)
- | c == falseDataCon -> Just (eq:standby, (unhandled, env))
+ (PmExprCon _ [], PmExprEq t1 t2)
+ | isTruePmExpr e1 -> solveComplexEq solver_state (t1, t2)
+ | isFalsePmExpr e1 -> Just (eq:standby, (unhandled, env))
+ (PmExprEq t1 t2, PmExprCon _ [])
+ | isTruePmExpr e2 -> solveComplexEq solver_state (t1, t2)
+ | isFalsePmExpr e2 -> Just (eq:standby, (unhandled, env))
(PmExprVar x, PmExprVar y)
| x == y -> Just solver_state
diff --git a/compiler/hsSyn/HsBinds.hs b/compiler/hsSyn/HsBinds.hs
index e04dc89559..1f38c387df 100644
--- a/compiler/hsSyn/HsBinds.hs
+++ b/compiler/hsSyn/HsBinds.hs
@@ -853,6 +853,7 @@ data Sig name
| SCCFunSig SourceText -- Note [Pragma source text] in BasicTypes
(Located name) -- Function name
(Maybe StringLiteral)
+ | CompleteMatchSig SourceText (Located [Located name]) (Maybe (Located name))
deriving instance (DataId name) => Data (Sig name)
@@ -920,6 +921,7 @@ isPragLSig :: LSig name -> Bool
isPragLSig (L _ (SpecSig {})) = True
isPragLSig (L _ (InlineSig {})) = True
isPragLSig (L _ (SCCFunSig {})) = True
+isPragLSig (L _ (CompleteMatchSig {})) = True
isPragLSig _ = False
isInlineLSig :: LSig name -> Bool
@@ -935,6 +937,10 @@ isSCCFunSig :: LSig name -> Bool
isSCCFunSig (L _ (SCCFunSig {})) = True
isSCCFunSig _ = False
+isCompleteMatchSig :: LSig name -> Bool
+isCompleteMatchSig (L _ (CompleteMatchSig {} )) = True
+isCompleteMatchSig _ = False
+
hsSigDoc :: Sig name -> SDoc
hsSigDoc (TypeSig {}) = text "type signature"
hsSigDoc (PatSynSig {}) = text "pattern synonym signature"
@@ -948,6 +954,7 @@ hsSigDoc (SpecInstSig {}) = text "SPECIALISE instance pragma"
hsSigDoc (FixSig {}) = text "fixity declaration"
hsSigDoc (MinimalSig {}) = text "MINIMAL pragma"
hsSigDoc (SCCFunSig {}) = text "SCC pragma"
+hsSigDoc (CompleteMatchSig {}) = text "COMPLETE pragma"
{-
Check if signatures overlap; this is used when checking for duplicate
@@ -983,6 +990,12 @@ ppr_sig (PatSynSig names sig_ty)
= text "pattern" <+> pprVarSig (map unLoc names) (ppr sig_ty)
ppr_sig (SCCFunSig src fn mlabel)
= pragSrcBrackets src "{-# SCC" (ppr fn <+> maybe empty ppr mlabel )
+ppr_sig (CompleteMatchSig src cs mty)
+ = pragSrcBrackets src "{-# COMPLETE"
+ ((hsep (punctuate comma (map ppr (unLoc cs))))
+ <+> opt_sig)
+ where
+ opt_sig = maybe empty ((\t -> dcolon <+> ppr t) . unLoc) mty
instance OutputableBndr name => Outputable (FixitySig name) where
ppr (FixitySig names fixity) = sep [ppr fixity, pprops]
diff --git a/compiler/iface/IfaceSyn.hs b/compiler/iface/IfaceSyn.hs
index 72a5b57f43..7a1d427397 100644
--- a/compiler/iface/IfaceSyn.hs
+++ b/compiler/iface/IfaceSyn.hs
@@ -19,6 +19,7 @@ module IfaceSyn (
IfaceSrcBang(..), SrcUnpackedness(..), SrcStrictness(..),
IfaceAxBranch(..),
IfaceTyConParent(..),
+ IfaceCompleteMatch(..),
-- * Binding names
IfaceTopBndr,
@@ -295,6 +296,11 @@ data IfaceAnnotation
type IfaceAnnTarget = AnnTarget OccName
+data IfaceCompleteMatch = IfaceCompleteMatch [IfExtName] IfExtName
+
+
+
+
-- Here's a tricky case:
-- * Compile with -O module A, and B which imports A.f
-- * Change function f in A, and recompile without -O
@@ -2090,3 +2096,7 @@ instance Binary IfaceTyConParent where
pr <- get bh
ty <- get bh
return $ IfDataInstance ax pr ty
+
+instance Binary IfaceCompleteMatch where
+ put_ bh (IfaceCompleteMatch cs ts) = put_ bh cs >> put_ bh ts
+ get bh = IfaceCompleteMatch <$> get bh <*> get bh
diff --git a/compiler/iface/MkIface.hs b/compiler/iface/MkIface.hs
index bdc9f0ffe5..5215965aa4 100644
--- a/compiler/iface/MkIface.hs
+++ b/compiler/iface/MkIface.hs
@@ -206,7 +206,8 @@ mkIface_ hsc_env maybe_old_fingerprint
md_anns = anns,
md_vect_info = vect_info,
md_types = type_env,
- md_exports = exports }
+ md_exports = exports,
+ md_complete_sigs = complete_sigs }
-- NB: notice that mkIface does not look at the bindings
-- only at the TypeEnv. The previous Tidy phase has
-- put exactly the info into the TypeEnv that we want
@@ -241,6 +242,7 @@ mkIface_ hsc_env maybe_old_fingerprint
iface_vect_info = flattenVectInfo vect_info
trust_info = setSafeMode safe_mode
annotations = map mkIfaceAnnotation anns
+ icomplete_sigs = map mkIfaceCompleteSig complete_sigs
intermediate_iface = ModIface {
mi_module = this_mod,
@@ -285,7 +287,8 @@ mkIface_ hsc_env maybe_old_fingerprint
-- And build the cached values
mi_warn_fn = mkIfaceWarnCache warns,
- mi_fix_fn = mkIfaceFixCache fixities }
+ mi_fix_fn = mkIfaceFixCache fixities,
+ mi_complete_sigs = icomplete_sigs }
(new_iface, no_change_at_all)
<- {-# SCC "versioninfo" #-}
@@ -993,6 +996,19 @@ mkOrphMap get_key decls
{-
************************************************************************
* *
+ COMPLETE Pragmas
+* *
+************************************************************************
+-}
+
+mkIfaceCompleteSig :: CompleteMatch -> IfaceCompleteMatch
+mkIfaceCompleteSig (CompleteMatch cls tc) =
+ IfaceCompleteMatch (map conLikeName cls) (tyConName tc)
+
+
+{-
+************************************************************************
+* *
Keeping track of what we've slurped, and fingerprints
* *
************************************************************************
diff --git a/compiler/iface/TcIface.hs b/compiler/iface/TcIface.hs
index c0b84640b0..e08a3d71f6 100644
--- a/compiler/iface/TcIface.hs
+++ b/compiler/iface/TcIface.hs
@@ -177,6 +177,9 @@ typecheckIface iface
-- Exports
; exports <- ifaceExportNames (mi_exports iface)
+ -- Complete Sigs
+ ; complete_sigs <- tcIfaceCompleteSigs (mi_complete_sigs iface)
+
-- Finished
; traceIf (vcat [text "Finished typechecking interface for" <+> ppr (mi_module iface),
-- Careful! If we tug on the TyThing thunks too early
@@ -190,6 +193,7 @@ typecheckIface iface
, md_anns = anns
, md_vect_info = vect_info
, md_exports = exports
+ , md_complete_sigs = complete_sigs
}
}
@@ -327,6 +331,7 @@ typecheckIfacesForMerging mod ifaces tc_env_var =
anns <- tcIfaceAnnotations (mi_anns iface)
vect_info <- tcIfaceVectInfo (mi_semantic_module iface) type_env (mi_vect_info iface)
exports <- ifaceExportNames (mi_exports iface)
+ complete_sigs <- tcIfaceCompleteSigs (mi_complete_sigs iface)
return $ ModDetails { md_types = type_env
, md_insts = insts
, md_fam_insts = fam_insts
@@ -334,6 +339,7 @@ typecheckIfacesForMerging mod ifaces tc_env_var =
, md_anns = anns
, md_vect_info = vect_info
, md_exports = exports
+ , md_complete_sigs = complete_sigs
}
return (global_type_env, details)
@@ -366,6 +372,7 @@ typecheckIfaceForInstantiate nsubst iface =
anns <- tcIfaceAnnotations (mi_anns iface)
vect_info <- tcIfaceVectInfo (mi_semantic_module iface) type_env (mi_vect_info iface)
exports <- ifaceExportNames (mi_exports iface)
+ complete_sigs <- tcIfaceCompleteSigs (mi_complete_sigs iface)
return $ ModDetails { md_types = type_env
, md_insts = insts
, md_fam_insts = fam_insts
@@ -373,6 +380,7 @@ typecheckIfaceForInstantiate nsubst iface =
, md_anns = anns
, md_vect_info = vect_info
, md_exports = exports
+ , md_complete_sigs = complete_sigs
}
-- Note [Resolving never-exported Names in TcIface]
@@ -1016,6 +1024,21 @@ tcIfaceAnnTarget (ModuleTarget mod) = do
{-
************************************************************************
* *
+ Complete Match Pragmas
+* *
+************************************************************************
+-}
+
+tcIfaceCompleteSigs :: [IfaceCompleteMatch] -> IfL [CompleteMatch]
+tcIfaceCompleteSigs = mapM tcIfaceCompleteSig
+
+tcIfaceCompleteSig :: IfaceCompleteMatch -> IfL CompleteMatch
+tcIfaceCompleteSig (IfaceCompleteMatch ms t) =
+ CompleteMatch <$> (mapM tcIfaceConLike ms) <*> tcIfaceTyConByName t
+
+{-
+************************************************************************
+* *
Vectorisation information
* *
************************************************************************
@@ -1668,6 +1691,14 @@ tcIfaceDataCon name = do { thing <- tcIfaceGlobal name
AConLike (RealDataCon dc) -> return dc
_ -> pprPanic "tcIfaceExtDC" (ppr name$$ ppr thing) }
+tcIfaceConLike :: Name -> IfL ConLike
+tcIfaceConLike name =
+ do { thing <- tcIfaceGlobal name
+ ; case thing of
+ AConLike cl -> return cl
+ _ -> pprPanic "tcIfaceExtCL" (ppr name$$ ppr thing) }
+
+
tcIfaceExtId :: Name -> IfL Id
tcIfaceExtId name = do { thing <- tcIfaceGlobal name
; case thing of
diff --git a/compiler/main/HscTypes.hs b/compiler/main/HscTypes.hs
index 51cec26006..0fcf58229b 100644
--- a/compiler/main/HscTypes.hs
+++ b/compiler/main/HscTypes.hs
@@ -37,6 +37,7 @@ module HscTypes (
HomePackageTable, HomeModInfo(..), emptyHomePackageTable,
lookupHpt, eltsHpt, filterHpt, allHpt, mapHpt, delFromHpt,
addToHpt, addListToHpt, lookupHptDirectly, listToHpt,
+ hptCompleteSigs,
hptInstances, hptRules, hptVectInfo, pprHPT,
hptObjs,
@@ -131,6 +132,9 @@ module HscTypes (
SourceError, GhcApiError, mkSrcErr, srcErrorMessages, mkApiErr,
throwOneError, handleSourceError,
handleFlagWarnings, printOrThrowWarnings,
+
+ -- * COMPLETE signature
+ CompleteMatch(..)
) where
#include "HsVersions.h"
@@ -614,6 +618,8 @@ lookupIfaceByModule _dflags hpt pit mod
-- We could eliminate (b) if we wanted, by making GHC.Prim belong to a package
-- of its own, but it doesn't seem worth the bother.
+hptCompleteSigs :: HscEnv -> [CompleteMatch]
+hptCompleteSigs = hptAllThings (md_complete_sigs . hm_details)
-- | Find all the instance declarations (of classes and families) from
-- the Home Package Table filtered by the provided predicate function.
@@ -916,13 +922,14 @@ data ModIface
mi_trust :: !IfaceTrustInfo,
-- ^ Safe Haskell Trust information for this module.
- mi_trust_pkg :: !Bool
+ mi_trust_pkg :: !Bool,
-- ^ Do we require the package this module resides in be trusted
-- to trust this module? This is used for the situation where a
-- module is Safe (so doesn't require the package be trusted
-- itself) but imports some trustworthy modules from its own
-- package (which does require its own package be trusted).
-- See Note [RnNames . Trust Own Package]
+ mi_complete_sigs :: [IfaceCompleteMatch]
}
-- | Old-style accessor for whether or not the ModIface came from an hs-boot
@@ -997,7 +1004,8 @@ instance Binary ModIface where
mi_vect_info = vect_info,
mi_hpc = hpc_info,
mi_trust = trust,
- mi_trust_pkg = trust_pkg }) = do
+ mi_trust_pkg = trust_pkg,
+ mi_complete_sigs = complete_sigs }) = do
put_ bh mod
put_ bh sig_of
put_ bh hsc_src
@@ -1023,6 +1031,7 @@ instance Binary ModIface where
put_ bh hpc_info
put_ bh trust
put_ bh trust_pkg
+ put_ bh complete_sigs
get bh = do
mod <- get bh
@@ -1050,6 +1059,7 @@ instance Binary ModIface where
hpc_info <- get bh
trust <- get bh
trust_pkg <- get bh
+ complete_sigs <- get bh
return (ModIface {
mi_module = mod,
mi_sig_of = sig_of,
@@ -1080,7 +1090,8 @@ instance Binary ModIface where
-- And build the cached values
mi_warn_fn = mkIfaceWarnCache warns,
mi_fix_fn = mkIfaceFixCache fixities,
- mi_hash_fn = mkIfaceHashCache decls })
+ mi_hash_fn = mkIfaceHashCache decls,
+ mi_complete_sigs = complete_sigs })
-- | The original names declared of a certain module that are exported
type IfaceExport = AvailInfo
@@ -1116,7 +1127,8 @@ emptyModIface mod
mi_hash_fn = emptyIfaceHashCache,
mi_hpc = False,
mi_trust = noIfaceTrustInfo,
- mi_trust_pkg = False }
+ mi_trust_pkg = False,
+ mi_complete_sigs = [] }
-- | Constructs cache for the 'mi_hash_fn' field of a 'ModIface'
@@ -1148,7 +1160,9 @@ data ModDetails
md_rules :: ![CoreRule], -- ^ Domain may include 'Id's from other modules
md_anns :: ![Annotation], -- ^ Annotations present in this module: currently
-- they only annotate things also declared in this module
- md_vect_info :: !VectInfo -- ^ Module vectorisation information
+ md_vect_info :: !VectInfo, -- ^ Module vectorisation information
+ md_complete_sigs :: [CompleteMatch]
+ -- ^ Complete match pragmas for this module
}
-- | Constructs an empty ModDetails
@@ -1160,7 +1174,8 @@ emptyModDetails
md_rules = [],
md_fam_insts = [],
md_anns = [],
- md_vect_info = noVectInfo }
+ md_vect_info = noVectInfo,
+ md_complete_sigs = [] }
-- | Records the modules directly imported by a module for extracting e.g.
-- usage information, and also to give better error message
@@ -1207,6 +1222,7 @@ data ModGuts
mg_foreign :: !ForeignStubs, -- ^ Foreign exports declared in this module
mg_warns :: !Warnings, -- ^ Warnings declared in the module
mg_anns :: [Annotation], -- ^ Annotations declared in this module
+ mg_complete_sigs :: [CompleteMatch], -- ^ Complete Matches
mg_hpc_info :: !HpcInfo, -- ^ Coverage tick boxes in the module
mg_modBreaks :: !(Maybe ModBreaks), -- ^ Breakpoints for the module
mg_vect_decls:: ![CoreVect], -- ^ Vectorisation declarations in this module
@@ -2965,3 +2981,17 @@ nameOfObject other = pprPanic "nameOfObject" (ppr other)
byteCodeOfObject :: Unlinked -> CompiledByteCode
byteCodeOfObject (BCOs bc) = bc
byteCodeOfObject other = pprPanic "byteCodeOfObject" (ppr other)
+
+
+-------------------------------------------
+
+-- | A list of conlikes which represents a complete pattern match.
+-- These arise from @COMPLETE@ signatures.
+data CompleteMatch = CompleteMatch {
+ completeMatch :: [ConLike]
+ , completeMatchType :: TyCon
+ }
+
+instance Outputable CompleteMatch where
+ ppr (CompleteMatch cl ty) = text "CompleteMatch:" <+> ppr cl
+ <+> dcolon <+> ppr ty
diff --git a/compiler/main/TidyPgm.hs b/compiler/main/TidyPgm.hs
index cbf7038187..c546e5c257 100644
--- a/compiler/main/TidyPgm.hs
+++ b/compiler/main/TidyPgm.hs
@@ -163,6 +163,7 @@ mkBootModDetailsTc hsc_env
, md_anns = []
, md_exports = exports
, md_vect_info = noVectInfo
+ , md_complete_sigs = []
})
}
where
@@ -318,6 +319,7 @@ tidyProgram hsc_env (ModGuts { mg_module = mod
, mg_rules = imp_rules
, mg_vect_info = vect_info
, mg_anns = anns
+ , mg_complete_sigs = complete_sigs
, mg_deps = deps
, mg_foreign = foreign_stubs
, mg_hpc_info = hpc_info
@@ -425,7 +427,8 @@ tidyProgram hsc_env (ModGuts { mg_module = mod
md_vect_info = tidy_vect_info,
md_fam_insts = fam_insts,
md_exports = exports,
- md_anns = anns -- are already tidy
+ md_anns = anns, -- are already tidy
+ md_complete_sigs = complete_sigs
})
}
where
diff --git a/compiler/parser/Lexer.x b/compiler/parser/Lexer.x
index 6c4abe047a..63715a08a8 100644
--- a/compiler/parser/Lexer.x
+++ b/compiler/parser/Lexer.x
@@ -636,6 +636,7 @@ data Token
| ITunpack_prag SourceText
| ITnounpack_prag SourceText
| ITann_prag SourceText
+ | ITcomplete_prag SourceText
| ITclose_prag
| IToptions_prag String
| ITinclude_prag String
@@ -2716,7 +2717,7 @@ ignoredPrags = Map.fromList (map ignored pragmas)
-- CFILES is a hugs-only thing.
pragmas = options_pragmas ++ ["cfiles", "contract"]
-oneWordPrags = Map.fromList([
+oneWordPrags = Map.fromList [
("rules", rulePrag),
("inline",
strtoken (\s -> (ITinline_prag (SourceText s) Inline FunLike))),
@@ -2744,7 +2745,9 @@ oneWordPrags = Map.fromList([
("overlappable", strtoken (\s -> IToverlappable_prag (SourceText s))),
("overlapping", strtoken (\s -> IToverlapping_prag (SourceText s))),
("incoherent", strtoken (\s -> ITincoherent_prag (SourceText s))),
- ("ctype", strtoken (\s -> ITctype (SourceText s)))])
+ ("ctype", strtoken (\s -> ITctype (SourceText s))),
+ ("complete", strtoken (\s -> ITcomplete_prag (SourceText s)))
+ ]
twoWordPrags = Map.fromList([
("inline conlike",
diff --git a/compiler/parser/Parser.y b/compiler/parser/Parser.y
index 222867483c..2b70fb7999 100644
--- a/compiler/parser/Parser.y
+++ b/compiler/parser/Parser.y
@@ -441,6 +441,7 @@ are the most common patterns, rewritten as regular expressions for clarity:
'{-# OVERLAPPABLE' { L _ (IToverlappable_prag _) }
'{-# OVERLAPS' { L _ (IToverlaps_prag _) }
'{-# INCOHERENT' { L _ (ITincoherent_prag _) }
+ '{-# COMPLETE' { L _ (ITcomplete_prag _) }
'#-}' { L _ ITclose_prag }
'..' { L _ ITdotdot } -- reserved symbols
@@ -1672,6 +1673,10 @@ opt_asig :: { ([AddAnn],Maybe (LHsType RdrName)) }
: {- empty -} { ([],Nothing) }
| '::' atype { ([mu AnnDcolon $1],Just $2) }
+opt_tyconsig :: { ([AddAnn], Maybe (Located RdrName)) }
+ : {- empty -} { ([], Nothing) }
+ | '::' gtycon { ([mu AnnDcolon $1], Just $2) }
+
sigtype :: { LHsType RdrName }
: ctype { $1 }
@@ -2248,6 +2253,13 @@ sigdecl :: { LHsDecl RdrName }
| pattern_synonym_sig { sLL $1 $> . SigD . unLoc $ $1 }
+ | '{-# COMPLETE' con_list opt_tyconsig '#-}'
+ {% let (dcolon, tc) = $3
+ in ams
+ (sLL $1 $>
+ (SigD (CompleteMatchSig (getCOMPLETE_PRAGs $1) $2 tc)))
+ ([ mo $1 ] ++ dcolon ++ [mc $4]) }
+
-- This rule is for both INLINE and INLINABLE pragmas
| '{-# INLINE' activation qvar '#-}'
{% ams ((sLL $1 $> $ SigD (InlineSig $3
@@ -3393,6 +3405,7 @@ getTH_ID_TY_SPLICE (L _ (ITidTyEscape x)) = x
getINLINE (L _ (ITinline_prag _ inl conl)) = (inl,conl)
getSPEC_INLINE (L _ (ITspec_inline_prag _ True)) = (Inline, FunLike)
getSPEC_INLINE (L _ (ITspec_inline_prag _ False)) = (NoInline,FunLike)
+getCOMPLETE_PRAGs (L _ (ITcomplete_prag x)) = x
getDOCNEXT (L _ (ITdocCommentNext x)) = x
getDOCPREV (L _ (ITdocCommentPrev x)) = x
diff --git a/compiler/parser/RdrHsSyn.hs b/compiler/parser/RdrHsSyn.hs
index 2c9600427c..64a60c4841 100644
--- a/compiler/parser/RdrHsSyn.hs
+++ b/compiler/parser/RdrHsSyn.hs
@@ -7,7 +7,7 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
-module RdrHsSyn (
+module RdrHsSyn (
mkHsOpApp,
mkHsIntegral, mkHsFractional, mkHsIsString,
mkHsDo, mkSpliceDecl,
diff --git a/compiler/rename/RnBinds.hs b/compiler/rename/RnBinds.hs
index c232e76ea0..f6a22f5df2 100644
--- a/compiler/rename/RnBinds.hs
+++ b/compiler/rename/RnBinds.hs
@@ -950,6 +950,13 @@ renameSig ctxt sig@(SCCFunSig st v s)
= do { new_v <- lookupSigOccRn ctxt sig v
; return (SCCFunSig st new_v s, emptyFVs) }
+-- COMPLETE Sigs can refer to imported IDs which is why we use
+-- lookupLocatedOccRn rather than lookupSigOccRn
+renameSig _ctxt (CompleteMatchSig s (L l bf) mty)
+ = do new_bf <- traverse lookupLocatedOccRn bf
+ new_mty <- traverse lookupLocatedOccRn mty
+ return (CompleteMatchSig s (L l new_bf) new_mty, emptyFVs)
+
ppr_sig_bndrs :: [Located RdrName] -> SDoc
ppr_sig_bndrs bs = quotes (pprWithCommas ppr bs)
@@ -991,6 +998,9 @@ okHsSig ctxt (L _ sig)
(SCCFunSig {}, HsBootCtxt {}) -> False
(SCCFunSig {}, _) -> True
+ (CompleteMatchSig {}, TopSigCtxt {} ) -> True
+ (CompleteMatchSig {}, _) -> False
+
-------------------
findDupSigs :: [LSig RdrName] -> [[(Located RdrName, Sig RdrName)]]
-- Check for duplicates on RdrName version,
diff --git a/compiler/typecheck/TcBinds.hs b/compiler/typecheck/TcBinds.hs
index 2ad00d50e3..25c40618f2 100644
--- a/compiler/typecheck/TcBinds.hs
+++ b/compiler/typecheck/TcBinds.hs
@@ -37,7 +37,7 @@ import FamInstEnv( normaliseType )
import FamInst( tcGetFamInstEnvs )
import TyCon
import TcType
-import Type( mkStrLitTy, tidyOpenType, mkTyVarBinder )
+import Type( mkStrLitTy, tidyOpenType, mkTyVarBinder, splitTyConApp_maybe)
import TysPrim
import TysWiredIn( cTupleTyConName )
import Id
@@ -62,6 +62,7 @@ import TcValidity (checkValidType)
import Unique (getUnique)
import UniqFM
import qualified GHC.LanguageExtensions as LangExt
+import ConLike
import Control.Monad
@@ -185,13 +186,115 @@ tcTopBinds binds sigs
; return (gbl, lcl) }
; specs <- tcImpPrags sigs -- SPECIALISE prags for imported Ids
- ; let { tcg_env' = tcg_env { tcg_imp_specs = specs ++ tcg_imp_specs tcg_env }
+ ; complete_matches <- setEnvs (tcg_env, tcl_env) $ tcCompleteSigs sigs
+ ; traceTc "complete_matches" (ppr binds $$ ppr sigs)
+ ; traceTc "complete_matches" (ppr complete_matches)
+
+ ; let { tcg_env' = tcg_env { tcg_imp_specs
+ = specs ++ tcg_imp_specs tcg_env
+ , tcg_complete_matches
+ = complete_matches
+ ++ tcg_complete_matches tcg_env }
`addTypecheckedBinds` map snd binds' }
; return (tcg_env', tcl_env) }
-- The top level bindings are flattened into a giant
-- implicitly-mutually-recursive LHsBinds
+
+-- Note [Typechecking Complete Matches]
+-- Much like when a user bundled a pattern synonym, the result types of
+-- all the constructors in the match pragma must be consistent.
+--
+-- If we allowed pragmas with inconsistent types then it would be
+-- impossible to ever match every constructor in the list and so
+-- the pragma would be useless.
+
+
+
+
+
+-- This is only used in `tcCompleteSig`. We fold over all the conlikes,
+-- this accumulator keeps track of the first `ConLike` with a concrete
+-- return type. After fixing the return type, all other constructors with
+-- a fixed return type must agree with this.
+--
+-- The fields of `Fixed` cache the first conlike and its return type so
+-- that that we can compare all the other conlikes to it. The conlike is
+-- stored for error messages.
+--
+-- `Nothing` in the case that the type is fixed by a type signature
+data CompleteSigType = AcceptAny | Fixed (Maybe ConLike) TyCon
+
+tcCompleteSigs :: [LSig Name] -> TcM [CompleteMatch]
+tcCompleteSigs sigs =
+ let
+ doOne :: Sig Name -> TcM (Maybe CompleteMatch)
+ doOne c@(CompleteMatchSig _ lns mtc)
+ = fmap Just $ do
+ addErrCtxt (text "In" <+> ppr c) $
+ case mtc of
+ Nothing -> infer_complete_match
+ Just tc -> check_complete_match tc
+ where
+
+ checkCLTypes acc = foldM checkCLType (acc, []) (unLoc lns)
+
+ infer_complete_match = do
+ (res, cls) <- checkCLTypes AcceptAny
+ case res of
+ AcceptAny -> failWithTc ambiguousError
+ Fixed _ tc -> return $ CompleteMatch cls tc
+
+ check_complete_match tc_name = do
+ ty_con <- tcLookupLocatedTyCon tc_name
+ (_, cls) <- checkCLTypes (Fixed Nothing ty_con)
+ return $ CompleteMatch cls ty_con
+ doOne _ = return Nothing
+
+ ambiguousError :: SDoc
+ ambiguousError =
+ text "A type signature must be provided for a set of polymorphic"
+ <+> text "pattern synonyms."
+
+
+ -- See note [Typechecking Complete Matches]
+ checkCLType :: (CompleteSigType, [ConLike]) -> Located Name
+ -> TcM (CompleteSigType, [ConLike])
+ checkCLType (cst, cs) n = do
+ cl <- addLocM tcLookupConLike n
+ let (_,_,_,_,_,_, res_ty) = conLikeFullSig cl
+ res_ty_con = fst <$> splitTyConApp_maybe res_ty
+ case (cst, res_ty_con) of
+ (AcceptAny, Nothing) -> return (AcceptAny, cl:cs)
+ (AcceptAny, Just tc) -> return (Fixed (Just cl) tc, cl:cs)
+ (Fixed mfcl tc, Nothing) -> return (Fixed mfcl tc, cl:cs)
+ (Fixed mfcl tc, Just tc') ->
+ if tc == tc'
+ then return (Fixed mfcl tc, cl:cs)
+ else case mfcl of
+ Nothing ->
+ addErrCtxt (text "In" <+> ppr cl) $
+ failWithTc typeSigErrMsg
+ Just cl -> failWithTc (errMsg cl)
+ where
+ typeSigErrMsg :: SDoc
+ typeSigErrMsg =
+ text "Couldn't match expected type"
+ <+> quotes (ppr tc)
+ <+> text "with"
+ <+> quotes (ppr tc')
+
+ errMsg :: ConLike -> SDoc
+ errMsg fcl =
+ text "Cannot form a group of complete patterns from patterns"
+ <+> quotes (ppr fcl) <+> text "and" <+> quotes (ppr cl)
+ <+> text "as they match different type constructors"
+ <+> parens (quotes (ppr tc)
+ <+> text "resp."
+ <+> quotes (ppr tc'))
+ in mapMaybeM (addLocM doOne) sigs
+
tcRecSelBinds :: HsValBinds Name -> TcM TcGblEnv
tcRecSelBinds (ValBindsOut binds sigs)
= tcExtendGlobalValEnv [sel_id | L _ (IdSig sel_id) <- sigs] $
diff --git a/compiler/typecheck/TcRnDriver.hs b/compiler/typecheck/TcRnDriver.hs
index 2d35e96851..28ca41b078 100644
--- a/compiler/typecheck/TcRnDriver.hs
+++ b/compiler/typecheck/TcRnDriver.hs
@@ -253,7 +253,7 @@ tcRnModuleTcRnM hsc_env hsc_src
tcg_env <- tcRnExports explicit_mod_hdr export_ies tcg_env ;
traceRn "rn4b: after exports" empty ;
- -- Check that main is exported (must be after rnExports)
+ -- Check that main is exported (must be after tcRnExports)
checkMainExported tcg_env ;
-- Compare the hi-boot iface (if any) with the real thing
diff --git a/compiler/typecheck/TcRnMonad.hs b/compiler/typecheck/TcRnMonad.hs
index 4388b4431c..3c6a6c432d 100644
--- a/compiler/typecheck/TcRnMonad.hs
+++ b/compiler/typecheck/TcRnMonad.hs
@@ -297,7 +297,8 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this
tcg_dependent_files = dependent_files_var,
tcg_tc_plugins = [],
tcg_top_loc = loc,
- tcg_static_wc = static_wc_var
+ tcg_static_wc = static_wc_var,
+ tcg_complete_matches = []
} ;
lcl_env = TcLclEnv {
tcl_errs = errs_var,
diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs
index a998e49632..9e3ed5b4aa 100644
--- a/compiler/typecheck/TcRnTypes.hs
+++ b/compiler/typecheck/TcRnTypes.hs
@@ -43,11 +43,11 @@ module TcRnTypes(
IdBindingInfo(..),
IsGroupClosed(..),
SelfBootInfo(..),
- pprTcTyThingCategory, pprPECategory,
+ pprTcTyThingCategory, pprPECategory, CompleteMatch(..),
-- Desugaring types
DsM, DsLclEnv(..), DsGblEnv(..), PArrBuiltin(..),
- DsMetaEnv, DsMetaVal(..),
+ DsMetaEnv, DsMetaVal(..), CompleteMatchMap, mkCompleteMatchMap,
-- Template Haskell
ThStage(..), SpliceType(..), PendingStuff(..),
@@ -174,6 +174,7 @@ import FastString
import qualified GHC.LanguageExtensions as LangExt
import Fingerprint
import Util
+import UniqFM ( emptyUFM, addToUFM_C, UniqFM )
import Control.Monad (ap, liftM, msum)
#if __GLASGOW_HASKELL__ > 710
@@ -181,12 +182,14 @@ import qualified Control.Monad.Fail as MonadFail
#endif
import Data.Set ( Set )
-import Data.Map ( Map )
+import Data.Map ( Map )
import Data.Dynamic ( Dynamic )
import Data.Typeable ( TypeRep )
import GHCi.Message
import GHCi.RemoteTypes
+import Data.List (foldl')
+
import qualified Language.Haskell.TH as TH
-- | A 'NameShape' is a substitution on 'Name's that can be used
@@ -376,8 +379,18 @@ data DsGblEnv
-- exported entities of 'Data.Array.Parallel' iff
-- '-XParallelArrays' was given; otherwise, empty
, ds_parr_bi :: PArrBuiltin -- desugarar names for '-XParallelArrays'
+ , ds_complete_matches :: CompleteMatchMap
+ -- Additional complete pattern matches
}
+type CompleteMatchMap = UniqFM [CompleteMatch]
+
+mkCompleteMatchMap :: [CompleteMatch] -> CompleteMatchMap
+mkCompleteMatchMap cms = foldl' insertMatch emptyUFM cms
+ where
+ insertMatch :: CompleteMatchMap -> CompleteMatch -> CompleteMatchMap
+ insertMatch ufm c@(CompleteMatch _ t) = addToUFM_C (++) ufm t [c]
+
instance ContainsModule DsGblEnv where
extractModule = ds_mod
@@ -651,9 +664,10 @@ data TcGblEnv
tcg_top_loc :: RealSrcSpan,
-- ^ The RealSrcSpan this module came from
- tcg_static_wc :: TcRef WantedConstraints
- -- ^ Wanted constraints of static forms.
+ tcg_static_wc :: TcRef WantedConstraints,
+ -- ^ Wanted constraints of static forms.
-- See Note [Constraints in static forms].
+ tcg_complete_matches :: [CompleteMatch]
}
-- NB: topModIdentity, not topModSemantic!
diff --git a/compiler/vectorise/Vectorise/Monad.hs b/compiler/vectorise/Vectorise/Monad.hs
index 7c04dfe0b1..c7909ef6cb 100644
--- a/compiler/vectorise/Vectorise/Monad.hs
+++ b/compiler/vectorise/Vectorise/Monad.hs
@@ -58,7 +58,7 @@ initV hsc_env guts info thing_inside
; let type_env = typeEnvFromEntities ids (mg_tcs guts) (mg_fam_insts guts)
; (_, Just res) <- initDs hsc_env (mg_module guts)
(mg_rdr_env guts) type_env
- (mg_fam_inst_env guts) go
+ (mg_fam_inst_env guts) [] go
; case res of
Nothing
diff --git a/docs/users_guide/glasgow_exts.rst b/docs/users_guide/glasgow_exts.rst
index 52163b976f..2f322d5153 100644
--- a/docs/users_guide/glasgow_exts.rst
+++ b/docs/users_guide/glasgow_exts.rst
@@ -4512,6 +4512,10 @@ synonyms, there is no restriction on the right-hand side pattern.
Pattern synonyms cannot be defined recursively.
+:ref:`complete-pragma` can be specified in order to tell
+the pattern match exhaustiveness checker that a set of pattern synonyms is
+complete.
+
.. _patsyn-impexp:
Import and export of pattern synonyms
@@ -12759,6 +12763,80 @@ The ``{-# SOURCE #-}`` pragma is used only in ``import`` declarations,
to break a module loop. It is described in detail in
:ref:`mutual-recursion`.
+.. _complete-pragma:
+
+``COMPLETE`` pragmas
+--------------------
+
+The ``COMPLETE`` pragma is used to inform the pattern match checker that a
+certain set of patterns is complete and that any function which matches
+on all the specified patterns is total.
+
+The most common usage of ``COMPLETE`` pragmas is with
+:ref:`pattern-synonyms`.
+On its own, the checker is very naive and assumes that any match involving
+a pattern synonym will fail. As a result, any pattern match on a
+pattern synonym is regarded as
+incomplete unless the user adds a catch-all case.
+
+For example, the data types ``2 * A`` and ``A + A`` are isomorphic but some
+computations are more naturally expressed in terms of one or the other. To
+get the best of both worlds, we can choose one as our implementation and then
+provide a set of pattern synonyms so that users can use the other representation
+if they desire. We can then specify a ``COMPLETE`` pragma in order to
+inform the pattern match checker that a function which matches on both ``LeftChoice``
+and ``RightChoice`` is total.
+
+::
+
+ data Choice a = Choice Bool a
+
+ pattern LeftChoice :: a -> Choice a
+ pattern LeftChoice a = Choice False a
+
+ pattern RightChoice :: a -> Choice a
+ pattern RightChoice a = Choice True a
+
+ {-# COMPLETE LeftChoice, RightChoice #-}
+
+ foo :: Choice Int -> Int
+ foo (LeftChoice n) = n * 2
+ foo (RightChoice n) = n - 2
+
+``COMPLETE`` pragmas are only used by the pattern match checker. If a function
+definition matches on all the constructors specified in the pragma then the
+compiler will produce no warning.
+
+``COMPLETE`` pragmas can contain any data constructors or pattern synonyms
+which are in scope. Once defined, they are automatically imported and exported
+from modules. ``COMPLETE`` pragmas should be thought of as asserting a universal
+truth about a set of patterns and as a result, should not be used to silence
+context specific incomplete match warnings.
+
+When specifing a ``COMPLETE`` pragma, the result types of all patterns must
+be consistent with each other. This is a sanity check as it would be impossible
+to match on all the patterns if the types were inconsistent.
+
+The result type must also be unambiguous. Usually this can be inferred but
+when all the pattern synonyms in a group are polymorphic in the constructor
+the user must provide a type signature.
+
+::
+ class LL f where
+ go :: f a -> ()
+
+ instance LL [] where
+ go _ = ()
+
+ pattern T :: LL f => f a
+ pattern T <- (go -> ())
+
+ {-# COMPLETE T :: [] #-}
+
+ -- No warning
+ foo :: [a] -> Int
+ foo T = 5
+
.. _overlap-pragma:
``OVERLAPPING``, ``OVERLAPPABLE``, ``OVERLAPS``, and ``INCOHERENT`` pragmas
diff --git a/testsuite/tests/pmcheck/complete_sigs/Completesig03.hs b/testsuite/tests/pmcheck/complete_sigs/Completesig03.hs
new file mode 100644
index 0000000000..5c6752a6be
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/Completesig03.hs
@@ -0,0 +1,7 @@
+{-# OPTIONS_GHC -Wall #-}
+module Module where
+
+import Completesig03A
+
+foo :: A -> ()
+foo A = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/Completesig03.stderr b/testsuite/tests/pmcheck/complete_sigs/Completesig03.stderr
new file mode 100644
index 0000000000..05dcb92856
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/Completesig03.stderr
@@ -0,0 +1,2 @@
+[1 of 2] Compiling Completesig03A ( Completesig03A.hs, Completesig03A.o )
+[2 of 2] Compiling Module ( Completesig03.hs, Completesig03.o )
diff --git a/testsuite/tests/pmcheck/complete_sigs/Completesig03A.hs b/testsuite/tests/pmcheck/complete_sigs/Completesig03A.hs
new file mode 100644
index 0000000000..c1b83dfb06
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/Completesig03A.hs
@@ -0,0 +1,5 @@
+module Completesig03A where
+
+data A = A | B
+
+{-# COMPLETE A #-}
diff --git a/testsuite/tests/pmcheck/complete_sigs/Makefile b/testsuite/tests/pmcheck/complete_sigs/Makefile
new file mode 100644
index 0000000000..9101fbd40a
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/Makefile
@@ -0,0 +1,3 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
diff --git a/testsuite/tests/pmcheck/complete_sigs/all.T b/testsuite/tests/pmcheck/complete_sigs/all.T
new file mode 100644
index 0000000000..4e8c33d9be
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/all.T
@@ -0,0 +1,15 @@
+test('completesig01', normal, compile, [''])
+test('completesig02', normal, compile, [''])
+test('Completesig03', normal, multimod_compile, ['Completesig03', '-Wall'])
+test('completesig04', normal, compile_fail, [''])
+test('completesig05', normal, compile, [''])
+test('completesig06', normal, compile, [''])
+test('completesig07', normal, compile, [''])
+test('completesig08', normal, compile, [''])
+test('completesig09', normal, compile, [''])
+test('completesig10', normal, compile, [''])
+test('completesig11', normal, compile, [''])
+test('completesig12', normal, compile, [''])
+test('completesig13', normal, compile, [''])
+test('completesig14', normal, compile, [''])
+test('completesig15', normal, compile_fail, [''])
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig01.hs b/testsuite/tests/pmcheck/complete_sigs/completesig01.hs
new file mode 100644
index 0000000000..9598aa6462
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig01.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# OPTIONS_GHC -Wall #-}
+module Simple where
+
+pattern Foo :: ()
+pattern Foo = ()
+
+a :: () -> ()
+a Foo = ()
+
+data A = B | C | D
+
+{-# COMPLETE Foo #-}
+{-# COMPLETE B,C #-}
+{-# COMPLETE B #-}
+
+b :: A -> A
+b B = B
+b C = C
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig02.hs b/testsuite/tests/pmcheck/complete_sigs/completesig02.hs
new file mode 100644
index 0000000000..282378b2de
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig02.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# OPTIONS_GHC -Wall #-}
+module Empty where
+
+pattern Foo :: ()
+pattern Foo = ()
+
+a :: () -> ()
+a Foo = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig02.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig02.stderr
new file mode 100644
index 0000000000..25b24fd836
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig02.stderr
@@ -0,0 +1,4 @@
+
+completesig02.hs:10:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘a’: Patterns not matched: _
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig04.hs b/testsuite/tests/pmcheck/complete_sigs/completesig04.hs
new file mode 100644
index 0000000000..0d8eb81660
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig04.hs
@@ -0,0 +1,3 @@
+module TyMismatch where
+
+{-# COMPLETE Just, Left #-}
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig04.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig04.stderr
new file mode 100644
index 0000000000..b72cf6e9b4
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig04.stderr
@@ -0,0 +1,4 @@
+
+completesig04.hs:3:1: error:
+ • Cannot form a group of complete patterns from patterns ‘Just’ and ‘Left’ as they match different type constructors (‘Maybe’ resp. ‘Either’)
+ • In {-# COMPLETE Just, Left #-}
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig05.hs b/testsuite/tests/pmcheck/complete_sigs/completesig05.hs
new file mode 100644
index 0000000000..c277604714
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig05.hs
@@ -0,0 +1,14 @@
+{-# OPTIONS_GHC -Wall #-}
+module Completesig05 where
+
+-- Matching against multiple arguments
+
+data T = A | B | C
+data S = D | E | F
+
+{-# COMPLETE A, B #-}
+{-# COMPLETE D #-}
+
+match :: T -> S -> ()
+match A D = ()
+match B D = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig06.hs b/testsuite/tests/pmcheck/complete_sigs/completesig06.hs
new file mode 100644
index 0000000000..c3f1c75807
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig06.hs
@@ -0,0 +1,29 @@
+{-# OPTIONS_GHC -Wall #-}
+module Completesig06 where
+
+-- Some non-exhaustive examples
+
+data T = A | B | C
+data S = D | E | F
+
+{-# COMPLETE A, B #-}
+{-# COMPLETE D #-}
+
+m1 :: T -> ()
+m1 A = ()
+
+m2 :: T -> ()
+m2 B = ()
+m2 C = ()
+
+m3 :: T -> ()
+m3 C = ()
+
+m4 :: T -> S -> ()
+m4 A E = ()
+m4 A F = ()
+m4 B F = ()
+m4 B E = ()
+
+m5 :: T -> S -> ()
+m5 C D = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig06.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig06.stderr
new file mode 100644
index 0000000000..50bc9bfebf
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig06.stderr
@@ -0,0 +1,29 @@
+
+completesig06.hs:13:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘m1’: Patterns not matched: B
+
+completesig06.hs:16:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘m2’: Patterns not matched: A
+
+completesig06.hs:20:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘m3’:
+ Patterns not matched:
+ A
+ B
+
+completesig06.hs:23:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘m4’:
+ Patterns not matched:
+ B D
+ A D
+
+completesig06.hs:29:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘m5’:
+ Patterns not matched:
+ A _
+ B _
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig07.hs b/testsuite/tests/pmcheck/complete_sigs/completesig07.hs
new file mode 100644
index 0000000000..fb155a5460
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig07.hs
@@ -0,0 +1,24 @@
+{-# OPTIONS_GHC -Wall #-}
+module Completesig07 where
+
+-- Some overlapping examples
+
+data T = A | B | C
+data S = D | E | F
+
+{-# COMPLETE A, B #-}
+{-# COMPLETE D #-}
+
+m1 :: T -> ()
+m1 A = ()
+m1 A = ()
+m1 B = ()
+
+m2 :: T -> S -> ()
+m2 A D = ()
+m2 B D = ()
+m2 A D = ()
+
+m3 :: T -> ()
+m3 C = ()
+m3 C = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig07.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig07.stderr
new file mode 100644
index 0000000000..bf5edb9205
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig07.stderr
@@ -0,0 +1,11 @@
+
+completesig07.hs:23:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘m3’:
+ Patterns not matched:
+ A
+ B
+
+completesig07.hs:24:1: warning: [-Woverlapping-patterns (in -Wdefault)]
+ Pattern match is redundant
+ In an equation for ‘m3’: m3 C = ...
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig08.hs b/testsuite/tests/pmcheck/complete_sigs/completesig08.hs
new file mode 100644
index 0000000000..323b13984c
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig08.hs
@@ -0,0 +1,30 @@
+{-# OPTIONS_GHC -Wall #-}
+module Completesig08 where
+
+-- Some redundant examples
+
+data T = A | B | C
+data S = D | E | F
+
+{-# COMPLETE A, B #-}
+{-# COMPLETE D #-}
+
+m1 :: T -> ()
+m1 A = ()
+m1 B = ()
+m1 C = ()
+
+m2 :: T -> S -> ()
+m2 A D = ()
+m2 B D = ()
+m2 C D = ()
+
+m3 :: T -> S -> ()
+m3 A D = ()
+m3 B D = ()
+m3 A E = ()
+m3 A F = ()
+
+m4 :: S -> ()
+m4 D = ()
+m4 E = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig09.hs b/testsuite/tests/pmcheck/complete_sigs/completesig09.hs
new file mode 100644
index 0000000000..b02aefede4
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig09.hs
@@ -0,0 +1,13 @@
+{-# OPTIONS_GHC -Wall #-}
+module Completesig08 where
+
+-- Nested matching
+
+data T = A S | B
+data S = D | E
+
+{-# COMPLETE A #-}
+{-# COMPLETE D #-}
+
+m1 :: T -> ()
+m1 (A D) = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig10.hs b/testsuite/tests/pmcheck/complete_sigs/completesig10.hs
new file mode 100644
index 0000000000..66c446b6ef
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig10.hs
@@ -0,0 +1,33 @@
+{-# OPTIONS_GHC -Wall #-}
+module Completesig10 where
+
+-- Multiple competing COMPLETE sigs AHHH!!
+
+data T = A | B | C | D | E
+
+{-# COMPLETE A,B #-}
+{-# COMPLETE C,D #-}
+
+-- Completely overlapping
+m1 :: T -> ()
+m1 A = ()
+m1 B = ()
+m1 C = ()
+m1 D = ()
+
+-- Incomplete overlap
+m2 :: T -> ()
+m2 B = ()
+m2 D = ()
+
+-- Redudant incomplete overlap
+m3 :: T -> ()
+m3 B = ()
+m3 C = ()
+m3 D = ()
+
+-- One matches
+
+m4 :: T -> ()
+m4 C = ()
+m4 D = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig10.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig10.stderr
new file mode 100644
index 0000000000..3d97bb47d7
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig10.stderr
@@ -0,0 +1,4 @@
+
+completesig10.hs:20:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘m2’: Patterns not matched: A
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig11.hs b/testsuite/tests/pmcheck/complete_sigs/completesig11.hs
new file mode 100644
index 0000000000..69a386ca66
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig11.hs
@@ -0,0 +1,14 @@
+{-# OPTIONS_GHC -Wall #-}
+{-# LANGUAGE PatternSynonyms #-}
+module Completesig11 where
+
+data T = A | B | C
+{-# COMPLETE A,B #-}
+{-# COMPLETE A,C #-}
+
+pattern BS :: T
+pattern BS = B
+{-# COMPLETE A,BS #-}
+
+m1 :: T -> ()
+m1 A = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig11.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig11.stderr
new file mode 100644
index 0000000000..8107071008
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig11.stderr
@@ -0,0 +1,4 @@
+
+completesig11.hs:14:1: warning: [-Wincomplete-patterns (in -Wextra)]
+ Pattern match(es) are non-exhaustive
+ In an equation for ‘m1’: Patterns not matched: BS
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig12.hs b/testsuite/tests/pmcheck/complete_sigs/completesig12.hs
new file mode 100644
index 0000000000..a6bf400b42
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig12.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE GADTs, PatternSynonyms #-}
+{-# OPTIONS_GHC -Wall #-}
+module Completesig11 where
+data G a where
+ G1' :: G Int
+ G2' :: G Bool
+
+pattern G1 :: () => (a ~ Int) => G a
+pattern G1 = G1'
+
+pattern G2 :: () => (a ~ Bool) => G a
+pattern G2 = G2'
+
+{-# COMPLETE G1, G2 #-}
+
+fa :: G a -> Int -- exhaustive function
+fa G1 = 1
+fa G2 = 2
+
+fb :: G Int -> Int -- exhaustive function
+fb G1 = 1
+-- fb G2 = 2 -- inaccessible clause
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig13.hs b/testsuite/tests/pmcheck/complete_sigs/completesig13.hs
new file mode 100644
index 0000000000..ac87baf9f0
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig13.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# OPTIONS_GHC -Wall #-}
+module Completesig11 where
+
+class LL f where
+ go :: f a -> ()
+
+instance LL [] where
+ go _ = ()
+
+pattern T :: LL f => f a
+pattern T <- (go -> ())
+
+{-# COMPLETE T :: [] #-}
+
+foo :: [a] -> Int
+foo T = 5
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig14.hs b/testsuite/tests/pmcheck/complete_sigs/completesig14.hs
new file mode 100644
index 0000000000..00331c9370
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig14.hs
@@ -0,0 +1,11 @@
+{-# OPTIONS_GHC -Wall #-}
+module Completesig11 where
+
+data A = A | B
+
+{-# COMPLETE A, B #-}
+
+foo :: A -> ()
+foo A = ()
+foo B = ()
+foo A = ()
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig14.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig14.stderr
new file mode 100644
index 0000000000..06e9da88ce
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig14.stderr
@@ -0,0 +1,4 @@
+
+completesig14.hs:11:1: warning: [-Woverlapping-patterns (in -Wdefault)]
+ Pattern match is redundant
+ In an equation for ‘foo’: foo A = ...
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig15.hs b/testsuite/tests/pmcheck/complete_sigs/completesig15.hs
new file mode 100644
index 0000000000..5936379aa7
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig15.hs
@@ -0,0 +1,12 @@
+{-# OPTIONS_GHC -Wall #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE ViewPatterns #-}
+module Completesig15 where
+
+class C f where
+ foo :: f a -> ()
+
+pattern P :: C f => f a
+pattern P <- (foo -> ())
+
+{-# COMPLETE P #-}
diff --git a/testsuite/tests/pmcheck/complete_sigs/completesig15.stderr b/testsuite/tests/pmcheck/complete_sigs/completesig15.stderr
new file mode 100644
index 0000000000..3bff495ebe
--- /dev/null
+++ b/testsuite/tests/pmcheck/complete_sigs/completesig15.stderr
@@ -0,0 +1,4 @@
+
+completesig15.hs:12:1: error:
+ • A type signature must be provided for a set of polymorphic pattern synonyms.
+ • In {-# COMPLETE P #-}
diff --git a/testsuite/tests/pmcheck/should_compile/all.T b/testsuite/tests/pmcheck/should_compile/all.T
index 94d351e07a..3f4e0c8af8 100644
--- a/testsuite/tests/pmcheck/should_compile/all.T
+++ b/testsuite/tests/pmcheck/should_compile/all.T
@@ -40,7 +40,7 @@ test('T11303', normal, compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-
test('T11276', compile_timeout_multiplier(0.01), compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns +RTS -M1G -RTS'])
test('T11303b', compile_timeout_multiplier(0.01), compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns +RTS -M1G -RTS'])
test('T11374', compile_timeout_multiplier(0.01), compile, ['-fwarn-incomplete-patterns -fwarn-overlapping-patterns +RTS -M1G -RTS'])
-test('T11195', compile_timeout_multiplier(0.40), compile, ['-package ghc -fwarn-incomplete-patterns -fwarn-overlapping-patterns +RTS -M1G -RTS'])
+test('T11195', compile_timeout_multiplier(0.40), compile, ['-package ghc -fwarn-incomplete-patterns -fwarn-overlapping-patterns +RTS -M2G -RTS'])
# Other tests
test('pmc001', [], compile,