module Agda.TypeChecking.Coverage.Match
( Match(..), match, matchClause
, SplitPattern, SplitPatVar(..), fromSplitPatterns, toSplitPatterns
, toSplitPSubst, applySplitPSubst
, isTrivialPattern
, BlockingVar(..), BlockingVars, BlockedOnResult(..)
, setBlockingVarOverlap
, ApplyOrIApply(..)
) where
import Control.Monad.State
import Prelude hiding ( null )
import qualified Data.List as List
import Data.Maybe (mapMaybe, fromMaybe)
import Data.Semigroup ( Semigroup, (<>))
import Agda.Syntax.Abstract (IsProjP(..))
import Agda.Syntax.Common
import Agda.Syntax.Internal
import Agda.Syntax.Literal
import Agda.Syntax.Position
import Agda.TypeChecking.Monad
import Agda.TypeChecking.Monad.Builtin
import Agda.TypeChecking.Pretty ( PrettyTCM(..) )
import Agda.TypeChecking.Records
import Agda.TypeChecking.Reduce
import Agda.TypeChecking.Substitute
import Agda.Utils.Null
import Agda.Utils.Pretty ( Pretty(..), text, (<+>), cat , prettyList_ )
import Agda.Utils.Monad
import Agda.Utils.Impossible
data Match a
= Yes a
| No
| Block
{ blockedOnResult :: BlockedOnResult
, blockedOnVars :: BlockingVars
}
deriving (Functor)
data BlockedOnResult
= BlockedOnProj
{ blockedOnResultOverlap :: Bool
}
| BlockedOnApply
{ blockedOnResultIApply :: ApplyOrIApply
}
| NotBlockedOnResult
data ApplyOrIApply = IsApply | IsIApply
data BlockingVar = BlockingVar
{ blockingVarNo :: Nat
, blockingVarCons :: [ConHead]
, blockingVarLits :: [Literal]
, blockingVarOverlap :: Bool
, blockingVarLazy :: Bool
} deriving (Show)
type BlockingVars = [BlockingVar]
type SplitInstantiation = [(Nat,SplitPattern)]
match :: (MonadReduce m , HasConstInfo m , HasBuiltins m)
=> [Clause]
-> [NamedArg SplitPattern]
-> m (Match (Nat, SplitInstantiation))
match cs ps = foldr choice (return No) $ zipWith matchIt [0..] cs
where
matchIt :: (MonadReduce m , HasConstInfo m , HasBuiltins m)
=> Nat
-> Clause
-> m (Match (Nat, SplitInstantiation))
matchIt i c = fmap (i,) <$> matchClause ps c
data SplitPatVar = SplitPatVar
{ splitPatVarName :: PatVarName
, splitPatVarIndex :: Int
, splitExcludedLits :: [Literal]
} deriving (Show)
instance Pretty SplitPatVar where
prettyPrec _ x =
(text $ patVarNameToString (splitPatVarName x)) <>
(text $ "@" ++ show (splitPatVarIndex x)) <>
(ifNull (splitExcludedLits x) empty $ \lits ->
"\\{" <> prettyList_ lits <> "}")
instance PrettyTCM SplitPatVar where
prettyTCM = prettyTCM . var . splitPatVarIndex
type SplitPattern = Pattern' SplitPatVar
toSplitVar :: DBPatVar -> SplitPatVar
toSplitVar x = SplitPatVar (dbPatVarName x) (dbPatVarIndex x) []
fromSplitVar :: SplitPatVar -> DBPatVar
fromSplitVar x = DBPatVar (splitPatVarName x) (splitPatVarIndex x)
instance DeBruijn SplitPatVar where
deBruijnView x = deBruijnView (fromSplitVar x)
debruijnNamedVar n i = toSplitVar (debruijnNamedVar n i)
toSplitPatterns :: [NamedArg DeBruijnPattern] -> [NamedArg SplitPattern]
toSplitPatterns = (fmap . fmap . fmap . fmap) toSplitVar
fromSplitPatterns :: [NamedArg SplitPattern] -> [NamedArg DeBruijnPattern]
fromSplitPatterns = (fmap . fmap . fmap . fmap) fromSplitVar
type SplitPSubstitution = Substitution' SplitPattern
toSplitPSubst :: PatternSubstitution -> SplitPSubstitution
toSplitPSubst = (fmap . fmap) toSplitVar
fromSplitPSubst :: SplitPSubstitution -> PatternSubstitution
fromSplitPSubst = (fmap . fmap) fromSplitVar
applySplitPSubst :: (Subst Term a) => SplitPSubstitution -> a -> a
applySplitPSubst = applyPatSubst . fromSplitPSubst
instance Subst SplitPattern SplitPattern where
applySubst IdS p = p
applySubst rho p = case p of
VarP i x ->
usePatternInfo i $
useName (splitPatVarName x) $
useExcludedLits (splitExcludedLits x) $
lookupS rho $ splitPatVarIndex x
DotP i u -> DotP i $ applySplitPSubst rho u
ConP c ci ps -> ConP c ci $ applySubst rho ps
DefP i q ps -> DefP i q $ applySubst rho ps
LitP{} -> p
ProjP{} -> p
IApplyP i l r x ->
useEndPoints (applySplitPSubst rho l) (applySplitPSubst rho r) $
usePatternInfo i $
useName (splitPatVarName x) $
useExcludedLits (splitExcludedLits x) $
lookupS rho $ splitPatVarIndex x
where
useEndPoints :: Term -> Term -> SplitPattern -> SplitPattern
useEndPoints l r (VarP o x) = IApplyP o l r x
useEndPoints l r (IApplyP o _ _ x) = IApplyP o l r x
useEndPoints l r x = __IMPOSSIBLE__
useName :: PatVarName -> SplitPattern -> SplitPattern
useName n (VarP o x)
| isUnderscore (splitPatVarName x)
= VarP o $ x { splitPatVarName = n }
useName _ x = x
useExcludedLits :: [Literal] -> SplitPattern -> SplitPattern
useExcludedLits lits = \case
(VarP o x) -> VarP o $ x
{ splitExcludedLits = lits ++ splitExcludedLits x }
p -> p
isTrivialPattern :: (HasConstInfo m) => Pattern' a -> m Bool
isTrivialPattern p = case p of
VarP{} -> return True
DotP{} -> return True
ConP c i ps -> andM $ ((conPLazy i ||) <$> isEtaCon (conName c))
: (map (isTrivialPattern . namedArg) ps)
DefP{} -> return False
LitP{} -> return False
ProjP{} -> return False
IApplyP{} -> return True
type MatchResult = Match SplitInstantiation
instance Pretty BlockingVar where
pretty (BlockingVar i cs ls o l) = cat
[ text ("variable " ++ show i)
, if null cs then empty else " blocked on constructors" <+> pretty cs
, if null ls then empty else " blocked on literals" <+> pretty ls
, if o then " (overlapping)" else empty
, if l then " (lazy)" else empty
]
yes :: Monad m => a -> m (Match a)
yes = return . Yes
no :: Monad m => m (Match a)
no = return No
blockedOnConstructor :: Monad m => Nat -> ConHead -> ConPatternInfo -> m (Match a)
blockedOnConstructor i c ci = return $ Block NotBlockedOnResult [BlockingVar i [c] [] False $ conPLazy ci]
blockedOnLiteral :: Monad m => Nat -> Literal -> m (Match a)
blockedOnLiteral i l = return $ Block NotBlockedOnResult [BlockingVar i [] [l] False False]
blockedOnProjection :: Monad m => m (Match a)
blockedOnProjection = return $ Block (BlockedOnProj False) []
blockedOnApplication :: Monad m => ApplyOrIApply -> m (Match a)
blockedOnApplication b = return $ Block (BlockedOnApply b) []
setBlockingVarOverlap :: BlockingVar -> BlockingVar
setBlockingVarOverlap = \x -> x { blockingVarOverlap = True }
overlapping :: BlockingVars -> BlockingVars
overlapping = map setBlockingVarOverlap
zipBlockingVars :: BlockingVars -> BlockingVars -> BlockingVars
zipBlockingVars xs ys = map upd xs
where
upd (BlockingVar x cons lits o l) = case List.find ((x ==) . blockingVarNo) ys of
Just (BlockingVar _ cons' lits' o' l') -> BlockingVar x (cons ++ cons') (lits ++ lits') (o || o') (l || l')
Nothing -> BlockingVar x cons lits True l
setBlockedOnResultOverlap :: BlockedOnResult -> BlockedOnResult
setBlockedOnResultOverlap b = case b of
BlockedOnProj{} -> b { blockedOnResultOverlap = True }
BlockedOnApply{} -> b
NotBlockedOnResult{} -> b
anyBlockedOnResult :: BlockedOnResult -> BlockedOnResult -> BlockedOnResult
anyBlockedOnResult b1 b2 = case (b1,b2) of
(NotBlockedOnResult , b2 ) -> b2
(b1 , NotBlockedOnResult) -> b1
(_ , _ ) -> __IMPOSSIBLE__
choiceBlockedOnResult :: BlockedOnResult -> BlockedOnResult -> BlockedOnResult
choiceBlockedOnResult b1 b2 = case (b1,b2) of
(NotBlockedOnResult , _ ) -> NotBlockedOnResult
(BlockedOnProj o1 , BlockedOnProj o2 ) -> BlockedOnProj (o1 || o2)
(BlockedOnProj _ , _ ) -> BlockedOnProj True
(BlockedOnApply b , _ ) -> BlockedOnApply b
choice :: Monad m => m (Match a) -> m (Match a) -> m (Match a)
choice m m' = m >>= \case
Yes a -> yes a
Block r xs -> m' >>= \case
Block s ys -> return $ Block (choiceBlockedOnResult r s) $ zipBlockingVars xs ys
Yes _ -> return $ Block (setBlockedOnResultOverlap r) $ overlapping xs
No -> return $ Block r xs
No -> m'
matchClause
:: (MonadReduce m , HasConstInfo m , HasBuiltins m)
=> [NamedArg SplitPattern]
-> Clause
-> m MatchResult
matchClause qs c = matchPats (namedClausePats c) qs
matchPats
:: (MonadReduce m , HasConstInfo m , HasBuiltins m, DeBruijn a)
=> [NamedArg (Pattern' a)]
-> [NamedArg SplitPattern]
-> m MatchResult
matchPats [] [] = yes []
matchPats (p:ps) (q:qs) =
matchPat (namedArg p) (namedArg q) `combine` matchPats ps qs
matchPats [] qs@(_:_) = case mapMaybe isProjP qs of
[] -> yes []
_ -> no
matchPats (p:ps) [] = case isProjP p of
Just{} -> blockedOnProjection
Nothing -> blockedOnApplication (case namedArg p of IApplyP{} -> IsIApply; _ -> IsApply)
combine :: (Monad m, Semigroup a) => m (Match a) -> m (Match a) -> m (Match a)
combine m m' = m >>= \case
Yes a -> m' >>= \case
Yes b -> yes (a <> b)
y -> return y
No -> no
x@(Block r xs) -> m' >>= \case
No -> no
Block s ys -> return $ Block (anyBlockedOnResult r s) (xs ++ ys)
Yes{} -> return x
matchPat
:: (MonadReduce m , HasConstInfo m , HasBuiltins m, DeBruijn a)
=> Pattern' a
-> SplitPattern
-> m MatchResult
matchPat p q = case p of
VarP _ x -> yes [(fromMaybe __IMPOSSIBLE__ (deBruijnView x),q)]
DotP{} -> yes []
p@(LitP _ l) -> case q of
VarP _ x -> if l `elem` splitExcludedLits x
then no
else blockedOnLiteral (splitPatVarIndex x) l
_ -> isLitP q >>= \case
Just l' -> if l == l' then yes [] else no
Nothing -> no
ProjP _ d -> case q of
ProjP _ d' -> do
d <- getOriginalProjection d
if d == d' then yes [] else no
_ -> __IMPOSSIBLE__
IApplyP _ _ _ x -> yes [(fromMaybe __IMPOSSIBLE__ (deBruijnView x),q)]
ConP c ci ps -> unDotP q >>= unLitP >>= \case
VarP _ x -> blockedOnConstructor (splitPatVarIndex x) c ci
ConP c' i qs
| c == c' -> matchPats ps qs
| otherwise -> no
DotP o t -> no
DefP{} -> no
LitP{} -> __IMPOSSIBLE__
ProjP{} -> __IMPOSSIBLE__
IApplyP _ _ _ x -> blockedOnConstructor (splitPatVarIndex x) c ci
DefP o c ps -> unDotP q >>= \case
VarP _ x -> __IMPOSSIBLE__
ConP c' i qs -> no
DotP o t -> no
LitP{} -> no
DefP o c' qs
| c == c' -> matchPats ps qs
| otherwise -> no
ProjP{} -> __IMPOSSIBLE__
IApplyP _ _ _ x -> __IMPOSSIBLE__
unDotP :: (MonadReduce m, DeBruijn a) => Pattern' a -> m (Pattern' a)
unDotP (DotP o v) = reduce v >>= \case
Var i [] -> return $ deBruijnVar i
Con c _ vs -> do
let ps = map (fmap $ unnamed . DotP o) $ fromMaybe __IMPOSSIBLE__ $ allApplyElims vs
return $ ConP c noConPatternInfo ps
Lit l -> return $ LitP (PatternInfo PatODot []) l
v -> return $ dotP v
unDotP p = return p
isLitP :: (MonadReduce m, HasBuiltins m) => Pattern' a -> m (Maybe Literal)
isLitP (LitP _ l) = return $ Just l
isLitP (DotP _ u) = reduce u >>= \case
Lit l -> return $ Just l
_ -> return $ Nothing
isLitP (ConP c ci []) = do
Con zero _ [] <- fromMaybe __IMPOSSIBLE__ <$> getBuiltin' builtinZero
if | c == zero -> return $ Just $ LitNat (getRange c) 0
| otherwise -> return Nothing
isLitP (ConP c ci [a]) | visible a && isRelevant a = do
Con suc _ [] <- fromMaybe __IMPOSSIBLE__ <$> getBuiltin' builtinSuc
if | c == suc -> fmap inc <$> isLitP (namedArg a)
| otherwise -> return Nothing
where
inc :: Literal -> Literal
inc (LitNat r n) = LitNat (fuseRange c r) $ n + 1
inc _ = __IMPOSSIBLE__
isLitP _ = return Nothing
unLitP :: HasBuiltins m => Pattern' a -> m (Pattern' a)
unLitP (LitP info l@(LitNat _ n)) | n >= 0 = do
Con c ci es <- constructorForm' (fromMaybe __IMPOSSIBLE__ <$> getBuiltin' builtinZero)
(fromMaybe __IMPOSSIBLE__ <$> getBuiltin' builtinSuc)
(Lit l)
let toP (Apply (Arg i (Lit l))) = Arg i (LitP info l)
toP _ = __IMPOSSIBLE__
cpi = noConPatternInfo { conPInfo = info }
return $ ConP c cpi $ map (fmap unnamed . toP) es
unLitP p = return p