g2-0.1.0.0: src/G2/Liquid/AddOrdToNum.hs
-- | Adds the Ord typeclass into the Num typeclass.
-- We need this to work with refinements like:
--
-- @
-- {-@ sub :: Num a => {x:a | x > 0} -> {y:a | y >= 0} @-}
-- @
--
-- which only have a prerequisite of Num, but use Ord functions in the refinement.
-- Not all Num types have an Ord instance. However, we will only try
-- to extact it in LH refinement types, and LH only allows Num/Ord to be
-- used with Ints or Integers. So this is perfectly safe.
module G2.Liquid.AddOrdToNum (addOrdToNum) where
import G2.Language
import G2.Language.Monad
import G2.Liquid.Types
-- | Adds an extra field to the Num dict, which contains the Ord
-- dict for the corresponding type. Updates all other code accordingly.
-- Of course, there might be types that have a Num instance, but no Ord
-- instance. These types dicts have the field filled in with Prim Undefined
addOrdToNum :: LHStateM ()
addOrdToNum = do
tc <- typeClasses
num <- numTCM
-- Rewrite dictionary declaration
let tcd = lookupTCDicts num tc
case tcd of
Just tcd' -> mapM_ (uncurry addOrdToNumDictDec) tcd'
Nothing -> return ()
-- Rewrite case statements
mapME addOrdToNumCase
mapMeasuresM addOrdToNumCase
-- Rewrite the types of Num DataCons
mapME changeNumType
mapMeasuresM changeNumType
-- Update Type Environment
changeNumTypeEnv
-- Create a function to extract the Ord Dict
ordDictFunc
addOrdToNumDictDec :: Type -> Id -> LHStateM ()
addOrdToNumDictDec t (Id n _) = do
ord <- ordTCM
me <- lookupE n
case me of
Just e -> do
ordD <- lookupTCDictTC ord t
let ordD' = maybe (Prim Undefined TyBottom) Var ordD
e' <- insertInLamsE (\_ e'' -> return (App e'' ordD')) e
insertE n e'
Nothing -> return ()
addOrdToNumCase :: Expr -> LHStateM Expr
addOrdToNumCase = modifyASTsM addOrdToNumCase'
addOrdToNumCase' :: Expr -> LHStateM Expr
addOrdToNumCase' ce@(Case e i@(Id _ t) a@[Alt (DataAlt dc is) ae])
| (TyCon n ts) <- tyAppCenter t = do
num <- numTCM
ord <- ordTCM
let ordT = mkTyApp (TyCon ord ts:tyAppArgs t)
if num == n then do
ordI <- freshIdN ordT
let is' = is ++ [ordI]
return (Case e i [Alt (DataAlt dc is') ae])
else return (Case e i a)
| otherwise = return ce
addOrdToNumCase' e = return e
changeNumType :: Expr -> LHStateM Expr
changeNumType e = do
num <- numTCM
modifyASTsM (changeNumType' num) e
changeNumType' :: Name -> Expr -> LHStateM Expr
changeNumType' num d@(Data dc)
| (TyCon n _) <- tyAppCenter $ returnType dc
, num == n = return . Data =<< changeNumTypeDC dc
| otherwise = return d
changeNumType' num ce@(Case e i@(Id n _) [Alt (DataAlt dc is) ae])
| num == n = do
dc' <- changeNumTypeDC dc
return (Case e i [Alt (DataAlt dc' is) ae])
| otherwise = return ce
changeNumType' _ e = return e
changeNumTypeDC :: DataCon -> LHStateM DataCon
changeNumTypeDC (DataCon n t) = do
t' <- changeNumTypeType Nothing t
return (DataCon n t')
changeNumTypeType :: Maybe Id -> Type -> LHStateM Type
changeNumTypeType _ (TyForAll b@(NamedTyBndr i) t) = return . TyForAll b =<< changeNumTypeType (Just i) t
changeNumTypeType i (TyForAll b t) = return . TyForAll b =<< changeNumTypeType i t
changeNumTypeType i (TyFun t t') = return . TyFun t =<< changeNumTypeType i t'
changeNumTypeType i t = do
ord <- ordTCM
let ordT = TyCon ord TYPE
case i of
Just i' -> return $ TyFun (TyApp ordT (TyVar i')) t
Nothing -> return (TyFun ordT t)
changeNumTypeEnv :: LHStateM ()
changeNumTypeEnv = do
num <- numTCM
adt <- lookupT num
case adt of
Just adt'@(DataTyCon {data_cons = [dc]}) -> do
dc' <- changeNumTypeDC dc
insertT num $ adt' {data_cons = [dc']}
_ -> return ()
-- Must be called after updating the TypeEnv, with changeNumTypeEnv, so that
-- the Num DataCon has it's correct type
ordDictFunc :: LHStateM ()
ordDictFunc = do
num <- numTCM
let numT = TyCon num TYPE
Just numDC <- lookupT num
let [numDC'] = dataCon numDC
let numA = dataConArgs numDC'
lamI <- freshIdN numT
caseI <- freshIdN numT
binds <- freshIdsN numA
let cOrdBIs = last binds
let e = Lam TermL lamI $ Case (Var lamI) caseI [Alt (DataAlt numDC' binds) (Var cOrdBIs)]
(Id n _) <- lhNumOrdM
insertE n e
return ()