packages feed

inch-0.1.0: src/Language/Inch/ProgramCheck.lhs

> {-# LANGUAGE GADTs, TypeOperators, FlexibleContexts, RankNTypes #-}

> module Language.Inch.ProgramCheck where

> import Control.Applicative hiding (Alternative)
> import Control.Monad
> import Control.Monad.State
> import Control.Monad.Writer hiding (All)
> import Data.List
> import Data.Traversable
> import Text.PrettyPrint.HughesPJ

> import Language.Inch.BwdFwd
> import Language.Inch.Kind
> import Language.Inch.Type
> import Language.Inch.Syntax
> import Language.Inch.ModuleSyntax
> import Language.Inch.Context
> import Language.Inch.Kit
> import Language.Inch.Error
> import Language.Inch.KindCheck
> import Language.Inch.TypeCheck
> import Language.Inch.Check
> import Language.Inch.PrettyPrinter

> checkModule :: SModule -> [STopDeclaration] -> Contextual Module
> checkModule (Mod mh is ds) xs = do
>     mapM_ makeTyCon xs
>     mapM_ (makeTopBinding True) xs
>     mapM_ checkTopDecl' xs
>     mapM_ makeTyCon ds
>     mapM_ (makeTopBinding False) ds
>     ds' <- concat <$> traverse checkTopDecl' ds
>     return $ Mod mh is ds'
>   where
>     checkTopDecl' ds' = assertContextEmpty *> checkTopDecl ds' <* assertContextEmpty 
>
>     makeTyCon :: STopDeclaration -> Contextual ()
>     makeTyCon (DataDecl t k _ _) = inLocation (text "in data type" <+> text t) $
>         case kindKind k of
>           Ex k' -> do
>             unless (targetsSet k') $ errKindTarget k
>             insertTyCon t (Ex k')
>     makeTyCon (TypeDecl x t) = inLocation (text "in type synonym" <+> text x) $ do
>         Ex t' <- checkTySyn B0 t
>         insertTySyn x t'
>     makeTyCon (CDecl x d)  = insertTyCon x (classKind d)
>     makeTyCon (IDecl _ _)  = return ()
>     makeTyCon (Decl _)     = return ()


> checkTySyn :: Bwd (Ex (Var ())) -> STypeSyn -> Contextual (Ex (TySyn ()))
> checkTySyn b (SSynTy t) = do
>     TK t' _ <- inferKind All b t
>     return . Ex $ SynTy t'
> checkTySyn b (SSynAll x k t) = case kindKind k of                               
>     Ex k' -> do
>         v   <- freshVar (UserVar All) x k'
>         Ex t'  <- checkTySyn (b :< Ex v) t
>         return . Ex $ SynAll x k' (bindTySyn v t')


> makeTopBinding :: Bool -> STopDeclaration -> Contextual ()
> makeTopBinding _ (DataDecl _ _ _ _)  = return ()
> makeTopBinding _ (TypeDecl _ _)      = return ()
> makeTopBinding _ (CDecl _ _)         = return ()
> makeTopBinding _ (IDecl _ _)         = return ()
> makeTopBinding b (Decl d)            = makeBinding b d



> checkTopDecl :: STopDeclaration -> Contextual [TopDeclaration]
> checkTopDecl (DataDecl t k cs ds) = checkDataDecl t k cs ds
> checkTopDecl (TypeDecl x _)       = do
>     Ex t' <- lookupTySyn x
>     return [TypeDecl x t']
> checkTopDecl (CDecl x d) = (\ d' -> [CDecl x d']) <$> checkClassDecl x d
> checkTopDecl (IDecl x d) = (\ d' -> [IDecl x d']) <$> checkInstDecl x d
> checkTopDecl (Decl d) = do
>     ds <- checkInferDecl d
>     unless (all (goodDecl B0) ds) $ erk $
>         unlines ("checkTopDecl: bad declaration" : map (renderMe . fog1) ds)
>     return $ map Decl ds


> checkDataDecl ::  TyConName -> SKind -> [TmConName ::: SType] ->
>                      [String] -> Contextual [TopDeclaration]
> checkDataDecl t k cs ds =  inLocation (text $ "in data type " ++ t) $ 
>   unEx (kindKind k) $ \ k' -> do
>     cs'    <- traverse checkConstructor cs
>     mapM_ (checkDerived k') ds
>     return [DataDecl t k' cs' ds]
>   where
>     checkConstructor :: SConstructor -> Contextual Constructor
>     checkConstructor (c ::: ty) = inLocation (text $ "in constructor " ++ c) $ do
>         ty' <- checkKind KSet All B0 (wrapForall [] ty)
>         unless (ty' `targets` t) $ errConstructorTarget ty
>         ty'' <- goGadtMangle ty'
>         insertTmCon c ty''
>         return (c ::: ty'')
> 
>     checkDerived :: Kind k -> ClassName -> Contextual ()
>     checkDerived l x
>       | x `notElem` derivableClasses = erk $ "Cannot derive instance of " ++ x
>       | otherwise = insertInstDecl x =<< instDecl l (TyCon t l)
>                         (\ s -> TyCon x (KSet :-> KConstraint) `TyApp` s)
>                                                 
>     instDecl :: Kind k -> Ty a k -> (Ty a KSet -> Type KConstraint) ->
>                 Contextual (Type KConstraint)
>     instDecl KSet        u f = return $ f u
>     instDecl (k' :-> l)  u f = do
>         v <- freshVar SysVar "_c" k'
>         instDecl l (u `TyApp` TyVar (wkClosedVar v))
>             (\ s -> Bind All "_c" k' (bindTy v (f s)))
>     instDecl _ _ _ = erk "instDecl: bad kind"

>     derivableClasses = ["Eq", "Ord", "Enum", "Bounded", "Show", "Read"] 


> checkClassDecl :: ClassName -> SClassDeclaration -> Contextual ClassDeclaration
> checkClassDecl x (ClassDecl vks ss ms) = inLocation (text $ "in class " ++ x) $ do
>     vks'  <- traverse checkVK vks
>     ss'   <- traverse (checkKind KConstraint All B0) ss
>     ms'   <- traverse (wongle vks') ms
>     putContext B0
>     let d = ClassDecl vks' ss' ms'
>     insertClassDecl x d
>     return d
>   where
>     checkVK :: VarKind RAW () -> Contextual (VarKind OK ())
>     checkVK (VK v k) = case kindKind k of
>         Ex k' -> flip VK k' <$> fresh (UserVar All) v k' Fixed
>
>     wongle :: [VarKind OK ()] -> TmName ::: SType -> Contextual (TmName ::: Type KSet)
>     wongle xs (m ::: t) = inLocation (text $ "in method " ++ m) $ do
>         t' <- checkKind KSet All B0 (wrapForall (map (\ (VK v _) -> nameToString (varName v)) xs) t)
>         let tsc = allWrapVK xs (Qual (applyVK (TyCon x) xs KConstraint) t')
>         insertBinding m (Just tsc, True)
>         -- mtrace $ "foo " ++ show tb ++ "\nbar " ++ show tsc
>         return $ m ::: t'



> checkInstDecl :: ClassName -> SInstDeclaration -> Contextual InstDeclaration
> checkInstDecl x (InstDecl ts cs zs) =
>   inLocation (text "in instance" <+> text x <+> fsep (map prettyLow ts)) $ do
>       let vs = unions (map (collectUnbound []) ts ++ map (collectUnbound []) cs)
>       vs' <- traverse (\ s -> fresh (UserVar All) s KSet Fixed) vs
>       ClassDecl vks _ _ <- lookupClassDecl x
>       cs' <- traverse checkPrecondition cs 
>       ts' <- traverse (uncurry checkTyKind) (zip vks ts)
>       zs' <- traverse (uncurry (checkMethod ts')) zs
>       insertInstDecl x (allWrapVK (map (\ v -> VK v KSet) vs')
>                            (cs' /=> applys (TyCon x) ts' KConstraint))
>       putContext B0
>       return $ InstDecl ts' cs' zs'
>     where
>       checkPrecondition :: SType -> Contextual (Type KConstraint)
>       checkPrecondition c = do
>           c' <- checkKind KConstraint All B0 c
>           modifyContext (:< Constraint Given c')
>           return c'

>       checkTyKind :: VarKind OK () -> SType -> Contextual (Ex (Ty ()))
>       checkTyKind (VK _ k) t = Ex <$> checkKind k All B0 t 
>
>       checkMethod :: [Ex (Ty ())] -> TmName -> [SAlternative ()] ->
>                          Contextual (TmName, [Alternative ()])
>       checkMethod tys mn as = do
>           (_ ::: qty, _) <- lookupTopBinding mn 
>           (\ as' -> (mn, as')) <$> checkFunDecl (instExTys tys qty) qty mn as
> 
>       instExTys :: [Ex (Ty ())] -> Type k -> Type k
>       instExTys []          t                = t
>       instExTys (Ex u : us) (Bind All _ k t) =
>           hetEq (getTyKind u) k
>               (instExTys us (instTy u t))
>               (error "instExTys: bad")
>       instExTys _ _ = error "instExTys: bad"

> {-
>       instSubst :: [(VarKind OK (), Ex (Ty ()))] -> Var () k -> Type k
>       instSubst [] v = TyVar v
>       instSubst ((VK w _, Ex u) : wus) v
>           | v =?= w    = hetEq (getTyKind u) (varKind v) u (error "instSubst bad")
>                                  
>           | otherwise  = instSubst wus v
> -}



> goGadtMangle :: Type KSet -> Contextual (Type KSet)
> goGadtMangle ty = do
>     (ty', vts) <- runWriterT $ makeEqGadtMangle [] ty
>     return $ foldr bindVarWrap ty' (map fst vts)
>   where
>     bindVarWrap :: Var () KNum -> Type KSet -> Type KSet
>     bindVarWrap a = Bind All (fogVar a) KNum . bindTy a

> makeEqGadtMangle :: [Ex (Var ())] -> Type KSet ->
>     ContextualWriter [(Var () KNum, Maybe TypeNum)] (Type KSet)
> makeEqGadtMangle as ty = do
>     (ty', vts) <- lift $ runWriterT $ gadtMangle as ty
>     tell $ map (\ (a, _) -> (a, Nothing)) vts
>     return $ foldr makeEq ty' vts
>   where
>     makeEq :: (Var () KNum, Maybe TypeNum) -> Type KSet -> Type KSet
>     makeEq (a, Just n)   = Qual (tyPred EL (TyVar a) n)
>     makeEq (_, Nothing)  = id

> gadtMangle :: [Ex (Var ())] -> Type k ->
>     ContextualWriter [(Var () KNum, Maybe TypeNum)] (Type k)
> gadtMangle as (Qual p t) = Qual p <$> gadtMangle as t
> gadtMangle as (Bind b x k t) = do
>     a <- freshVar SysVar x k
>     let as' = case b of
>                   All -> Ex a : as
>                   _   -> as
>         t' = unbindTy a t
>     case getTyKind t' of
>         KSet -> do
>             t'' <- makeEqGadtMangle as' t'
>             return $ Bind b x k (bindTy a t'')
>         l -> errKindNotSet (fogKind l)

> gadtMangle as (TyApp (TyApp Arr s) t) =
>     TyApp (TyApp Arr s) <$> gadtMangle as t

> gadtMangle xs (TyApp f s) = help xs (TyApp f s)
>   where
>     isAllBound :: [Ex (Var ())] -> Type k -> Either String [Ex (Var ())]
>     isAllBound as (TyVar a)
>         | Ex a `elem` as     = Right $ delete (Ex a) as
>         | otherwise          = Left  $ fogVar a ++ "'"
>     isAllBound _  _          = Left "_ga"

>     help :: [Ex (Var ())] -> Type k ->
>                 ContextualWriter [(Var () KNum, Maybe TypeNum)] (Type k)
>     help _  (TyCon c k) = pure $ TyCon c k
>     help as (TyApp g t) = do
>         (t', as') <- warp as t
>         TyApp <$> help as' g <*> pure t'
>     help _ t = error $ "gadtMangle.help: malformed type " ++ show t

>     warp :: [Ex (Var ())] -> Type k ->
>                 ContextualWriter [(Var () KNum, Maybe TypeNum)]
>                     (Type k, [Ex (Var ())])
>     warp as t = case (isAllBound as t, getTyKind t) of
>         (Right as', _) -> pure (t, as')
>         (Left x, KNum) -> do
>             a <- freshVar SysVar x KNum
>             tell [(a, Just t)]
>             return (TyVar a, as)
>         (Left _, _) -> erk "Non-numeric GADT"

> gadtMangle _ t = pure t