g2-0.1.0.0: src/G2/Liquid/TCGen.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
module G2.Liquid.TCGen (createLHState) where
import G2.Language
import qualified G2.Language.KnownValues as KV
import G2.Language.Monad
import G2.Liquid.Conversion
import G2.Liquid.TCValues
import G2.Liquid.Types
import Data.Foldable
import qualified Data.Map as M
import qualified Data.Text as T
-- | Creates an LHState. This involves building a TCValue, and
-- creating the new LH TC which checks equality, and has a function to
-- check refinements of polymorphic types
createLHState :: Measures -> KnownValues -> State [FuncCall] -> Bindings -> (LHState, Bindings)
createLHState meenv mkv s b =
let
(tcv, (s', b')) = runStateM (createTCValues mkv) s b
lh_s = consLHState s' meenv tcv
in
execLHStateM (do
createLHTCFuncs
createExtractors) lh_s b'
createTCValues :: KnownValues -> StateM [FuncCall] TCValues
createTCValues kv = do
lhTCN <- freshSeededStringN "lh"
lhEqN <- freshSeededStringN "lhEq"
lhNeN <- freshSeededStringN "lhNe"
lhLtN <- freshSeededStringN "lhLt"
lhLeN <- freshSeededStringN "lhLe"
lhGtN <- freshSeededStringN "lhGt"
lhGeN <- freshSeededStringN "lhGe"
lhPPN <- freshSeededStringN "lhPP"
lhNuOr <- freshSeededStringN "lhNuOr"
let tcv = (TCValues { lhTC = lhTCN
, lhNumTC = KV.numTC kv
, lhOrdTC = KV.ordTC kv
, lhEq = lhEqN
, lhNe = lhNeN
, lhLt = lhLtN
, lhLe = lhLeN
, lhGt = lhGtN
, lhGe = lhGeN
, lhPlus = KV.plusFunc kv
, lhMinus = KV.minusFunc kv
, lhTimes = KV.timesFunc kv
, lhDiv = KV.divFunc kv
, lhNegate = KV.negateFunc kv
, lhMod = KV.modFunc kv
, lhFromInteger = KV.fromIntegerFunc kv
, lhToInteger = KV.toIntegerFunc kv
, lhNumOrd = lhNuOr
, lhAnd = KV.andFunc kv
, lhOr = KV.orFunc kv
, lhPP = lhPPN })
return tcv
type PredFunc = LHDictMap -> Name -> AlgDataTy -> DataCon -> [Id] -> LHStateM [Alt]
createLHTCFuncs :: LHStateM ()
createLHTCFuncs = do
lhm <- mapM (uncurry initalizeLHTC) . M.toList =<< typeEnv
let lhm' = M.fromList lhm
-- createLHTCFuncs' relies on the standard TypeClass lookup functions to get access to
-- LH Dicts. So it is important that, before calling it, we set up the TypeClass correctly
lhtc <- mapM (\(n, f) -> do
adt <- lookupT n
case adt of
Just adt' -> do
let bnvK = mkTyApp $ map (const TYPE) $ bound_ids adt'
return (TyCon n bnvK, f)
Nothing -> error $ "No LH Dict name for " ++ show n) lhm
tc <- typeClasses
tcn <- lhTCM
tci <- freshIdN TYPE
let tc' = insertClass tcn (Class { insts = lhtc, typ_ids = [tci] }) tc
putTypeClasses tc'
-- Now, we do the work of actually generating all the code/functions for the typeclass
mapM_ (uncurry (createLHTCFuncs' lhm')) . M.toList =<< typeEnv
initalizeLHTC :: Name -> AlgDataTy -> LHStateM (Name, Id)
initalizeLHTC n adt = do
lhf <- lhName "lh" n
t <- lhtcT n adt
return (n, Id lhf t)
lhtcT :: Name -> AlgDataTy -> LHStateM Type
lhtcT n adt = do
lh <- lhTCM
let bi = bound_ids adt
let ct = foldl' TyApp (TyCon n TYPE) $ map TyVar bi
let t = (TyApp
(TyCon lh TYPE)
ct
)
let t' = foldr TyFun t $ map (TyApp (TyCon lh (TyFun TYPE TYPE)) . TyVar) bi
let t'' = foldr TyForAll t' $ map NamedTyBndr bi
return t''
lhName :: T.Text -> Name -> LHStateM Name
lhName t (Name n m _ _) = freshSeededNameN $ Name (t `T.append` n) m 0 Nothing
createLHTCFuncs' :: LHDictMap -> Name -> AlgDataTy -> LHStateM ()
createLHTCFuncs' lhm n adt = do
eqN <- lhName "lhEq" n
eq <- createFunc lhEqFunc n adt
insertMeasureM eqN eq
neN <- lhName "lhNe" n
ne <- createFunc lhNeFunc n adt
insertMeasureM neN ne
ltN <- lhName "lhLt" n
lt <- createLtFunc n adt
insertMeasureM ltN lt
leN <- lhName "lhLe" n
le <- createLeFunc n adt
insertMeasureM leN le
gtN <- lhName "lhGt" n
gt <- createGtFunc n adt
insertMeasureM gtN gt
geN <- lhName "lhGe" n
ge <- createGeFunc n adt
insertMeasureM geN ge
ppN <- lhName "lhPP" n
pp <- lhPPFunc n adt
insertMeasureM ppN pp
-- We define a function to get the LH Dict for this type
-- It takes and passes on the type arguments, and the LH Dicts for those
-- type arguments
lh <- lhTCM
let bi = bound_ids adt
let bt = map (Type . TyVar) bi
lhd <- freshIdsN (map (TyApp (TyCon lh (TyApp TYPE TYPE)) . TyVar) bi)
let lhdv = map Var lhd
let fs = map (\(n', t) -> Var (Id n' t)) [ (eqN, (typeOf eq))
, (neN, (typeOf ne))
, (ltN, (typeOf lt))
, (leN, (typeOf le))
, (gtN, (typeOf gt))
, (geN, (typeOf ge))
, (ppN, (typeOf pp)) ]
let fs' = map (\f -> mkApp $ f:bt ++ lhdv) fs
lhdct <- lhDCType
let e = mkApp $ Data (DataCon lh lhdct):fs'
let e' = foldr (Lam TermL) e lhd
let e'' = foldr (Lam TypeL) e' bi
let fn = M.lookup n lhm
case fn of
Just fn' -> do
insertMeasureM (idName fn') e''
-- let bnvK = mkTyApp $ map (const TYPE) bi
return () -- return (TyCon n bnvK, fn')
Nothing -> error $ "No LH Dict name for " ++ show n
lhDCType :: LHStateM Type
lhDCType = do
lh <- lhTCM
n <- freshIdN TYPE
a <- freshSeededStringN "a"
bool <- tyBoolT
let tva = TyVar (Id a TYPE)
let taab = TyFun tva (TyFun tva bool)
return $ (TyFun
taab -- eq
(TyFun
taab --neq
(TyFun
taab --lt
(TyFun
taab --le
(TyFun
taab --gt
(TyFun
taab --ge
(TyFun
TyUnknown
(TyApp
(TyCon lh TYPE)
(TyVar n)
)
)
)
)
)
)
)
)
createFunc :: PredFunc -> Name -> AlgDataTy -> LHStateM Expr
createFunc cf n adt = do
-- Our function needs the following arguments:
-- Type arguments
-- A LH typeclass for each type argument
-- Two expression of the adt type
-- We set up the needed Ids here
let bi = bound_ids adt
lh <- lhTCM
lhbi <- mapM (freshIdN . TyApp (TyCon lh TYPE) . TyVar) bi
d1 <- freshIdN (TyCon n TYPE)
d2 <- freshIdN (TyCon n TYPE)
let m = M.fromList $ zip (map idName bi) lhbi
e <- mkFirstCase cf m d1 d2 n adt
let e' = mkLams (map (TypeL,) bi ++ map (TermL,) lhbi ++ [(TermL, d1), (TermL, d2)]) e
return e'
mkFirstCase :: PredFunc -> LHDictMap -> Id -> Id -> Name -> AlgDataTy -> LHStateM Expr
mkFirstCase f ldm d1 d2 n adt@(DataTyCon { data_cons = dcs }) = do
caseB <- freshIdN (typeOf d1)
return . Case (Var d1) caseB =<< mapM (mkFirstCase' f ldm d2 n adt) dcs
mkFirstCase f ldm d1 d2 n adt@(NewTyCon { data_con = dc }) = do
caseB <- freshIdN (typeOf d1)
return . Case (Var d1) caseB . (:[]) =<< mkFirstCase' f ldm d2 n adt dc
mkFirstCase _ _ _ _ _ _ = error "mkFirstCase: Unsupported AlgDataTy"
mkFirstCase' :: PredFunc -> LHDictMap -> Id -> Name -> AlgDataTy -> DataCon -> LHStateM Alt
mkFirstCase' f ldm d2 n adt dc = do
ba <- freshIdsN $ anonArgumentTypes dc
return . Alt (DataAlt dc ba) =<< mkSecondCase f ldm d2 n adt dc ba
mkSecondCase :: PredFunc -> LHDictMap -> Id -> Name -> AlgDataTy -> DataCon -> [Id] -> LHStateM Expr
mkSecondCase f ldm d2 n adt dc ba1 = do
caseB <- freshIdN (typeOf dc)
alts <- f ldm n adt dc ba1
return $ Case (Var d2) caseB alts
lhEqFunc :: PredFunc
lhEqFunc ldm _ _ dc ba1 = do
ba2 <- freshIdsN $ anonArgumentTypes dc
an <- lhAndE
true <- mkTrueE
false <- mkFalseE
pr <- mapM (uncurry (eqLHFuncCall ldm)) $ zip ba1 ba2
let pr' = foldr (\e -> App (App an e)) true pr
return [ Alt Default false
, Alt (DataAlt dc ba2) pr']
eqLHFuncCall :: LHDictMap -> Id -> Id -> LHStateM Expr
eqLHFuncCall ldm i1 i2
| TyCon _ _ <- tyAppCenter t = do
lhe <- lhEqM
i <- freshIdN TYPE
b <- tyBoolT
let lhv = App (Var $ Id lhe (TyForAll (NamedTyBndr i) (TyFun (TyVar i) (TyFun (TyVar i) b)))) (Type t)
lhd <- lhTCDict' ldm t
return $ foldl' App (App lhv lhd) [Var i1, Var i2]
| TyVar _ <- t = do
lhe <- lhEqM
i <- freshIdN TYPE
b <- tyBoolT
lhd <- lhTCDict' ldm t
let lhv = App (Var (Id lhe (TyForAll (NamedTyBndr i) (TyFun (TyVar i) (TyFun (TyVar i) b))))) (Type t)
return $ App (App (App lhv lhd) (Var i1)) (Var i2)
| TyFun _ _ <- t = mkTrueE
| TyApp _ _ <- t = mkTrueE
| TyForAll _ _ <- t = mkTrueE
| t == TyLitInt
|| t == TyLitDouble
|| t == TyLitFloat
|| t == TyLitChar = do
b <- tyBoolT
let pt = TyFun t (TyFun t b)
return $ App (App (Prim Eq pt) (Var i1)) (Var i2)
| otherwise = error $ "\nError in eqLHFuncCall " ++ show t ++ "\n" ++ show ldm
where
t = typeOf i1
lhNeFunc :: PredFunc
lhNeFunc ldm _ _ dc ba1 = do
ba2 <- freshIdsN $ anonArgumentTypes dc
an <- lhAndE
true <- mkTrueE
false <- mkFalseE
trueDC <- mkDCTrueM
falseDC <- mkDCFalseM
pr <- mapM (uncurry (eqLHFuncCall ldm)) $ zip ba1 ba2
let pr' = foldr (\e -> App (App an e)) true pr
b <- freshIdN =<< tyBoolT
let pr'' = Case pr' b [ Alt (DataAlt trueDC []) false
, Alt (DataAlt falseDC []) true ]
return [ Alt Default false
, Alt (DataAlt dc ba2) pr'']
createLtFunc :: Name -> AlgDataTy -> LHStateM Expr
createLtFunc = createOrdFunc Lt
createLeFunc :: Name -> AlgDataTy -> LHStateM Expr
createLeFunc = createOrdFunc Le
createGtFunc :: Name -> AlgDataTy -> LHStateM Expr
createGtFunc = createOrdFunc Gt
createGeFunc :: Name -> AlgDataTy -> LHStateM Expr
createGeFunc = createOrdFunc Ge
-- We currently treat relations between Ints/Floats/Doubles correctly,
-- and just assume all other relations are true.
-- In LH, relations between x :: T and y :: T work by checking that
-- f x < f y
-- for all f :: T -> Int, so this could make us miss some counterexamples.
-- However, we will never generate an incorrect counterexample.
-- (i.e. it is sound but incomplete)
createOrdFunc :: Primitive -> Name -> AlgDataTy -> LHStateM Expr
createOrdFunc pr n adt = do
let bi = bound_ids adt
lh <- lhTCM
lhbi <- mapM (freshIdN . TyApp (TyCon lh TYPE) . TyVar) bi
d1 <- freshIdN (TyCon n TYPE)
d2 <- freshIdN (TyCon n TYPE)
kv <- knownValues
e <- mkOrdCases pr kv d1 d2 n adt
let e' = mkLams (map (TypeL,) bi ++ map (TermL,) lhbi ++ [(TermL, d1), (TermL, d2)]) e
return e'
mkOrdCases :: Primitive -> KnownValues -> Id -> Id -> Name -> AlgDataTy -> LHStateM Expr
mkOrdCases pr kv i1 i2 n (DataTyCon { data_cons = [dc]})
| n == KV.tyInt kv = mkPrimOrdCases pr TyLitInt i1 i2 dc
| n == KV.tyFloat kv = mkPrimOrdCases pr TyLitFloat i1 i2 dc
| n == KV.tyDouble kv = mkPrimOrdCases pr TyLitDouble i1 i2 dc
| otherwise = mkTrueE
mkOrdCases _ _ _ _ _ _ = mkTrueE
mkPrimOrdCases :: Primitive -> Type -> Id -> Id -> DataCon -> LHStateM Expr
mkPrimOrdCases pr t i1 i2 dc = do
i1' <- freshIdN (typeOf dc)
i2' <- freshIdN (typeOf dc)
b1 <- freshIdN t
b2 <- freshIdN t
b <- tyBoolT
let eq = App
(App
(Prim pr (TyFun t (TyFun t b)))
(Var b1)
)
(Var b2)
let c2 = Case (Var i2) i2' [Alt (DataAlt dc [b2]) eq]
return $ Case (Var i1) i1' [Alt (DataAlt dc [b1]) c2]
lhPPFunc :: Name -> AlgDataTy -> LHStateM Expr
lhPPFunc n adt = do
let bi = bound_ids adt
lh <- lhTCM
lhbi <- mapM (freshIdN . TyApp (TyCon lh TYPE) . TyVar) bi
b <- tyBoolT
fs <- mapM (\v -> freshIdN (TyFun (TyVar v) b)) bi
d <- freshIdN (TyCon n TYPE)
let lhm = M.fromList $ zip (map idName bi) lhbi
let fnm = M.fromList $ zip (map idName bi) fs
e <- lhPPCase lhm fnm adt d
let e' = mkLams (map (TypeL,) bi ++ map (TermL,) lhbi ++ map (TermL,) fs ++ [(TermL, d)]) e
return e'
type PPFuncMap = M.Map Name Id
lhPPCase :: LHDictMap -> PPFuncMap -> AlgDataTy -> Id -> LHStateM Expr
lhPPCase lhm fnm adt i = do
ci <- freshIdN (typeOf i)
return . Case (Var i) ci =<< mapM (lhPPAlt lhm fnm) (dataCon adt)
lhPPAlt :: LHDictMap -> PPFuncMap -> DataCon -> LHStateM Alt
lhPPAlt lhm fnm dc = do
ba <- freshIdsN $ anonArgumentTypes dc
an <- lhAndE
true <- mkTrueE
pr <- mapM (\i -> do
pp <- lhPPCall lhm fnm (typeOf i)
return $ App pp (Var i)) ba
let pr' = foldr (\e -> App (App an e)) true pr
return $ Alt (DataAlt dc ba) pr'
-- This returns an Expr with a function type, of the given Type to Bool.
lhPPCall :: LHDictMap -> PPFuncMap -> Type -> LHStateM Expr
lhPPCall lhm fnm t
| TyCon _ _ <- tyAppCenter t
, ts <- tyAppArgs t = do
lhpp <- lhPPM
let lhv = Var $ Id lhpp TyUnknown
dict <- lhTCDict' lhm t
undefs <- mapM (lhPPCall lhm fnm) ts
return . mkApp $ lhv:[Type t, dict] ++ undefs -- ++ [Var i]
| TyVar (Id n _) <- t
, Just f <- M.lookup n fnm = return $ Var f -- App (Var f) (Var i)
| TyVar _ <- tyAppCenter t = do
i <- freshIdN t
return . Lam TermL i =<< mkTrueE
| TyFun _ _ <- t = do
i <- freshIdN t
return . Lam TermL i =<< mkTrueE
| TyForAll _ _ <- t = do
i <- freshIdN t
return . Lam TermL i =<< mkTrueE
| t == TyLitInt
|| t == TyLitDouble
|| t == TyLitFloat
|| t == TyLitChar = do
i <- freshIdN t
return . Lam TermL i =<< mkTrueE
| otherwise = error $ "\nError in lhPPCall " ++ show t ++ "\n" ++ show lhm
createExtractors :: LHStateM ()
createExtractors = do
lh <- lhTCM
eq <- lhEqM
lt <- lhLtM
le <- lhLeM
gt <- lhGtM
ge <- lhGeM
ne <- lhNeM
pp <- lhPPM
createExtractors' lh [eq, ne, lt, le, gt, ge, pp]
createExtractors' :: Name -> [Name] -> LHStateM ()
createExtractors' lh ns = mapM_ (uncurry (createExtractors'' lh (length ns))) $ zip [0..] ns
createExtractors'' :: Name -> Int -> Int -> Name -> LHStateM ()
createExtractors'' lh i j n = do
a <- freshIdN TYPE
bi <- freshIdsN $ replicate i TyUnknown
li <- freshIdN (TyCon lh (TyApp TYPE TYPE))
ci <- freshIdN (TyCon lh (TyApp TYPE TYPE))
b <- freshIdN TYPE
let d = DataCon lh (TyForAll
(NamedTyBndr b)
(TyFun
(TyVar b)
(TyApp (TyCon lh (TyApp TYPE TYPE)) (TyVar b))
)
)
let c = Case (Var li) ci [Alt (DataAlt d bi) (Var $ bi !! j)]
let e = Lam TypeL a $ Lam TermL li c
insertMeasureM n e