packages feed

hoq-0.1.0.0: src/TypeChecking/Definitions/DataTypes.hs

{-# LANGUAGE RecursiveDo #-}

module TypeChecking.Definitions.DataTypes
    ( typeCheckDataType
    ) where

import Control.Monad
import Control.Monad.Fix
import Data.List

import Syntax.Expr as E
import Syntax.Term as T
import Syntax.ErrorDoc
import TypeChecking.Monad
import TypeChecking.Context
import TypeChecking.Expressions
import TypeChecking.Definitions.Patterns
import TypeChecking.Definitions.Conditions
import TypeChecking.Definitions.Termination
import Normalization

type Tele = [([Arg], Expr)]

typeCheckDataType :: MonadFix m => PIdent -> Tele -> [(PIdent,Tele)] -> [(E.Pattern, Expr)] -> TCM m ()
typeCheckDataType p@(PIdent (lc,dt)) params cons conds = mdo
    let lcons = length cons
    (SomeEq ctx, dataType@(Type dtTerm _)) <- checkTele Nil params $ Closed (T.Universe NoLevel)
    addDataTypeCheck p dataType lcons
    cons' <- forW (zip cons [0..]) $ \((con@(PIdent (lc,conName)),tele),i) -> do
        (_, Type conType conLevel) <- checkTele ctx tele $ Closed $ DataType dt lcons []
        checkPositivity p (nf WHNF conType)
        let conTerm = T.Con i lc conName (map snd $ filter (\(c,_) -> c == conName) conds') []
        return $ Just (con, conTerm, Type conType conLevel)
    forM_ cons' $ \(con, te, Type ty lvl) ->
        addConstructorCheck con dt lcons (abstractTermInCtx ctx te) (abstractTermInCtx ctx ty) lvl
    conds' <- forW conds $ \(E.Pattern (E.PIdent (lc,con)) pats,expr) ->
        case find (\(PIdent (_,c),_,_) -> c == con) cons' of
            Nothing -> do
                warn [notInScope lc "data constructor" con]
                return Nothing
            Just (_, _, ty) -> do
                (bf, TermsInCtx ctx' _ ty', rtpats) <- typeCheckPatterns ctx (nfType WHNF ty) pats
                when bf $ warn [emsgLC lc "Absurd patterns are not allowed in conditions" enull]
                (term, _) <- typeCheckCtx (ctx +++ ctx') expr (Just ty')
                let scope = closed (abstractTermInCtx ctx' term)
                throwErrors (checkTermination con rtpats scope)
                return $ Just (con, (rtpats, scope))
    lift $ deleteDataType dt
    let lvls = map (\(_, _, Type _ lvl) -> lvl) cons'
        lvl = if null lvls then NoLevel else maximum lvls
    lift $ addDataType dt (Type (replaceLevel dtTerm lvl) lvl) lcons
    forM_ cons' $ \(_, T.Con i lc' conName conConds [], _) -> do
        warn $ checkConditions lc (Closed $ T.Con i lc' conName conConds []) conConds

checkTele :: (Monad m, Eq a) => Ctx String Type String a -> Tele -> Closed Term
    -> TCM m (SomeEq (Ctx String Type String), Type a)
checkTele ctx [] (Closed term) = return (SomeEq ctx, Type term NoLevel)
checkTele ctx ((args,expr):tele) term = do
    (r1, Type t1 _) <- typeCheckCtx ctx expr Nothing
    lvl1 <- checkIsType ctx expr (nf WHNF t1)
    case extendCtx (map unArg args) Nil (Type r1 lvl1) of
        SomeEq ctx' -> do
            (rctx, Type r2 lvl2) <- checkTele (ctx +++ ctx') tele term
            return (rctx, Type (T.Pi (Type r1 lvl1) (abstractTermInCtx ctx' r2) lvl2) $ max lvl1 lvl2)

replaceLevel :: Term a -> Level -> Term a
replaceLevel (T.Pi r1 r2 lvl2) lvl = T.Pi r1 (replaceLevelScope r2) lvl2
  where
    replaceLevelScope :: Scope String Term a -> Scope String Term a
    replaceLevelScope (ScopeTerm t) = ScopeTerm (replaceLevel t lvl)
    replaceLevelScope (Scope v t) = Scope v (replaceLevelScope t)
replaceLevel _ lvl = T.Universe lvl

checkPositivity :: (Eq a, Monad m) => PIdent -> Term a -> EDocM m ()
checkPositivity dt (T.Pi (Type a _) b _) = checkNoNegative dt (nf WHNF a) >> checkPositivityScope b
  where
    checkPositivityScope :: (Eq a, Monad m) => Scope String Term a -> EDocM m ()
    checkPositivityScope (ScopeTerm t) = checkPositivity dt (nf WHNF t)
    checkPositivityScope (Scope v t) = checkPositivityScope t
checkPositivity _ _ = return ()

checkNoNegative :: (Eq a, Monad m) => PIdent -> Term a -> EDocM m ()
checkNoNegative dt (T.Pi (Type a _) b _) = checkNoDataType dt a >> checkNoNegativeScope b
  where
    checkNoNegativeScope :: (Eq a, Monad m) => Scope String Term a -> EDocM m ()
    checkNoNegativeScope (ScopeTerm t) = checkNoNegative dt (nf WHNF t)
    checkNoNegativeScope (Scope v t) = checkNoNegativeScope t
checkNoNegative _ _ = return ()

checkNoDataType :: Monad m => PIdent -> Term a -> EDocM m ()
checkNoDataType (PIdent (lc,dt)) t = when (dt `elem` collectDataTypes t) $
    throwError [emsgLC lc "Data type is not strictly positive" enull]

collectDataTypes :: Term a            -> [String]
collectDataTypes T.Var{}               = []
collectDataTypes (T.App e1 e2)         = collectDataTypes e1 ++ collectDataTypes e2
collectDataTypes (T.Lam (Scope1 _ e))  = collectDataTypes e
collectDataTypes (T.Pi (Type e _) s _) = collectDataTypes e ++ go s
  where
    go :: Scope s Term a -> [String]
    go (ScopeTerm t) = collectDataTypes t
    go (Scope _   s) = go s
collectDataTypes (T.Con _ _ _ _ as)    = as >>= collectDataTypes
collectDataTypes FunCall{}             = []
collectDataTypes FunSyn{}              = []
collectDataTypes (DataType d _ as)     = d : (as >>= collectDataTypes)
collectDataTypes T.Universe{}          = []
collectDataTypes T.Interval            = []
collectDataTypes ICon{}                = []
collectDataTypes (T.Path _ me1 es)     = maybe [] collectDataTypes me1 ++ (es >>= collectDataTypes)
collectDataTypes (PCon me)             = maybe [] collectDataTypes me
collectDataTypes (T.At _ _ e3 e4)      = collectDataTypes e3 ++ collectDataTypes e4
collectDataTypes (T.Coe es)            = es >>= collectDataTypes
collectDataTypes (T.Iso es)            = es >>= collectDataTypes
collectDataTypes (T.Squeeze es)        = es >>= collectDataTypes