module Language.Nano.Liquid.CGMonad (
CGM
, getFInfo
, getDefType
, cgError
, freshTyFun
, freshTyInst
, freshTyPhis
, envAddFresh
, envAdds
, envAddReturn
, envAddGuard
, envFindTy
, envFindReturn
, subTypes
, subType
) where
import Data.Maybe (fromMaybe)
import qualified Data.HashMap.Strict as M
import Language.Fixpoint.PrettyPrint
import Text.PrettyPrint.HughesPJ
import Language.Nano.Types
import Language.Nano.Errors
import qualified Language.Nano.Env as E
import Language.Nano.Typecheck.Types
import Language.Nano.Typecheck.Subst
import Language.Nano.Liquid.Types
import qualified Language.Fixpoint.Types as F
import Language.Fixpoint.Misc
import Control.Applicative
import Control.Monad
import Control.Monad.State
import Control.Monad.Error
import Text.Printf
import Language.ECMAScript3.Syntax
getFInfo :: NanoRefType -> CGM a -> F.FInfo Cinfo
getFInfo pgm = cgStateFInfo pgm . execute pgm . (>> fixCWs)
where
fixCWs = (,) <$> fixCs <*> fixWs
fixCs = concatMapM splitC . cs =<< get
fixWs = concatMapM splitW . ws =<< get
execute :: Nano z RefType -> CGM a -> (a, CGState)
execute pgm act
= case runState (runErrorT act) $ initState pgm of
(Left err, _) -> errorstar err
(Right x, st) -> (x, st)
initState :: Nano z RefType -> CGState
initState pgm = CGS F.emptyBindEnv (defs pgm) [] [] 0
getDefType f
= do m <- cg_defs <$> get
maybe err return $ E.envFindTy f m
where
err = cgError l $ errorMissingSpec l f
l = srcPos f
cgStateFInfo :: Nano a1 (RType F.Reft)-> (([F.SubC a], [F.WfC a]), CGState) -> F.FInfo a
cgStateFInfo pgm ((fcs, fws), cg)
= F.FI { F.cm = M.fromList $ F.addIds fcs
, F.ws = fws
, F.bs = binds cg
, F.gs = measureEnv pgm
, F.lits = []
, F.kuts = F.ksEmpty
, F.quals = quals pgm
}
measureEnv :: Nano a (RType F.Reft) -> F.SEnv F.SortedReft
measureEnv = fmap rTypeSortedReft . E.envSEnv . consts
data CGState
= CGS { binds :: F.BindEnv
, cg_defs :: !(E.Env RefType)
, cs :: ![SubC]
, ws :: ![WfC]
, count :: !Integer
}
type CGM = ErrorT String (State CGState)
cgError :: (IsLocated l) => l -> String -> CGM a
cgError l msg = throwError $ printf "CG-ERROR at %s : %s" (ppshow $ srcPos l) msg
envAddFresh :: (IsLocated l) => l -> RefType -> CGEnv -> CGM (Id l, CGEnv)
envAddFresh l t g
= do x <- freshId l
g' <- envAdds [(x, t)] g
return (x, g')
envAdds :: (F.Symbolic x, IsLocated x) => [(x, RefType)] -> CGEnv -> CGM CGEnv
envAdds xts g
= do is <- mapM addFixpointBind xts
return $ g { renv = E.envAdds xts (renv g) }
{ fenv = F.insertsIBindEnv is (fenv g) }
addFixpointBind :: (F.Symbolic x) => (x, RefType) -> CGM F.BindId
addFixpointBind (x, t)
= do let s = F.symbol x
let r = rTypeSortedReft t
(i, bs') <- F.insertBindEnv s r . binds <$> get
modify $ \st -> st { binds = bs' }
return i
envAddReturn :: (IsLocated f) => f -> RefType -> CGEnv -> CGEnv
envAddReturn f t g = g { renv = E.envAddReturn f t (renv g) }
envAddGuard :: (F.Symbolic x, IsLocated x) => x -> Bool -> CGEnv -> CGEnv
envAddGuard x b g = g { guards = guard b x : guards g }
where
guard True = F.eProp
guard False = F.PNot . F.eProp
envFindTy :: (IsLocated x, F.Symbolic x, F.Expression x) => x -> CGEnv -> RefType
envFindTy x g = (`eSingleton` x) $ fromMaybe err $ E.envFindTy x $ renv g
where
err = errorstar $ bugUnboundVariable (srcPos x) (F.symbol x)
envFindReturn :: CGEnv -> RefType
envFindReturn = E.envFindReturn . renv
freshTyFun :: (IsLocated l) => CGEnv -> l -> RefType -> CGM RefType
freshTyFun g l t
| not $ isTrivialRefType t = return t
| otherwise = freshTy "freshTyFun" (toType t) >>= wellFormed l g
freshTyInst :: (IsLocated l) => l -> CGEnv -> [TVar] -> [Type] -> RefType -> CGM RefType
freshTyInst l g αs τs tbody
= do ts <- mapM (freshTy "freshTyInst") τs
_ <- mapM (wellFormed l g) ts
let θ = fromList $ zip αs ts
return $ apply θ tbody
freshTyPhis :: (IsLocated l) => l -> CGEnv -> [Id l] -> [Type] -> CGM (CGEnv, [RefType])
freshTyPhis l g xs τs
= do ts <- mapM (freshTy "freshTyPhis") τs
g' <- envAdds (safeZip "freshTyPhis" xs ts) g
_ <- mapM (wellFormed l g') ts
return (g', ts)
subTypes :: (IsLocated l, IsLocated x, F.Expression x, F.Symbolic x)
=> l -> CGEnv -> [x] -> [RefType] -> CGM ()
subTypes l g xs ts = zipWithM_ (subType l g) [envFindTy x g | x <- xs] ts
subType :: (IsLocated l) => l -> CGEnv -> RefType -> RefType -> CGM ()
subType l g t1 t2 = modify $ \st -> st {cs = c : (cs st)}
where
c = Sub g (ci l) t1 t2
wellFormed :: (IsLocated l) => l -> CGEnv -> RefType -> CGM RefType
wellFormed l g t = do modify $ \st -> st { ws = (W g (ci l) t) : ws st }
return t
class Freshable a where
fresh :: CGM a
true :: a -> CGM a
true = return . id
refresh :: a -> CGM a
refresh = return . id
instance Freshable Integer where
fresh = do modify $ \st -> st { count = 1 + (count st) }
count <$> get
instance Freshable F.Symbol where
fresh = F.tempSymbol "nano" <$> fresh
instance Freshable String where
fresh = F.symbolString <$> fresh
freshId :: (IsLocated l) => l -> CGM (Id l)
freshId l = Id l <$> fresh
freshTy :: (Show a) => a -> Type -> CGM RefType
freshTy _ τ = (refresh $ rType τ)
instance Freshable F.Refa where
fresh = (`F.RKvar` F.emptySubst) <$> (F.intKvar <$> fresh)
instance Freshable [F.Refa] where
fresh = single <$> fresh
instance Freshable F.Reft where
fresh = errorstar "fresh Reft"
true (F.Reft (v,_)) = return $ F.Reft (v, [])
refresh (F.Reft (_,_)) = curry F.Reft <$> freshVV <*> fresh
where freshVV = F.vv . Just <$> fresh
instance Freshable F.SortedReft where
fresh = errorstar "fresh Reft"
true (F.RR so r) = F.RR so <$> true r
refresh (F.RR so r) = F.RR so <$> refresh r
instance Freshable RefType where
fresh = errorstar "fresh RefType"
refresh = refreshRefType
true = trueRefType
trueRefType :: RefType -> CGM RefType
trueRefType = mapReftM true
refreshRefType :: RefType -> CGM RefType
refreshRefType = mapReftM refresh
splitC :: SubC -> CGM [FixSubC]
splitC (Sub g i tf1@(TFun xt1s t1 _) tf2@(TFun xt2s t2 _))
= do let bcs = bsplitC g i tf1 tf2
g' <- envTyAdds i xt2s g
cs <- concatMapM splitC $ safeZipWith "splitC1" (Sub g' i) t2s t1s'
cs' <- splitC $ Sub g' i (F.subst su t1) t2
return $ bcs ++ cs ++ cs'
where
t2s = b_type <$> xt2s
t1s' = F.subst su (b_type <$> xt1s)
su = F.mkSubst $ safeZipWith "splitC2" bSub xt1s xt2s
bSub b1 b2 = (b_sym b1, F.eVar $ b_sym b2)
splitC (Sub g i (TAll α1 t1) (TAll α2 t2))
| α1 == α2
= splitC $ Sub g i t1 t2
| otherwise
= splitC $ Sub g i t1 t2'
where
θ = fromList [(α2, tVar α1 :: RefType)]
t2' = apply θ t2
splitC (Sub g i t1@(TVar α1 _) t2@(TVar α2 _))
| α1 == α2
= return $ bsplitC g i t1 t2
| otherwise
= errorstar "UNEXPECTED CRASH in splitC"
splitC (Sub g i t1@(TApp _ t1s _) t2@(TApp _ t2s _))
= do let cs = bsplitC g i t1 t2
cs' <- concatMapM splitC $ safeZipWith "splitC2" (Sub g i) t1s t2s
return $ cs ++ cs'
splitC x
= cgError (srcPos x) $ bugBadSubtypes x
bsplitC g ci t1 t2
| F.isFunctionSortedReft r1 && F.isNonTrivialSortedReft r2
= [F.subC (fenv g) F.PTrue (r1 {F.sr_reft = F.top}) r2 Nothing [] ci]
| F.isNonTrivialSortedReft r2
= [F.subC (fenv g) p r1 r2 Nothing [] ci]
| otherwise
= []
where
p = F.pAnd $ guards g
r1 = rTypeSortedReft t1
r2 = rTypeSortedReft t2
splitW :: WfC -> CGM [FixWfC]
splitW (W g i (TFun ts t _))
= do let bws = bsplitW g t i
g' <- envTyAdds i ts g
ws <- concatMapM splitW [W g' i ti | B _ ti <- ts]
ws' <- splitW (W g' i t)
return $ bws ++ ws ++ ws'
splitW (W g i (TAll _ t))
= splitW (W g i t)
splitW (W g i t@(TVar _ _))
= return $ bsplitW g t i
splitW (W g i t@(TApp _ ts _))
= do let ws = bsplitW g t i
ws' <- concatMapM splitW [W g i ti | ti <- ts]
return $ ws ++ ws'
bsplitW g t i
| F.isNonTrivialSortedReft r'
= [F.wfC (fenv g) r' Nothing i]
| otherwise
= []
where r' = rTypeSortedReft t
envTyAdds l xts = envAdds [(symbolId l x, t) | B x t <- xts]