{-# LANGUAGE BangPatterns, DeriveGeneric, FlexibleInstances,
MultiParamTypeClasses, PatternGuards #-}
{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
{-# OPTIONS_GHC -fwarn-unused-imports #-}
module Idris.Core.Evaluate(normalise, normaliseTrace, normaliseC,
normaliseAll, normaliseBlocking, toValue, quoteTerm,
rt_simplify, simplify, inlineSmall,
specialise, unfold, convEq, convEq',
Def(..), CaseInfo(..), CaseDefs(..),
Accessibility(..), Injectivity, Totality(..), TTDecl, PReason(..), MetaInformation(..),
Context, initContext, ctxtAlist, next_tvar,
addToCtxt, setAccess, setInjective, setTotal, setRigCount,
setMetaInformation, addCtxtDef, addTyDecl,
addDatatype, addCasedef, simplifyCasedef, addOperator,
lookupNames, lookupTyName, lookupTyNameExact, lookupTy, lookupTyExact,
lookupP, lookupP_all, lookupDef, lookupNameDef, lookupDefExact, lookupDefAcc, lookupDefAccExact, lookupVal,
mapDefCtxt, tcReducible,
lookupTotal, lookupTotalExact, lookupInjectiveExact,
lookupRigCount, lookupRigCountExact,
lookupNameTotal, lookupMetaInformation, lookupTyEnv, isTCDict,
isCanonical, isDConName, canBeDConName, isTConName, isConName, isFnName,
conGuarded,
Value(..), Quote(..), initEval, uniqueNameCtxt, uniqueBindersCtxt, definitions,
visibleDefinitions,
isUniverse, linearCheck, linearCheckArg) where
import Idris.Core.CaseTree
import Idris.Core.TT
import Control.Monad.State
import Data.List
import Data.Maybe (listToMaybe)
import GHC.Generics (Generic)
import qualified Data.Map.Strict as Map
data EvalState = ES { limited :: [(Name, Int)],
nexthole :: Int,
blocking :: Bool }
deriving Show
type Eval a = State EvalState a
data EvalOpt = Spec
| Simplify Bool
| AtREPL
| RunTT
| Unfold
deriving (Show, Eq)
initEval = ES [] 0 False
data Value = VP NameType Name Value
| VV Int
| VBind Bool Name (Binder Value) (Value -> Eval Value)
| VBLet RigCount Int Name Value Value Value
| VApp Value Value
| VType UExp
| VUType Universe
| VErased
| VImpossible
| VConstant Const
| VProj Value Int
| VTmp Int
canonical :: Value -> Bool
canonical (VP (DCon _ _ _) _ _) = True
canonical (VP (TCon _ _) _ _) = True
canonical (VApp f a) = canonical f
canonical (VConstant _) = True
canonical (VType _) = True
canonical (VUType _) = True
canonical VErased = True
canonical _ = False
instance Show Value where
show x = show $ evalState (quote 100 x) initEval
instance Show (a -> b) where
show x = "<<fn>>"
normaliseC :: Context -> Env -> TT Name -> TT Name
normaliseC ctxt env t
= evalState (do val <- eval False ctxt [] (map finalEntry env) t []
quote 0 val) initEval
normaliseAll :: Context -> Env -> TT Name -> TT Name
normaliseAll ctxt env t
= evalState (do val <- eval False ctxt [] (map finalEntry env) t [AtREPL]
quote 0 val) initEval
normaliseBlocking :: Context -> Env -> [Name] -> TT Name -> TT Name
normaliseBlocking ctxt env blocked t
= evalState (do val <- eval False ctxt (map (\n -> (n, 0)) blocked)
(map finalEntry env) t [AtREPL]
quote 0 val) initEval
normalise :: Context -> Env -> TT Name -> TT Name
normalise = normaliseTrace False
normaliseTrace :: Bool -> Context -> Env -> TT Name -> TT Name
normaliseTrace tr ctxt env t
= evalState (do val <- eval tr ctxt [] (map finalEntry env) (finalise t) []
quote 0 val) initEval
toValue :: Context -> Env -> TT Name -> Value
toValue ctxt env t
= evalState (eval False ctxt [] (map finalEntry env) t []) initEval
quoteTerm :: Value -> TT Name
quoteTerm val = evalState (quote 0 val) initEval
specialise :: Context -> Env -> [(Name, Int)] -> TT Name ->
(TT Name, [(Name, Int)])
specialise ctxt env limits t
= let (tm, st) =
runState (do val <- eval False ctxt []
(map finalEntry env) (finalise t)
[Spec]
quote 0 val) (initEval { limited = limits }) in
(tm, limited st)
simplify :: Context -> Env -> TT Name -> TT Name
simplify ctxt env t
= evalState (do val <- eval False ctxt [(sUN "lazy", 0),
(sUN "force", 0),
(sUN "Force", 0),
(sUN "assert_smaller", 0),
(sUN "assert_total", 0),
(sUN "par", 0),
(sUN "prim__syntactic_eq", 0),
(sUN "fork", 0)]
(map finalEntry env) (finalise t)
[Simplify True]
quote 0 val) initEval
inlineSmall :: Context -> Env -> TT Name -> TT Name
inlineSmall ctxt env t
= evalState (do val <- eval False ctxt []
(map finalEntry env) (finalise t)
[Simplify False]
quote 0 val) initEval
rt_simplify :: Context -> Env -> TT Name -> TT Name
rt_simplify ctxt env t
= evalState (do val <- eval False ctxt [(sUN "lazy", 0),
(sUN "force", 0),
(sUN "Force", 0),
(sUN "par", 0),
(sUN "prim__syntactic_eq", 0),
(sUN "prim_fork", 0)]
(map finalEntry env) (finalise t)
[RunTT]
quote 0 val) initEval
unfold :: Context -> Env -> [(Name, Int)] -> TT Name -> TT Name
unfold ctxt env ns t
= evalState (do val <- eval False ctxt ns
(map finalEntry env) (finalise t)
[Unfold]
quote 0 val) initEval
finalEntry :: (Name, RigCount, Binder (TT Name)) -> (Name, RigCount, Binder (TT Name))
finalEntry (n, r, b) = (n, r, fmap finalise b)
usable :: Bool
-> Bool
-> Int
-> Name -> [(Name, Int)] -> Eval (Bool, [(Name, Int)])
usable False uf depthlimit n [] = return (True, [])
usable True uf depthlimit n ns
= do ES ls num b <- get
if b then return (False, ns)
else case lookup n ls of
Just 0 -> return (False, ns)
Just i -> return (True, ns)
_ -> return (False, ns)
usable False uf depthlimit n ns
= case lookup n ns of
Just 0 -> return (False, ns)
Just i -> return $ (True, (n, abs (i-1)) : filter (\ (n', _) -> n/=n') ns)
_ -> return $ if uf
then (False, ns)
else (True, (n, depthlimit) : filter (\ (n', _) -> n/=n') ns)
fnCount :: Int -> Name -> Eval ()
fnCount inc n
= do ES ls num b <- get
case lookup n ls of
Just i -> do put $ ES ((n, (i - inc)) :
filter (\ (n', _) -> n/=n') ls) num b
_ -> return ()
setBlock :: Bool -> Eval ()
setBlock b = do ES ls num _ <- get
put (ES ls num b)
deduct = fnCount 1
reinstate = fnCount (-1)
eval :: Bool -> Context -> [(Name, Int)] -> Env -> TT Name ->
[EvalOpt] -> Eval Value
eval traceon ctxt ntimes genv tm opts = ev ntimes [] True [] tm where
spec = Spec `elem` opts
simpl = Simplify True `elem` opts || Simplify False `elem` opts
simpl_inline = Simplify False `elem` opts
runtime = RunTT `elem` opts
atRepl = AtREPL `elem` opts
unfold = Unfold `elem` opts
noFree = all canonical . map snd
blockSimplify (CaseInfo inl always dict) n stk
| runtime
= if always then False
else not (inl || dict) || elem n stk
| simpl
= (not inl || elem n stk)
|| (n == sUN "prim__syntactic_eq")
| otherwise = False
getCases cd | simpl = cases_compiletime cd
| runtime = cases_runtime cd
| otherwise = cases_compiletime cd
ev ntimes stk top env (P _ n ty)
| Just (Let _ t v) <- lookupBinder n genv = ev ntimes stk top env v
ev ntimes_in stk top env (P Ref n ty)
= do let limit = if simpl then 100 else 10000
(u, ntimes) <- usable spec unfold limit n ntimes_in
let red = u && (tcReducible n ctxt || spec || (atRepl && noFree env)
|| runtime || unfold
|| sUN "assert_total" `elem` stk)
if red then
do let val = lookupDefAccExact n (spec || unfold || (atRepl && noFree env) || runtime) ctxt
case val of
Just (Function _ tm, Public) ->
ev ntimes (n:stk) True env tm
Just (TyDecl nt ty, _) -> do vty <- ev ntimes stk True env ty
return $ VP nt n vty
Just (CaseOp ci _ _ _ _ cd, acc)
| (acc == Public || acc == Hidden) &&
null (fst (cases_compiletime cd)) ->
let (ns, tree) = getCases cd in
if blockSimplify ci n stk
then liftM (VP Ref n) (ev ntimes stk top env ty)
else
do c <- evCase ntimes n (n:stk) top env ns [] tree
case c of
(Nothing, _) -> liftM (VP Ref n) (ev ntimes stk top env ty)
(Just v, _) -> return v
_ -> liftM (VP Ref n) (ev ntimes stk top env ty)
else liftM (VP Ref n) (ev ntimes stk top env ty)
ev ntimes stk top env (P nt n ty)
= liftM (VP nt n) (ev ntimes stk top env ty)
ev ntimes stk top env (V i)
| i < length env && i >= 0 = return $ snd (env !! i)
| otherwise = return $ VV i
ev ntimes stk top env (Bind n (Let c t v) sc)
| (not (runtime || simpl_inline || unfold)) || occurrences n sc < 2
= do v' <- ev ntimes stk top env v
sc' <- ev ntimes stk top ((n, v') : env) sc
wknV (-1) sc'
| otherwise
= do t' <- ev ntimes stk top env t
v' <- ev ntimes stk top env v
hs <- get
let vd = nexthole hs
put (hs { nexthole = vd + 1 })
sc' <- ev ntimes stk top ((n, VP Bound (sMN vd "vlet") VErased) : env) sc
return $ VBLet c vd n t' v' sc'
ev ntimes stk top env (Bind n (NLet t v) sc)
= do t' <- ev ntimes stk top env (finalise t)
v' <- ev ntimes stk top env (finalise v)
sc' <- ev ntimes stk top ((n, v') : env) sc
return $ VBind True n (Let RigW t' v') (\x -> return sc')
ev ntimes stk top env (Bind n b sc)
= do b' <- vbind env b
let n' = uniqueName n (map fstEnv genv ++ map fst env)
return $ VBind True
n' b' (\x -> ev ntimes stk False ((n', x):env) sc)
where vbind env t
= fmapMB (\tm -> ev ntimes stk top env (finalise tm)) t
ev ntimes stk top env
(App _ (App _ (App _ d@(P _ (UN dly) _) l@(P _ (UN lco) _)) t) arg)
| dly == txt "Delay" && lco == txt "Infinite" && not (unfold || simpl)
= do let (f, _) = unApply arg
let ntimes' = case f of
P _ fn _ -> (fn, 0) : ntimes
_ -> ntimes
when spec $ setBlock True
d' <- ev ntimes' stk False env d
l' <- ev ntimes' stk False env l
t' <- ev ntimes' stk False env t
arg' <- ev ntimes' stk False env arg
when spec $ setBlock False
evApply ntimes' stk top env [l',t',arg'] d'
ev ntimes stk top env (App _ (App _ (P _ n@(UN at) _) _) arg)
| Just (CaseOp _ _ _ _ _ _, _) <- lookupDefAccExact n (spec || (atRepl && noFree env)|| runtime) ctxt,
at == txt "assert_total" && not (simpl || unfold)
= ev ntimes (n : stk) top env arg
ev ntimes stk top env (App _ f a)
= do f' <- ev ntimes stk False env f
a' <- ev ntimes stk False env a
evApply ntimes stk top env [a'] f'
ev ntimes stk top env (Proj t i)
= do
t' <- ev ntimes stk top env t
return (doProj t' (getValArgs t'))
where doProj t' (VP (DCon _ _ _) _ _, args)
| i >= 0 && i < length args = args!!i
doProj t' _ = VProj t' i
ev ntimes stk top env (Constant c) = return $ VConstant c
ev ntimes stk top env Erased = return VErased
ev ntimes stk top env Impossible = return VImpossible
ev ntimes stk top env (Inferred tm) = ev ntimes stk top env tm
ev ntimes stk top env (TType i) = return $ VType i
ev ntimes stk top env (UType u) = return $ VUType u
evApply ntimes stk top env args (VApp f a)
= evApply ntimes stk top env (a:args) f
evApply ntimes stk top env args f
= apply ntimes stk top env f args
apply ntimes stk top env (VBind True n (Lam _ t) sc) (a:as)
= do a' <- sc a
app <- apply ntimes stk top env a' as
wknV 1 app
apply ntimes_in stk top env f@(VP Ref n ty) args
= do let limit = if simpl then 100 else 10000
(u, ntimes) <- usable spec unfold limit n ntimes_in
let red = u && (tcReducible n ctxt || spec || (atRepl && noFree env)
|| unfold || runtime
|| sUN "assert_total" `elem` stk)
if red then
do let val = lookupDefAccExact n (spec || unfold || (atRepl && noFree env) || runtime) ctxt
case val of
Just (CaseOp ci _ _ _ _ cd, acc)
| acc == Public || acc == Hidden ->
let (ns, tree) = getCases cd in
if blockSimplify ci n stk
then return $ unload env (VP Ref n ty) args
else
do c <- evCase ntimes n (n:stk) top env ns args tree
case c of
(Nothing, _) -> return $ unload env (VP Ref n ty) args
(Just v, rest) -> evApply ntimes stk top env rest v
Just (Operator _ i op, _) ->
if (i <= length args)
then case op (take i args) of
Nothing -> return $ unload env (VP Ref n ty) args
Just v -> evApply ntimes stk top env (drop i args) v
else return $ unload env (VP Ref n ty) args
_ -> case args of
[] -> return f
_ -> return $ unload env f args
else case args of
(a : as) -> return $ unload env f (a:as)
[] -> return f
apply ntimes stk top env f (a:as) = return $ unload env f (a:as)
apply ntimes stk top env f [] = return f
unload :: [(Name, Value)] -> Value -> [Value] -> Value
unload env f [] = f
unload env f (a:as) = unload env (VApp f a) as
evCase ntimes n stk top env ns args tree
| length ns <= length args
= do let args' = take (length ns) args
let rest = drop (length ns) args
when spec $ deduct n
t <- evTree ntimes stk top env (zip ns args') tree
when spec $ case t of
Nothing -> reinstate n
Just _ -> return ()
return (t, rest)
| otherwise = return (Nothing, args)
evTree :: [(Name, Int)] -> [Name] -> Bool ->
[(Name, Value)] -> [(Name, Value)] -> SC -> Eval (Maybe Value)
evTree ntimes stk top env amap (UnmatchedCase str) = return Nothing
evTree ntimes stk top env amap (STerm tm)
= do let etm = pToVs (map fst amap) tm
etm' <- ev ntimes stk (not (conHeaded tm))
(amap ++ env) etm
return $ Just etm'
evTree ntimes stk top env amap (ProjCase t alts)
= do t' <- ev ntimes stk top env t
doCase ntimes stk top env amap t' alts
evTree ntimes stk top env amap (Case _ n alts)
= case lookup n amap of
Just v -> doCase ntimes stk top env amap v alts
_ -> return Nothing
evTree ntimes stk top env amap ImpossibleCase = return Nothing
doCase ntimes stk top env amap v alts =
do c <- chooseAlt env v (getValArgs v) alts amap
case c of
Just (altmap, sc) -> evTree ntimes stk top env altmap sc
_ -> do c' <- chooseAlt' ntimes stk env v (getValArgs v) alts amap
case c' of
Just (altmap, sc) -> evTree ntimes stk top env altmap sc
_ -> return Nothing
conHeaded tm@(App _ _ _)
| (P (DCon _ _ _) _ _, args) <- unApply tm = True
conHeaded t = False
chooseAlt' ntimes stk env _ (f, args) alts amap
= do f' <- apply ntimes stk True env f args
chooseAlt env f' (getValArgs f')
alts amap
chooseAlt :: [(Name, Value)] -> Value -> (Value, [Value]) -> [CaseAlt] ->
[(Name, Value)] ->
Eval (Maybe ([(Name, Value)], SC))
chooseAlt env _ (VP (DCon i a _) _ _, args) alts amap
| Just (ns, sc) <- findTag i alts = return $ Just (updateAmap (zip ns args) amap, sc)
| Just v <- findDefault alts = return $ Just (amap, v)
chooseAlt env _ (VP (TCon i a) _ _, args) alts amap
| Just (ns, sc) <- findTag i alts
= return $ Just (updateAmap (zip ns args) amap, sc)
| Just v <- findDefault alts = return $ Just (amap, v)
chooseAlt env _ (VConstant c, []) alts amap
| Just v <- findConst c alts = return $ Just (amap, v)
| Just (n', sub, sc) <- findSuc c alts
= return $ Just (updateAmap [(n',sub)] amap, sc)
| Just v <- findDefault alts = return $ Just (amap, v)
chooseAlt env _ (VP _ n _, args) alts amap
| Just (ns, sc) <- findFn n alts = return $ Just (updateAmap (zip ns args) amap, sc)
chooseAlt env _ (VBind _ _ (Pi _ i s k) t, []) alts amap
| Just (ns, sc) <- findFn (sUN "->") alts
= do t' <- t (VV 0)
return $ Just (updateAmap (zip ns [s, t']) amap, sc)
chooseAlt _ _ _ alts amap
| Just v <- findDefault alts
= if (any fnCase alts)
then return $ Just (amap, v)
else return Nothing
| otherwise = return Nothing
fnCase (FnCase _ _ _) = True
fnCase _ = False
updateAmap newm amap
= newm ++ filter (\ (x, _) -> not (elem x (map fst newm))) amap
findTag i [] = Nothing
findTag i (ConCase n j ns sc : xs) | i == j = Just (ns, sc)
findTag i (_ : xs) = findTag i xs
findFn fn [] = Nothing
findFn fn (FnCase n ns sc : xs) | fn == n = Just (ns, sc)
findFn fn (_ : xs) = findFn fn xs
findDefault [] = Nothing
findDefault (DefaultCase sc : xs) = Just sc
findDefault (_ : xs) = findDefault xs
findSuc c [] = Nothing
findSuc (BI val) (SucCase n sc : _)
| val /= 0 = Just (n, VConstant (BI (val - 1)), sc)
findSuc c (_ : xs) = findSuc c xs
findConst c [] = Nothing
findConst c (ConstCase c' v : xs) | c == c' = Just v
findConst (AType (ATInt ITNative)) (ConCase n 1 [] v : xs) = Just v
findConst (AType ATFloat) (ConCase n 2 [] v : xs) = Just v
findConst (AType (ATInt ITChar)) (ConCase n 3 [] v : xs) = Just v
findConst StrType (ConCase n 4 [] v : xs) = Just v
findConst (AType (ATInt ITBig)) (ConCase n 6 [] v : xs) = Just v
findConst (AType (ATInt (ITFixed ity))) (ConCase n tag [] v : xs)
| tag == 7 + fromEnum ity = Just v
findConst c (_ : xs) = findConst c xs
getValArgs tm = getValArgs' tm []
getValArgs' (VApp f a) as = getValArgs' f (a:as)
getValArgs' f as = (f, as)
instance Eq Value where
(==) x y = getTT x == getTT y
where getTT v = evalState (quote 0 v) initEval
class Quote a where
quote :: Int -> a -> Eval (TT Name)
instance Quote Value where
quote i (VP nt n v) = liftM (P nt n) (quote i v)
quote i (VV x) = return $ V x
quote i (VBind _ n b sc) = do sc' <- sc (VTmp i)
b' <- quoteB b
liftM (Bind n b') (quote (i+1) sc')
where quoteB t = fmapMB (quote i) t
quote i (VBLet c vd n t v sc)
= do sc' <- quote i sc
t' <- quote i t
v' <- quote i v
let sc'' = pToV (sMN vd "vlet") (addBinder sc')
return (Bind n (Let c t' v') sc'')
quote i (VApp f a) = liftM2 (App MaybeHoles) (quote i f) (quote i a)
quote i (VType u) = return (TType u)
quote i (VUType u) = return (UType u)
quote i VErased = return Erased
quote i VImpossible = return Impossible
quote i (VProj v j) = do v' <- quote i v
return (Proj v' j)
quote i (VConstant c) = return $ Constant c
quote i (VTmp x) = return $ V (i - x - 1)
wknV :: Int -> Value -> Eval Value
wknV i (VV x) | x >= i = return $ VV (x - 1)
wknV i (VBind red n b sc) = do b' <- fmapMB (wknV i) b
return $ VBind red n b' (\x -> do x' <- sc x
wknV (i + 1) x')
wknV i (VApp f a) = liftM2 VApp (wknV i f) (wknV i a)
wknV i t = return t
isUniverse :: Term -> Bool
isUniverse (TType _) = True
isUniverse (UType _) = True
isUniverse _ = False
isUsableUniverse :: Term -> Bool
isUsableUniverse (UType NullType) = False
isUsableUniverse x = isUniverse x
convEq' ctxt hs x y = evalStateT (convEq ctxt hs x y) (0, [])
convEq :: Context -> [Name] -> TT Name -> TT Name -> StateT UCs TC Bool
convEq ctxt holes topx topy = ceq [] topx topy where
ceq :: [(Name, Name)] -> TT Name -> TT Name -> StateT UCs TC Bool
ceq ps (P xt x _) (P yt y _)
| x `elem` holes || y `elem` holes = return True
| x == y || (x, y) `elem` ps || (y,x) `elem` ps = return True
| otherwise = sameDefs ps x y
ceq ps (Bind n (PVar _ t) sc) y = ceq ps sc y
ceq ps x (Bind n (PVar _ t) sc) = ceq ps x sc
ceq ps (Bind n (PVTy t) sc) y = ceq ps sc y
ceq ps x (Bind n (PVTy t) sc) = ceq ps x sc
ceq ps (V x) (V y) = return (x == y)
ceq ps (V x) (P _ y _)
| x >= 0 && length ps > x = return (fst (ps!!x) == y)
| otherwise = return False
ceq ps (P _ x _) (V y)
| y >= 0 && length ps > y = return (x == snd (ps!!y))
| otherwise = return False
ceq ps (Bind n xb xs) (Bind n' yb ys)
= liftM2 (&&) (ceqB ps xb yb) (ceq ((n,n'):ps) xs ys)
where
ceqB ps (Let c v t) (Let c' v' t') = liftM2 (&&) (ceq ps v v') (ceq ps t t')
ceqB ps (Guess v t) (Guess v' t') = liftM2 (&&) (ceq ps v v') (ceq ps t t')
ceqB ps (Pi r i v t) (Pi r' i' v' t') = liftM2 (&&) (ceq ps v v') (ceq ps t t')
ceqB ps b b' = ceq ps (binderTy b) (binderTy b')
ceq ps x (Bind n (Lam _ t) (App _ y (V 0)))
= ceq ps x (substV (P Bound n t) y)
ceq ps (Bind n (Lam _ t) (App _ x (V 0))) y
= ceq ps (substV (P Bound n t) x) y
ceq ps x (Bind n (Lam _ t) (App _ y (P Bound n' _)))
| n == n' = ceq ps x y
ceq ps (Bind n (Lam _ t) (App _ x (P Bound n' _))) y
| n == n' = ceq ps x y
ceq ps x@(App _ _ _) y@(App _ _ _)
| (P _ cx _, xargs) <- unApply x,
(P _ cy _, yargs) <- unApply y,
caseName cx && caseName cy = sameCase ps cx cy xargs yargs
ceq ps (App _ fx ax) (App _ fy ay) = liftM2 (&&) (ceq ps fx fy) (ceq ps ax ay)
ceq ps (Constant x) (Constant y) = return (x == y)
ceq ps (TType x) (TType y) | x == y = return True
ceq ps (TType (UVal 0)) (TType y) = return True
ceq ps (TType x) (TType y) = do (v, cs) <- get
put (v, ULE x y : cs)
return True
ceq ps (UType AllTypes) x = return (isUsableUniverse x)
ceq ps x (UType AllTypes) = return (isUsableUniverse x)
ceq ps (UType u) (UType v) = return (u == v)
ceq ps Erased _ = return True
ceq ps _ Erased = return True
ceq ps x y = return False
caseeq ps (Case _ n cs) (Case _ n' cs') = caseeqA ((n,n'):ps) cs cs'
where
caseeqA ps (ConCase x i as sc : rest) (ConCase x' i' as' sc' : rest')
= do q1 <- caseeq (zip as as' ++ ps) sc sc'
q2 <- caseeqA ps rest rest'
return $ x == x' && i == i' && q1 && q2
caseeqA ps (ConstCase x sc : rest) (ConstCase x' sc' : rest')
= do q1 <- caseeq ps sc sc'
q2 <- caseeqA ps rest rest'
return $ x == x' && q1 && q2
caseeqA ps (DefaultCase sc : rest) (DefaultCase sc' : rest')
= liftM2 (&&) (caseeq ps sc sc') (caseeqA ps rest rest')
caseeqA ps [] [] = return True
caseeqA ps _ _ = return False
caseeq ps (STerm x) (STerm y) = ceq ps x y
caseeq ps (UnmatchedCase _) (UnmatchedCase _) = return True
caseeq ps _ _ = return False
sameDefs ps x y = case (lookupDef x ctxt, lookupDef y ctxt) of
([Function _ xdef], [Function _ ydef])
-> ceq ((x,y):ps) xdef ydef
([CaseOp _ _ _ _ _ xd],
[CaseOp _ _ _ _ _ yd])
-> let (_, xdef) = cases_compiletime xd
(_, ydef) = cases_compiletime yd in
caseeq ((x,y):ps) xdef ydef
_ -> return False
sameCase :: [(Name, Name)] -> Name -> Name -> [Term] -> [Term] ->
StateT UCs TC Bool
sameCase ps x y xargs yargs
= case (lookupDef x ctxt, lookupDef y ctxt) of
([Function _ xdef], [Function _ ydef])
-> ceq ((x,y):ps) xdef ydef
([CaseOp _ _ _ _ _ xd],
[CaseOp _ _ _ _ _ yd])
-> let (xin, xdef) = cases_compiletime xd
(yin, ydef) = cases_compiletime yd in
do liftM2 (&&)
(do ok <- zipWithM (ceq ps)
(drop (length xin) xargs)
(drop (length yin) yargs)
return (and ok))
(caseeq ((x,y):ps) xdef ydef)
_ -> return False
data Def = Function !Type !Term
| TyDecl NameType !Type
| Operator Type Int ([Value] -> Maybe Value)
| CaseOp CaseInfo
!Type
![(Type, Bool)]
![Either Term (Term, Term)]
![([Name], Term, Term)]
!CaseDefs
deriving Generic
data CaseDefs = CaseDefs {
cases_compiletime :: !([Name], SC),
cases_runtime :: !([Name], SC)
}
deriving Generic
data CaseInfo = CaseInfo {
case_inlinable :: Bool,
case_alwaysinline :: Bool,
tc_dictionary :: Bool
}
deriving Generic
instance Show Def where
show (Function ty tm) = "Function: " ++ show (ty, tm)
show (TyDecl nt ty) = "TyDecl: " ++ show nt ++ " " ++ show ty
show (Operator ty _ _) = "Operator: " ++ show ty
show (CaseOp (CaseInfo inlc inla inlr) ty atys ps_in ps cd)
= let (ns, sc) = cases_compiletime cd
(ns', sc') = cases_runtime cd in
"Case: " ++ show ty ++ " " ++ show ps ++ "\n" ++
"COMPILE TIME:\n\n" ++
show ns ++ " " ++ show sc ++ "\n\n" ++
"RUN TIME:\n\n" ++
show ns' ++ " " ++ show sc' ++ "\n\n" ++
if inlc then "Inlinable" else "Not inlinable" ++
if inla then " Aggressively\n" else "\n"
data Accessibility = Hidden | Public | Frozen | Private
deriving (Eq, Ord, Generic)
instance Show Accessibility where
show Public = "public export"
show Frozen = "export"
show Private = "private"
show Hidden = "hidden"
type Injectivity = Bool
data Totality = Total [Int]
| Productive
| Partial PReason
| Unchecked
| Generated
deriving (Eq, Generic)
data PReason = Other [Name] | Itself | NotCovering | NotPositive | UseUndef Name
| ExternalIO | BelieveMe | Mutual [Name] | NotProductive
deriving (Show, Eq, Generic)
instance Show Totality where
show (Total args)= "Total"
show Productive = "Productive"
show Unchecked = "not yet checked for totality"
show (Partial Itself) = "possibly not total as it is not well founded"
show (Partial NotCovering) = "not total as there are missing cases"
show (Partial NotPositive) = "not strictly positive"
show (Partial ExternalIO) = "an external IO primitive"
show (Partial NotProductive) = "not productive"
show (Partial BelieveMe) = "not total due to use of believe_me in proof"
show (Partial (Other ns)) = "possibly not total due to: " ++ showSep ", " (map show ns)
show (Partial (Mutual ns)) = "possibly not total due to recursive path " ++
showSep " --> " (map show ns)
show (Partial (UseUndef n)) = "possibly not total because it uses the undefined name " ++ show n
show Generated = "auto-generated"
data MetaInformation =
EmptyMI
| DataMI [Int]
deriving (Eq, Show, Generic)
data Context = MkContext {
next_tvar :: Int,
definitions :: Ctxt TTDecl
} deriving (Show, Generic)
type TTDecl = (Def, RigCount, Injectivity, Accessibility, Totality, MetaInformation)
initContext = MkContext 0 emptyContext
mapDefCtxt :: (Def -> Def) -> Context -> Context
mapDefCtxt f (MkContext t !defs) = MkContext t (mapCtxt f' defs)
where f' (!d, r, i, a, t, m) = f' (f d, r, i, a, t, m)
ctxtAlist :: Context -> [(Name, Def)]
ctxtAlist ctxt = map (\(n, (d, r, i, a, t, m)) -> (n, d)) $ toAlist (definitions ctxt)
veval ctxt env t = evalState (eval False ctxt [] env t []) initEval
addToCtxt :: Name -> Term -> Type -> Context -> Context
addToCtxt n tm ty uctxt
= let ctxt = definitions uctxt
!ctxt' = addDef n (Function ty tm, RigW, False, Public, Unchecked, EmptyMI) ctxt in
uctxt { definitions = ctxt' }
setAccess :: Name -> Accessibility -> Context -> Context
setAccess n a uctxt
= let ctxt = definitions uctxt
!ctxt' = updateDef n (\ (d, r, i, _, t, m) -> (d, r, i, a, t, m)) ctxt in
uctxt { definitions = ctxt' }
setInjective :: Name -> Injectivity -> Context -> Context
setInjective n i uctxt
= let ctxt = definitions uctxt
!ctxt' = updateDef n (\ (d, r, _, a, t, m) -> (d, r, i, a, t, m)) ctxt in
uctxt { definitions = ctxt' }
setTotal :: Name -> Totality -> Context -> Context
setTotal n t uctxt
= let ctxt = definitions uctxt
!ctxt' = updateDef n (\ (d, r, i, a, _, m) -> (d, r, i, a, t, m)) ctxt in
uctxt { definitions = ctxt' }
setRigCount :: Name -> RigCount -> Context -> Context
setRigCount n rc uctxt
= let ctxt = definitions uctxt
!ctxt' = updateDef n (\ (d, _, i, a, t, m) -> (d, rc, i, a, t, m)) ctxt in
uctxt { definitions = ctxt' }
setMetaInformation :: Name -> MetaInformation -> Context -> Context
setMetaInformation n m uctxt
= let ctxt = definitions uctxt
!ctxt' = updateDef n (\ (d, r, i, a, t, _) -> (d, r, i, a, t, m)) ctxt in
uctxt { definitions = ctxt' }
addCtxtDef :: Name -> Def -> Context -> Context
addCtxtDef n d c = let ctxt = definitions c
!ctxt' = addDef n (d, RigW, False, Public, Unchecked, EmptyMI) $! ctxt in
c { definitions = ctxt' }
addTyDecl :: Name -> NameType -> Type -> Context -> Context
addTyDecl n nt ty uctxt
= let ctxt = definitions uctxt
!ctxt' = addDef n (TyDecl nt ty, RigW, False, Public, Unchecked, EmptyMI) ctxt in
uctxt { definitions = ctxt' }
addDatatype :: Datatype Name -> Context -> Context
addDatatype (Data n tag ty unique cons) uctxt
= let ctxt = definitions uctxt
ty' = normalise uctxt [] ty
!ctxt' = addCons 0 cons (addDef n
(TyDecl (TCon tag (arity ty')) ty, RigW, True, Public, Unchecked, EmptyMI) ctxt) in
uctxt { definitions = ctxt' }
where
addCons tag [] ctxt = ctxt
addCons tag ((n, ty) : cons) ctxt
= let ty' = normalise uctxt [] ty in
addCons (tag+1) cons (addDef n
(TyDecl (DCon tag (arity ty') unique) ty, RigW, True, Public, Unchecked, EmptyMI) ctxt)
addCasedef :: Name -> ErasureInfo -> CaseInfo ->
Bool -> SC ->
Bool -> Bool ->
[(Type, Bool)] ->
[Int] ->
[Either Term (Term, Term)] ->
[([Name], Term, Term)] ->
[([Name], Term, Term)] ->
Type -> Context -> TC Context
addCasedef n ei ci@(CaseInfo inline alwaysInline tcdict)
tcase covering reflect asserted argtys inacc
ps_in ps_ct ps_rt ty uctxt
= do let ctxt = definitions uctxt
access = case lookupDefAcc n False uctxt of
[(_, acc)] -> acc
_ -> Public
compileTime <- simpleCase tcase covering reflect CompileTime emptyFC inacc argtys ps_ct ei
runtime <- simpleCase tcase covering reflect RunTime emptyFC inacc argtys ps_rt ei
ctxt' <- case (compileTime, runtime) of
( CaseDef args_ct sc_ct _,
CaseDef args_rt sc_rt _) ->
let inl = alwaysInline
inlc = (inl || small n args_rt sc_rt) && (not asserted)
cdef = CaseDefs (args_ct, sc_ct)
(args_rt, sc_rt)
op = (CaseOp (ci { case_inlinable = inlc })
ty argtys ps_in ps_ct cdef,
RigW, False, access, Unchecked, EmptyMI)
in return $ addDef n op ctxt
return uctxt { definitions = ctxt' }
simplifyCasedef :: Name -> [Name] -> [[Name]] -> ErasureInfo -> Context -> TC Context
simplifyCasedef n ufnames umethss ei uctxt
= do let ctxt = definitions uctxt
ctxt' <- case lookupCtxt n ctxt of
[(CaseOp ci ty atys [] ps _, rc, inj, acc, tot, metainf)] ->
return ctxt
[(CaseOp ci ty atys ps_in ps cd, rc, inj, acc, tot, metainf)] ->
do let ps_in' = map simpl ps_in
pdef = map debind ps_in'
CaseDef args sc _ <- simpleCase False (STerm Erased) False CompileTime emptyFC [] atys pdef ei
return $ addDef n (CaseOp ci
ty atys ps_in' ps (cd { cases_compiletime = (args, sc) }),
rc, inj, acc, tot, metainf) ctxt
_ -> return ctxt
return uctxt { definitions = ctxt' }
where
depat acc (Bind n (PVar _ t) sc)
= depat (n : acc) (instantiate (P Bound n t) sc)
depat acc x = (acc, x)
debind (Right (x, y)) = let (vs, x') = depat [] x
(_, y') = depat [] y in
(vs, x', y')
debind (Left x) = let (vs, x') = depat [] x in
(vs, x', Impossible)
simpl (Right (x, y))
= if null ufnames then Right (x, y)
else Right (x, unfold uctxt [] (map (\n -> (n, 1)) (uns y)) y)
simpl t = t
uns tm = getNamesToUnfold ufnames umethss tm
getNamesToUnfold :: [Name] -> [[Name]] -> Term -> [Name]
getNamesToUnfold inames ms tm = nub $ inames ++ getNames Nothing tm ++ concat ms
where
getNames under fn@(App _ _ _)
| (f, args) <- unApply fn
= let under' = case f of
P _ fn _ -> Just fn
_ -> Nothing
in
getNames under f ++ concatMap (getNames under') args
getNames (Just under) (P _ ref _)
= if ref `elem` inames then [under] else []
getNames under (Bind n (Let c t v) sc)
= getNames Nothing t ++
getNames Nothing v ++
getNames Nothing sc
getNames under (Bind n b sc) = getNames Nothing (binderTy b) ++
getNames Nothing sc
getNames _ _ = []
addOperator :: Name -> Type -> Int -> ([Value] -> Maybe Value) ->
Context -> Context
addOperator n ty a op uctxt
= let ctxt = definitions uctxt
ctxt' = addDef n (Operator ty a op, RigW, False, Public, Unchecked, EmptyMI) ctxt in
uctxt { definitions = ctxt' }
tfst (a, _, _, _, _, _) = a
lookupNames :: Name -> Context -> [Name]
lookupNames n ctxt
= let ns = lookupCtxtName n (definitions ctxt) in
map fst ns
lookupTyName :: Name -> Context -> [(Name, Type)]
lookupTyName n ctxt = do
(name, def) <- lookupCtxtName n (definitions ctxt)
ty <- case tfst def of
(Function ty _) -> return ty
(TyDecl _ ty) -> return ty
(Operator ty _ _) -> return ty
(CaseOp _ ty _ _ _ _) -> return ty
return (name, ty)
lookupTyNameExact :: Name -> Context -> Maybe (Name, Type)
lookupTyNameExact n ctxt = listToMaybe [ (nm, v) | (nm, v) <- lookupTyName n ctxt, nm == n ]
lookupTy :: Name -> Context -> [Type]
lookupTy n ctxt = map snd (lookupTyName n ctxt)
lookupTyExact :: Name -> Context -> Maybe Type
lookupTyExact n ctxt = fmap snd (lookupTyNameExact n ctxt)
isCanonical :: Type -> Context -> Bool
isCanonical t ctxt
= case unApply t of
(P _ n _, _) -> isConName n ctxt
(Constant _, _) -> True
_ -> False
isConName :: Name -> Context -> Bool
isConName n ctxt = isTConName n ctxt || isDConName n ctxt
isTConName :: Name -> Context -> Bool
isTConName n ctxt
= case lookupDefExact n ctxt of
Just (TyDecl (TCon _ _) _) -> True
_ -> False
isDConName :: Name -> Context -> Bool
isDConName n ctxt
= case lookupDefExact n ctxt of
Just (TyDecl (DCon _ _ _) _) -> True
_ -> False
canBeDConName :: Name -> Context -> Bool
canBeDConName n ctxt
= or $ do def <- lookupCtxt n (definitions ctxt)
case tfst def of
(TyDecl (DCon _ _ _) _) -> return True
_ -> return False
isFnName :: Name -> Context -> Bool
isFnName n ctxt
= case lookupDefExact n ctxt of
Just (Function _ _) -> True
Just (Operator _ _ _) -> True
Just (CaseOp _ _ _ _ _ _) -> True
_ -> False
isTCDict :: Name -> Context -> Bool
isTCDict n ctxt
= case lookupDefExact n ctxt of
Just (Function _ _) -> False
Just (Operator _ _ _) -> False
Just (CaseOp ci _ _ _ _ _) -> tc_dictionary ci
_ -> False
conGuarded :: Context -> Name -> Term -> Bool
conGuarded ctxt n tm = guarded n tm
where
guarded n (P _ n' _) = n == n'
guarded n ap@(App _ _ _)
| (P _ f _, as) <- unApply ap,
isConName f ctxt = any (guarded n) as
guarded _ _ = False
visibleDefinitions :: Context -> Ctxt TTDecl
visibleDefinitions ctxt =
Map.filter (\m -> length m > 0) . Map.map onlyVisible . definitions $ ctxt
where
onlyVisible = Map.filter visible
visible (_def, _rigCount, _injectivity, accessibility, _totality, _metaInformation) =
accessibility `notElem` [Hidden, Private]
lookupP :: Name -> Context -> [Term]
lookupP = lookupP_all False False
lookupP_all :: Bool -> Bool -> Name -> Context -> [Term]
lookupP_all all exact n ctxt
= do (n', def) <- names
p <- case def of
(Function ty tm, _, inj, a, _, _) -> return (P Ref n' ty, a)
(TyDecl nt ty, _, _, a, _, _) -> return (P nt n' ty, a)
(CaseOp _ ty _ _ _ _, _, inj, a, _, _) -> return (P Ref n' ty, a)
(Operator ty _ _, _, inj, a, _, _) -> return (P Ref n' ty, a)
case snd p of
Hidden -> if all then return (fst p) else []
Private -> if all then return (fst p) else []
_ -> return (fst p)
where
names = let ns = lookupCtxtName n (definitions ctxt) in
if exact
then filter (\ (n', d) -> n' == n) ns
else ns
lookupDefExact :: Name -> Context -> Maybe Def
lookupDefExact n ctxt = tfst <$> lookupCtxtExact n (definitions ctxt)
lookupDef :: Name -> Context -> [Def]
lookupDef n ctxt = tfst <$> lookupCtxt n (definitions ctxt)
lookupNameDef :: Name -> Context -> [(Name, Def)]
lookupNameDef n ctxt = mapSnd tfst $ lookupCtxtName n (definitions ctxt)
where mapSnd f [] = []
mapSnd f ((x,y):xys) = (x, f y) : mapSnd f xys
lookupDefAcc :: Name -> Bool -> Context ->
[(Def, Accessibility)]
lookupDefAcc n mkpublic ctxt
= map mkp $ lookupCtxt n (definitions ctxt)
where mkp (d, _, inj, a, _, _) = if mkpublic && (not (n == sUN "io_bind" || n == sUN "io_pure"))
then (d, Public) else (d, a)
lookupDefAccExact :: Name -> Bool -> Context ->
Maybe (Def, Accessibility)
lookupDefAccExact n mkpublic ctxt
= fmap mkp $ lookupCtxtExact n (definitions ctxt)
where mkp (d, _, inj, a, _, _) = if mkpublic && (not (n == sUN "io_bind" || n == sUN "io_pure"))
then (d, Public) else (d, a)
lookupTotal :: Name -> Context -> [Totality]
lookupTotal n ctxt = map mkt $ lookupCtxt n (definitions ctxt)
where mkt (d, _, inj, a, t, m) = t
lookupTotalExact :: Name -> Context -> Maybe Totality
lookupTotalExact n ctxt = fmap mkt $ lookupCtxtExact n (definitions ctxt)
where mkt (d, _, inj, a, t, m) = t
lookupRigCount :: Name -> Context -> [Totality]
lookupRigCount n ctxt = map mkt $ lookupCtxt n (definitions ctxt)
where mkt (d, _, inj, a, t, m) = t
lookupRigCountExact :: Name -> Context -> Maybe RigCount
lookupRigCountExact n ctxt = fmap mkt $ lookupCtxtExact n (definitions ctxt)
where mkt (d, rc, inj, a, t, m) = rc
lookupInjectiveExact :: Name -> Context -> Maybe Injectivity
lookupInjectiveExact n ctxt = fmap mkt $ lookupCtxtExact n (definitions ctxt)
where mkt (d, _, inj, a, t, m) = inj
linearCheck :: Context -> Type -> TC ()
linearCheck ctxt t = checkArgs t
where
checkArgs (Bind n (Pi RigW _ ty _) sc)
= do linearCheckArg ctxt ty
checkArgs (substV (P Bound n Erased) sc)
checkArgs (Bind n (Pi _ _ _ _) sc)
= checkArgs (substV (P Bound n Erased) sc)
checkArgs _ = return ()
linearCheckArg :: Context -> Type -> TC ()
linearCheckArg ctxt ty = mapM_ checkNameOK (allTTNames ty)
where
checkNameOK f
= case lookupRigCountExact f ctxt of
Just Rig1 ->
tfail $ Msg $ show f ++ " can only appear in a linear binding"
_ -> return ()
tcReducible :: Name -> Context -> Bool
tcReducible n ctxt = case lookupTotalExact n ctxt of
Nothing -> True
Just (Partial _) -> False
_ -> True
lookupMetaInformation :: Name -> Context -> [MetaInformation]
lookupMetaInformation n ctxt = map mkm $ lookupCtxt n (definitions ctxt)
where mkm (d, _, inj, a, t, m) = m
lookupNameTotal :: Name -> Context -> [(Name, Totality)]
lookupNameTotal n = map (\(n, (_, _, _, _, t, _)) -> (n, t)) . lookupCtxtName n . definitions
lookupVal :: Name -> Context -> [Value]
lookupVal n ctxt
= do def <- lookupCtxt n (definitions ctxt)
case tfst def of
(Function _ htm) -> return (veval ctxt [] htm)
(TyDecl nt ty) -> return (VP nt n (veval ctxt [] ty))
_ -> []
lookupTyEnv :: Name -> Env -> Maybe (Int, RigCount, Type)
lookupTyEnv n env = li n 0 env where
li n i [] = Nothing
li n i ((x, r, b): xs)
| n == x = Just (i, r, binderTy b)
| otherwise = li n (i+1) xs
uniqueNameCtxt :: Context -> Name -> [Name] -> Name
uniqueNameCtxt ctxt n hs
| n `elem` hs = uniqueNameCtxt ctxt (nextName n) hs
| [_] <- lookupTy n ctxt = uniqueNameCtxt ctxt (nextName n) hs
| otherwise = n
uniqueBindersCtxt :: Context -> [Name] -> TT Name -> TT Name
uniqueBindersCtxt ctxt ns (Bind n b sc)
= let n' = uniqueNameCtxt ctxt n ns in
Bind n' (fmap (uniqueBindersCtxt ctxt (n':ns)) b) (uniqueBindersCtxt ctxt ns sc)
uniqueBindersCtxt ctxt ns (App s f a) = App s (uniqueBindersCtxt ctxt ns f) (uniqueBindersCtxt ctxt ns a)
uniqueBindersCtxt ctxt ns t = t