g2-0.2.0.0: src/G2/Language/Typing.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Type Checker
-- Provides type checking capabilities over G2 Language.
module G2.Language.Typing
( Typed (..)
, PresType (..)
, checkType
, tyInt
, tyInteger
, tyDouble
, tyFloat
, tyBool
, tyChar
, tyRational
, tyList
, tyMaybe
, tyUnit
, mkTyApp
, mkTyFun
, tyAppCenter
, tyAppArgs
, unTyApp
, mkTyCon
, mkFullAppedTyCon
, (.::)
, (.::.)
, specializes
, specializes'
, hasFuncType
, higherOrderFuncs
, isTYPE
, isTyFun
, hasTYPE
, isTyVar
, tyVarIds
, tyVarNames
, numArgs
, replaceTyVar
, applyTypeMap
, applyTypeHashMap
, ArgType (..)
, argumentTypes
, argTypeToType
, argTypeToLamUse
, spArgumentTypes
, leadingTyForAllBindings
, tyForAllBindings
, anonArgumentTypes
, returnType
, splitTyForAlls
, splitTyFuns
, retype
, retypeRespectingTyForAll
, mapInTyForAlls
, inTyForAlls
, numTypeArgs
, typeToExpr
, getTyApps
, tyAppsToExpr
, isADTType
, isPrimType
) where
import qualified G2.Data.UFMap as UF
import G2.Language.AST
import qualified G2.Language.KnownValues as KV
import G2.Language.Syntax
import qualified Data.HashMap.Lazy as HM
import qualified Data.Map as M
import Data.Maybe
import qualified Data.List as L
import Data.Monoid hiding (Alt)
import Control.Monad
tyInt :: KV.KnownValues -> Type
tyInt kv = TyCon (KV.tyInt kv) (tyTYPE kv)
tyInteger :: KV.KnownValues -> Type
tyInteger kv = TyCon (KV.tyInteger kv) (tyTYPE kv)
tyDouble :: KV.KnownValues -> Type
tyDouble kv = TyCon (KV.tyDouble kv) (tyTYPE kv)
tyFloat :: KV.KnownValues -> Type
tyFloat kv = TyCon (KV.tyFloat kv) (tyTYPE kv)
tyBool :: KV.KnownValues -> Type
tyBool kv = TyCon (KV.tyBool kv) (tyTYPE kv)
tyChar :: KV.KnownValues -> Type
tyChar kv = TyCon (KV.tyChar kv) (tyTYPE kv)
tyRational :: KV.KnownValues -> Type
tyRational kv = TyCon (KV.tyRational kv) (tyTYPE kv)
tyList :: KV.KnownValues -> Type
tyList kv = TyCon (KV.tyList kv) (TyFun TYPE TYPE)
tyMaybe :: KV.KnownValues -> Type
tyMaybe kv = TyCon (KV.tyMaybe kv) (TyFun TYPE TYPE)
tyUnit :: KV.KnownValues -> Type
tyUnit kv = TyCon (KV.tyUnit kv) (TyFun TYPE TYPE)
tyTYPE :: KV.KnownValues -> Type
tyTYPE _ = TYPE
-- | Turns the Expr list into an application spine
mkTyFun :: [Type] -> Type
mkTyFun [] = error "mkTyFun: empty list"
mkTyFun [t] = t
mkTyFun (t1:ts) = TyFun t1 (mkTyFun ts)
tyAppCenter :: Type -> Type
tyAppCenter (TyApp t _) = tyAppCenter t
tyAppCenter t = t
tyAppArgs :: Type -> [Type]
tyAppArgs (TyApp t t') = tyAppArgs t ++ [t']
tyAppArgs _ = []
mkTyApp :: [Type] -> Type
mkTyApp [] = TYPE
mkTyApp (t:[]) = t
mkTyApp (t1:t2:ts) = mkTyApp (TyApp t1 t2 : ts)
mkTyCon :: Name
-> [Type]
-> Kind
-> Type
mkTyCon n ts k = mkTyApp $ TyCon n k:ts
-- | Makes a fully applied TyCon.
-- Since the TyCon is fully applied, we can figure out its kind based on it's
-- arguments and result kind.
mkFullAppedTyCon :: Name
-> [Type] -- ^ Type arguments
-> Kind -- ^ Result kind
-> Type
mkFullAppedTyCon n ts k =
let
tsk = mkTyFun $ map typeOf ts ++ [k]
in
mkTyApp $ TyCon n tsk:ts
-- | Unravels the application spine of (nested) `TyApp`s.
unTyApp :: Type -> [Type]
unTyApp (TyApp t t') = unTyApp t ++ [t']
unTyApp t = [t]
-- | Typeclass for things that have type information.
class Typed a where
typeOf :: a -> Type
typeOf = typeOf' M.empty
typeOf' :: M.Map Name Type -> a -> Type
instance Typed Id where
typeOf' m (Id _ ty) = tyVarRename m ty
instance Typed Lit where
typeOf (LitInt _) = TyLitInt
typeOf (LitFloat _) = TyLitFloat
typeOf (LitDouble _) = TyLitDouble
typeOf (LitChar _) = TyLitChar
typeOf (LitString _) = TyLitString
typeOf (LitInteger _) = TyLitInt
typeOf' _ t = typeOf t
instance Typed DataCon where
typeOf' _ (DataCon _ ty) = ty
instance Typed Alt where
typeOf' m (Alt _ expr) = typeOf' m expr
instance Typed Expr where
typeOf' m (Var v) = typeOf' m v
typeOf' m (Lit lit) = typeOf' m lit
typeOf' _ (Prim _ ty) = ty
typeOf' m (Data dcon) = typeOf' m dcon
typeOf' m a@(App _ _) =
let
as = passedArgs a
t = typeOf' m $ appCenter a
in
appTypeOf M.empty t as
typeOf' m (Lam u b e) =
case u of
TypeL -> TyForAll b (typeOf' m e)
TermL -> TyFun (typeOf' m b) (typeOf' m e)
typeOf' m (Let _ expr) = typeOf' m expr
typeOf' _ (Case _ _ t _) = t
typeOf' _ (Type _) = TYPE
typeOf' m (Cast _ (_ :~ t')) = tyVarRename m t'
typeOf' m (Coercion (_ :~ t')) = tyVarRename m t'
typeOf' m (Tick _ e) = typeOf' m e
typeOf' m (NonDet (e:_)) = typeOf' m e
typeOf' _ (NonDet []) = TyBottom
typeOf' _ (SymGen _ t) = t
typeOf' m (Assert _ _ e) = typeOf' m e
typeOf' m (Assume _ _ e) = typeOf' m e
passedArgs :: Expr -> [Expr]
passedArgs = reverse . passedArgs'
passedArgs' :: Expr -> [Expr]
passedArgs' (App e e') = e':passedArgs' e
passedArgs' _ = []
appCenter :: Expr -> Expr
appCenter (App a _) = appCenter a
appCenter e = e
appTypeOf :: M.Map Name Type -> Type -> [Expr] -> Type
appTypeOf m (TyForAll i t) (Type t':es) =
let
m' = M.insert (idName i) (tyVarRename m t') m
in
appTypeOf m' t es
appTypeOf m (TyForAll _ t) (_:es) = appTypeOf m t es
appTypeOf m (TyFun _ t) (_:es) = appTypeOf m t es
appTypeOf m t [] = tyVarRename m t
appTypeOf m (TyVar (Id n _)) es =
case M.lookup n m of
Just t -> appTypeOf m t es
Nothing -> error ("appTypeOf: Unknown TyVar")
appTypeOf _ TyBottom _ = TyBottom
appTypeOf _ TyUnknown _ = TyUnknown
appTypeOf _ t es = error ("appTypeOf\n" ++ show t ++ "\n" ++ show es ++ "\n\n")
checkType :: Expr -> Maybe (UF.UFMap Name Type)
checkType = check' UF.empty
check' :: UF.UFMap Name Type -> Expr -> Maybe (UF.UFMap Name Type)
check' uf (Var (Id _ _)) = Just uf
check' uf (Lit _) = Just uf
check' uf (Prim _ _) = Just uf
check' uf (Data _) = Just uf
check' uf (App e1 e2) =
let
t1 = case typeOf e1 of
TyFun t _ -> t
TyForAll (Id _ t) _ -> t
_ -> error "check: Unexpected type in App"
t2 = typeOf e2
in
fmap snd (unify t1 t2 uf) >>= flip check' e1 >>= flip check' e2
check' uf (Lam u b e) = check' uf e
check' uf (Let b e) = foldM check' uf (map snd b) >>= flip check' e
check' uf (Case e _ _ alts) = foldM check' uf (map altExpr alts) >>= flip check' e
check' uf (Type t) = Just uf
check' uf (Cast e (t :~ t')) = check' uf e
check' uf (Coercion (t :~ t')) = Just uf
check' uf (Tick _ t) = check' uf t
check' uf (NonDet es) = foldM check' uf es
check' uf (SymGen _ _) = Just uf
check' uf (Assert _ e1 e2) = check' uf e1 >>= flip check' e2
check' uf (Assume _ e1 e2) = check' uf e1 >>= flip check' e2
check' _ _ = error "check'"
unify :: Type -> Type -> UF.UFMap Name Type -> Maybe (Type, UF.UFMap Name Type)
unify t@(TyVar (Id n1 _)) (TyVar (Id n2 _)) uf
| Just t1 <- UF.lookup n1 uf
, Just t2 <- UF.lookup n2 uf =
case unify t1 t2 uf of
Just (t, uf') -> Just (t, UF.join (\_ _ -> t) n1 n2 uf')
Nothing -> Nothing
| otherwise = Just (t, UF.join (error "unify: impossible, no need to join") n1 n2 uf)
unify (TyVar (Id n _)) t uf
| Just t1 <- UF.lookup n uf = unify t1 t uf
| otherwise = Just (t, UF.insert n t uf)
unify t (TyVar (Id n _)) uf
| Just t2 <- UF.lookup n uf = unify t t2 uf
| otherwise = Just (t, UF.insert n t uf)
unify (TyFun t1 t2) (TyFun t1' t2') uf = do
(ft1, uf') <- unify t1 t1' uf
(ft2, uf'') <- unify t2 t2' uf'
return (TyFun ft1 ft2, uf'')
unify (TyApp t1 t2) (TyApp t1' t2') uf = do
(ft1, uf') <- unify t1 t1' uf
(ft2, uf'') <- unify t2 t2' uf'
return (TyApp ft1 ft2, uf'')
unify (TyCon n1 k1) (TyCon n2 k2) uf | n1 == n2 = do
(fk, uf') <- unify k1 k2 uf
return (TyCon n1 fk, uf')
unify (TyForAll i t1) (TyForAll i2 t2) uf = do
(t, uf') <- unify t1 t2 uf
return (TyForAll i t, uf')
unify t1 t2 uf | t1 == t2 = return (t1, uf)
| otherwise = Nothing
instance Typed Type where
typeOf' _ (TyVar (Id _ t)) = t
typeOf' _ (TyFun _ _) = TYPE
typeOf' m (TyApp t1 t2) =
let
ft = typeOf' m t1
at = typeOf' m t2
in
case (ft, at) of
((TyForAll _ t2'), _) -> t2'
((TyFun _ t2'), _) -> t2'
((TyApp t1' _), _) -> t1'
_ -> error $ "Overapplied Type\n" ++ show t1 ++ "\n" ++ show t2 ++ "\n\n" ++ show ft ++ "\n" ++ show at
typeOf' _ (TyCon _ t) = t
typeOf' m (TyForAll b t) = TyApp (typeOf b) (typeOf' m t)
typeOf' _ TyLitInt = TYPE
typeOf' _ TyLitFloat = TYPE
typeOf' _ TyLitDouble = TYPE
typeOf' _ TyLitChar = TYPE
typeOf' _ TyLitString = TYPE
typeOf' _ TYPE = TYPE
typeOf' _ TyBottom = TyBottom
typeOf' _ TyUnknown = TyUnknown
newtype PresType = PresType Type deriving (Show, Read)
instance Typed PresType where
typeOf' _ (PresType t) = t
-- | Retyping
-- We look to see if the type we potentially replace has a TyVar whose Id is a
-- match on the target key that we want to replace.
retype :: (ASTContainer m Type, Show m) => Id -> Type -> m -> m
retype key new e = modifyContainedASTs (retype' key new) $ e
retype' :: Id -> Type -> Type -> Type
retype' key new (TyVar test) = if key == test then new else TyVar test
retype' key new ty = modifyChildren (retype' key new) ty
retypeRespectingTyForAll :: (ASTContainer m Type, Show m) => Id -> Type -> m -> m
retypeRespectingTyForAll key new = modifyContainedASTs (retypeRespectingTyForAll' key new)
retypeRespectingTyForAll' :: Id -> Type -> Type -> Type
retypeRespectingTyForAll' i _ t@(TyForAll ni _) | i == ni = t
retypeRespectingTyForAll' key new (TyVar test) = if idName key == idName test then new else TyVar test
retypeRespectingTyForAll' key new ty = modifyChildren (retypeRespectingTyForAll' key new) ty
tyVarRename :: (ASTContainer t Type) => M.Map Name Type -> t -> t
tyVarRename m = modifyASTs (tyVarRename' m)
tyVarRename' :: M.Map Name Type -> Type -> Type
tyVarRename' m t@(TyVar (Id n _)) = M.findWithDefault t n m
tyVarRename' _ t = t
-- | Returns if the first type given is a specialization of the second,
-- i.e. if given t1, t2, returns true iff t1 :: t2
(.::) :: Typed t => t -> Type -> Bool
t1 .:: t2 = isJust $ specializes (typeOf t1) t2
{-# INLINE (.::) #-}
-- | Checks if the first type is equivalent to the second type.
-- That is, @e@ has type @t1@ iff @e@ has type @t2@.
(.::.) :: Type -> Type -> Bool
t1 .::. t2 = PresType t1 .:: t2 && PresType t2 .:: t1
{-# INLINE (.::.) #-}
specializes :: Type -> Type -> Maybe (M.Map Name Type)
specializes = specializes' M.empty
specializes' :: M.Map Name Type -> Type -> Type -> Maybe (M.Map Name Type)
specializes' m _ TYPE = Just m
specializes' m t (TyVar (Id n vt)) =
case M.lookup n m of
Just t' | t == t' -> Just m
| otherwise -> Nothing
Nothing -> M.insert n t <$> specializes' m (typeOf t) vt
specializes' m (TyFun t1 t2) (TyFun t1' t2') = do
m' <- specializes' m t1 t1'
specializes' m' t2 t2'
specializes' m (TyApp t1 t2) (TyApp t1' t2') = do
m' <- specializes' m t1 t1'
specializes' m' t2 t2'
specializes' m (TyCon n _) (TyCon n' _) = if n == n' then Just m else Nothing
specializes' m (TyForAll (Id _ t1) t2) (TyForAll (Id _ t1') t2') = do
m' <- specializes' m t1 t1'
specializes' m' t2 t2'
specializes' m t (TyForAll _ t') =
specializes' m t t'
specializes' m TyUnknown _ = Just m
specializes' m _ TyUnknown = Just m
specializes' m TyBottom _ = Just m
specializes' _ _ TyBottom = Nothing
specializes' m t1 t2 = if t1 == t2 then Just m else Nothing
replaceTyVar :: ASTContainer e Type => Name -> Type -> e -> e
replaceTyVar n t = modifyASTs (replaceTyVar' n t)
replaceTyVar' :: Name -> Type -> Type -> Type
replaceTyVar' n t (TyVar (Id n' _)) | n == n' = t
replaceTyVar' _ _ t = t
applyTypeMap :: ASTContainer e Type => M.Map Name Type -> e -> e
applyTypeMap m = modifyASTs (applyTypeMap' m)
applyTypeMap' :: M.Map Name Type -> Type -> Type
applyTypeMap' m (TyVar (Id n _))
| Just t <- M.lookup n m = t
applyTypeMap' _ t = t
applyTypeHashMap :: ASTContainer e Type => HM.HashMap Name Type -> e -> e
applyTypeHashMap m = modifyASTs (applyTypeHashMap' m)
applyTypeHashMap' :: HM.HashMap Name Type -> Type -> Type
applyTypeHashMap' m (TyVar (Id n _))
| Just t <- HM.lookup n m = t
applyTypeHashMap' _ t = t
hasFuncType :: (Typed t) => t -> Bool
hasFuncType t =
case typeOf t of
(TyFun _ _) -> True
(TyForAll _ _) -> True
_ -> False
-- | higherOrderFuncs
-- Returns all internal higher order function types
higherOrderFuncs :: Typed t => t -> [Type]
higherOrderFuncs = higherOrderFuncs' . typeOf
higherOrderFuncs' :: Type -> [Type]
higherOrderFuncs' = eval higherOrderFuncs''
higherOrderFuncs'' :: Type -> [Type]
higherOrderFuncs'' (TyFun t@(TyFun _ _) _) = [t]
higherOrderFuncs'' _ = []
isTYPE :: Type -> Bool
isTYPE TYPE = True
isTYPE (TyCon (Name "TYPE" _ _ _) _) = True
isTYPE _ = False
hasTYPE :: Type -> Bool
hasTYPE TYPE = True
hasTYPE (TyCon (Name "TYPE" _ _ _) _) = True
hasTYPE (TyFun t t') = hasTYPE t || hasTYPE t'
hasTYPE (TyApp t t') = hasTYPE t || hasTYPE t'
hasTYPE _ = False
isTyVar :: Type -> Bool
isTyVar (TyVar _) = True
isTyVar _ = False
isTyFun :: Type -> Bool
isTyFun (TyFun _ _) = True
isTyFun _ = False
-- | Returns a list of all tyVars Ids
tyVarIds :: ASTContainer m Type => m -> [Id]
tyVarIds = evalASTs tyVarIds'
tyVarIds' :: Type -> [Id]
tyVarIds' (TyVar i) = [i]
tyVarIds' _ = []
-- | Returns a list of all tyVars Names
tyVarNames :: ASTContainer m Type => m -> [Name]
tyVarNames = map idName . tyVarIds
-- | Computes the number of type and value level arguments
numArgs :: Typed t => t -> Int
numArgs = length . argumentTypes
data ArgType = AnonType Type | NamedType Id deriving (Show, Read)
-- | Gives the types of the arguments of the functions
argumentTypes :: Typed t => t -> [Type]
argumentTypes = argumentTypes' . typeOf
argumentTypes' :: Type -> [Type]
argumentTypes' (TyForAll _ t2) = TYPE:argumentTypes' t2
argumentTypes' (TyFun t1 t2) = t1:argumentTypes' t2
argumentTypes' _ = []
argTypeToType :: ArgType -> Type
argTypeToType (AnonType t) = t
argTypeToType (NamedType i) = TyVar i
argTypeToLamUse :: ArgType -> LamUse
argTypeToLamUse (AnonType _) = TermL
argTypeToLamUse (NamedType _) = TypeL
spArgumentTypes :: Typed t => t -> [ArgType]
spArgumentTypes = spArgumentTypes' . typeOf
spArgumentTypes' :: Type -> [ArgType]
spArgumentTypes' (TyForAll i t2) = NamedType i:spArgumentTypes' t2
spArgumentTypes' (TyFun t1 t2) = AnonType t1:spArgumentTypes' t2
spArgumentTypes' _ = []
leadingTyForAllBindings :: Typed t => t -> [Id]
leadingTyForAllBindings = leadingTyForAllBindings' . typeOf
leadingTyForAllBindings' :: Type -> [Id]
leadingTyForAllBindings' (TyForAll i t) = i:leadingTyForAllBindings' t
leadingTyForAllBindings' _ = []
tyForAllBindings :: Typed t => t -> [Id]
tyForAllBindings = tyForAllBindings' . typeOf
tyForAllBindings' :: Type -> [Id]
tyForAllBindings' (TyForAll i t) = i:tyForAllBindings' t
tyForAllBindings' (TyFun t t') = tyForAllBindings' t ++ tyForAllBindings t'
tyForAllBindings' _ = []
anonArgumentTypes :: Typed t => t -> [Type]
anonArgumentTypes = anonArgumentTypes' . typeOf
anonArgumentTypes' :: Type -> [Type]
anonArgumentTypes' (TyForAll _ t) = anonArgumentTypes' t
anonArgumentTypes' (TyFun t1 t2) = t1:anonArgumentTypes' t2
anonArgumentTypes' _ = []
-- | Gives the return type if the given function type is fully saturated
returnType :: Typed t => t -> Type
returnType = returnType' . typeOf
returnType' :: Type -> Type
returnType' (TyForAll _ t) = returnType' t
returnType' (TyFun _ t) = returnType' t
returnType' t = t
-- | Turns TyForAll types into a list of type ids
splitTyForAlls :: Type -> ([Id], Type)
splitTyForAlls (TyForAll i t) =
let
(i', t') = splitTyForAlls t
in
(i:i', t')
splitTyForAlls t = ([], t)
-- | Turns TyFun types into a list of types
splitTyFuns :: Type -> [Type]
splitTyFuns (TyFun t t') = t:splitTyFuns t'
splitTyFuns t = [t]
-- | Nests a new type in TyForAlls
mapInTyForAlls :: (Type -> Type) -> Type -> Type
mapInTyForAlls f (TyForAll b t) = TyForAll b $ mapInTyForAlls f t
mapInTyForAlls f t = f t
-- | Extracts the type inside TyForAlls, recursively
inTyForAlls :: Type -> Type
inTyForAlls (TyForAll _ t) = inTyForAlls t
inTyForAlls t = t
numTypeArgs :: Typed t => t -> Int
numTypeArgs = numTypeArgs' . typeOf
numTypeArgs' :: Type -> Int
numTypeArgs' (TyForAll _ t) = 1 + numTypeArgs' t
numTypeArgs' _ = 0
-- | Converts nested TyApps into a list of Expr-level Types
typeToExpr :: Type -> [Expr]
typeToExpr (TyApp f t) = [Type t] ++ (typeToExpr f)
typeToExpr _ = []
-- | Find nested tyApps, if any, in the given Type
getTyApps :: Type -> Maybe Type
getTyApps (TyForAll _ t) = getTyApps t
getTyApps (TyFun t _) = getTyApps t
getTyApps t@(TyApp _ _) = Just t
getTyApps _ = Nothing
-- | Given sequence of nested tyApps e.g. tyApp (tyApp ...) ...), returns list of expr level Types, searching through [Id,Type] list in the process
tyAppsToExpr :: Type -> [(Id, Type)] -> [Expr]
tyAppsToExpr (TyApp t (TyVar tVarId)) bindings = exprs ++ newTyExpr
where
newTyExpr =
case (L.find (\(i, _) -> (tVarId == i)) bindings) of -- search list of (Id, Type) to find corresponding Type, and convert to expr
(Just (_, ty)) -> [Type ty]
Nothing -> []
exprs = tyAppsToExpr t bindings
tyAppsToExpr (TyApp t1 t2) bindings = exprs ++ newTyExpr
where
newTyExpr = [Type t2]
exprs = tyAppsToExpr t1 bindings
tyAppsToExpr _ _ = []
-- | Returns True if Type represents an ADT
isADTType :: Type -> Bool
isADTType t =
let tCenter = tyAppCenter t
in case tCenter of
(TyCon _ _) -> True
_ -> False
isPrimType :: Type -> Bool
isPrimType TyLitInt = True
isPrimType TyLitFloat = True
isPrimType TyLitDouble = True
isPrimType TyLitChar = True
isPrimType TyLitString = True
isPrimType _ = False