g2-0.1.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 (..)
, tyInt
, tyInteger
, tyDouble
, tyFloat
, tyBool
, mkTyApp
, mkTyFun
, tyAppCenter
, tyAppArgs
, unTyApp
, mkTyCon
, mkFullAppedTyCon
, (.::)
, (.::.)
, specializes
, hasFuncType
, appendType
, higherOrderFuncs
, isTYPE
, isTyFun
, hasTYPE
, isTyVar
, hasTyBottom
, tyVars
, tyVarIds
, tyVarNames
, hasTyFuns
, isPolyFunc
, numArgs
, ArgType (..)
, argumentTypes
, argTypeToType
, argTypeToLamUse
, spArgumentTypes
, leadingTyForAllBindings
, tyForAllBindings
, anonArgumentTypes
, returnType
, polyIds
, splitTyForAlls
, splitTyFuns
, retype
, mapInTyForAlls
, inTyForAlls
, numTypeArgs
, typeToExpr
, getTyApps
, tyAppsToExpr
) where
import G2.Language.AST
import qualified G2.Language.KnownValues as KV
import G2.Language.Syntax
import qualified Data.Map as M
import qualified Data.List as L
import Data.Monoid hiding (Alt)
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)
tyTYPE :: KV.KnownValues -> Type
tyTYPE _ = TYPE
-- | mkTyFun
-- 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
-- | unTyApp
-- Unravels the application spine.
unTyApp :: Type -> [Type]
unTyApp (TyApp t t') = unTyApp t ++ [t']
unTyApp t = [t]
-- | Typed typeclass.
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 (NamedTyBndr b) (typeOf' m e)
TermL -> TyFun (typeOf' m b) (typeOf' m e)
typeOf' m (Let _ expr) = typeOf' m expr
typeOf' m (Case _ _ (a:_)) = typeOf' m a
typeOf' _ (Case _ _ []) = TyBottom
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 (NamedTyBndr i) t) (Type t':es) =
let
m' = M.insert (idName i) (tyVarRename m t') m
in
appTypeOf m' t es
appTypeOf m (TyForAll (NamedTyBndr _) 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 _ t es = error ("appTypeOf\n" ++ show t ++ "\n" ++ show es ++ "\n\n")
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
((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 (NamedTyBndr b) t) = TyApp (typeOf b) (typeOf' m t)
typeOf' m (TyForAll _ t) = 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 (TyForAll (NamedTyBndr nid) ty) =
if key == nid
then modifyChildren (retype' key new) ty
else TyForAll (NamedTyBndr nid) (modifyChildren (retype' key new) ty)
retype' key new ty = modifyChildren (retype' 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 = fst $ specializes M.empty (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 :: M.Map Name Type -> Type -> Type -> (Bool, M.Map Name Type)
specializes m _ TYPE = (True, m)
specializes m t (TyVar (Id n _)) =
case M.lookup n m of
Just (TyVar _) -> (True, m)
Just t' -> specializes m t t'
Nothing -> (True, M.insert n t m)
specializes m (TyFun t1 t2) (TyFun t1' t2') =
let
(b1, m') = specializes m t1 t1'
(b2, m'') = specializes m' t2 t2'
in
(b1 && b2, m'')
specializes m (TyApp t1 t2) (TyApp t1' t2') =
let
(b1, m') = specializes m t1 t1'
(b2, m'') = specializes m' t2 t2'
in
(b1 && b2, m'')
specializes m (TyCon n _) (TyCon n' _) = (n == n', m)
specializes m (TyFun t1 t2) (TyForAll (AnonTyBndr t1') t2') =
let
(b1, m') = specializes m t1 t1'
(b2, m'') = specializes m' t2 t2'
in (b1 && b2, m'')
specializes m (TyFun t1 t2) (TyForAll (NamedTyBndr _) t2') =
specializes m (TyFun t1 t2) t2'
specializes m (TyForAll (AnonTyBndr t1) t2) (TyFun t1' t2') =
let
(b1, m') = specializes m t1 t1'
(b2, m'') = specializes m' t2 t2'
in (b1 && b2, m'')
specializes m (TyForAll (AnonTyBndr t1) t2) (TyForAll (AnonTyBndr t1') t2') =
let
(b1, m') = specializes m t1 t1'
(b2, m'') = specializes m' t2 t2'
in (b1 && b2, m'')
specializes m (TyForAll (AnonTyBndr t1) t2) (TyForAll (NamedTyBndr _) t2') =
specializes m (TyForAll (AnonTyBndr t1) t2) t2'
specializes m (TyForAll (NamedTyBndr (Id _ t1)) t2) (TyForAll (NamedTyBndr (Id _ t1')) t2') =
let
(b1, m') = specializes m t1 t1'
(b2, m'') = specializes m' t2 t2'
in (b1 && b2, m'')
specializes m t (TyForAll _ t') =
specializes m t t'
specializes m TyUnknown _ = (True, m)
specializes m _ TyUnknown = (True, m)
specializes m TyBottom _ = (True, m)
specializes m _ TyBottom = (False, m)
specializes m t1 t2 = (t1 == t2, m)
hasFuncType :: (Typed t) => t -> Bool
hasFuncType t =
case typeOf t of
(TyFun _ _) -> True
(TyForAll _ _) -> True
_ -> False
-- | appendType
-- Converts the (function) type t1 to return t2
-- appendType (a -> b) c = (a -> b -> c)
appendType :: Type -> Type -> Type
appendType (TyFun t t1) t2 = TyFun t (appendType t1 t2)
appendType t1 t2 = TyFun t1 t2
-- | 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
-- | isPolyFunc
-- Checks if the given function is a polymorphic function
isPolyFunc :: Typed t => t -> Bool
isPolyFunc = isPolyFunc' . typeOf
isPolyFunc' :: Type -> Bool
isPolyFunc' (TyForAll _ _) = True
isPolyFunc' _ = False
-- tyVars
-- Returns a list of all tyVars
tyVars :: ASTContainer m Type => m -> [Type]
tyVars = evalASTs tyVars'
tyVars' :: Type -> [Type]
tyVars' t@(TyVar _) = [t]
tyVars' _ = []
-- | 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
-- | hasTyFuns
hasTyFuns :: ASTContainer m Type => m -> Bool
hasTyFuns = getAny . evalASTs hasTyFuns'
hasTyFuns' :: Type -> Any
hasTyFuns' (TyFun _ _) = Any True
hasTyFuns' _ = Any False
-- hasTyBottom
hasTyBottom :: ASTContainer m Type => m -> Bool
hasTyBottom = getAny . evalASTs hasTyBottom'
hasTyBottom' :: Type -> Any
hasTyBottom' TyBottom = Any True
hasTyBottom' _ = Any False
-- | numArgs
numArgs :: Typed t => t -> Int
numArgs = length . argumentTypes
data ArgType = AnonType Type | NamedType Id
-- | Gives the types of the arguments of the functions
argumentTypes :: Typed t => t -> [Type]
argumentTypes = argumentTypes' . typeOf
argumentTypes' :: Type -> [Type]
argumentTypes' (TyForAll (AnonTyBndr t1) t2) = t1:argumentTypes' t2
argumentTypes' (TyForAll (NamedTyBndr _) 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 (AnonTyBndr t1) t2) = AnonType t1:spArgumentTypes' t2
spArgumentTypes' (TyForAll (NamedTyBndr 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 (NamedTyBndr i) t) = i:leadingTyForAllBindings' t
leadingTyForAllBindings' _ = []
tyForAllBindings :: Typed t => t -> [Id]
tyForAllBindings = tyForAllBindings' . typeOf
tyForAllBindings' :: Type -> [Id]
tyForAllBindings' (TyForAll (NamedTyBndr i) t) = i:tyForAllBindings' t
tyForAllBindings' (TyForAll _ t) = 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
-- | Returns all polymorphic Ids in the given type
polyIds :: Type -> [Id]
polyIds = fst . splitTyForAlls
-- | Turns TyForAll types into a list of type ids
splitTyForAlls :: Type -> ([Id], Type)
splitTyForAlls (TyForAll (NamedTyBndr 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 (NamedTyBndr _) 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 _ _ = []