{- CAO Compiler
Copyright (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . -}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PatternGuards #-}
{- |
Module : $Header$
Description : CAO type checker.
Copyright : (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
License : GPL
Maintainer : Paulo Silva
Stability : experimental
Portability : non-portable
This module implements the typechecker of the CAO language. This follows the paper ...
-}
module Language.CAO.Typechecker ( tcCaoAST ) where
import Control.Applicative ( (<$>) )
import Control.Monad
import Data.List ( genericLength )
import qualified Data.Set as Set
import Language.CAO.Common.Error
import Language.CAO.Common.Literal
import Language.CAO.Common.Monad
import Language.CAO.Common.Polynomial hiding ((.*.), (.+.))
import Language.CAO.Common.SrcLoc
import Language.CAO.Common.State
import Language.CAO.Common.Utils
import Language.CAO.Common.Var
import Language.CAO.Index
import Language.CAO.Syntax
import Language.CAO.Syntax.Utils
import Language.CAO.Type
import Language.CAO.Type.Utils
import Language.CAO.Typechecker.Check
import Language.CAO.Typechecker.Constraint
import Language.CAO.Typechecker.Expr
import Language.CAO.Typechecker.Heap
import Language.CAO.Typechecker.Index
import Language.CAO.Typechecker.PostProcessor
import Language.CAO.Typechecker.Solver
import Main.Flags (RunMode(..))
--------------------------------------------------------------------------------
--Typechecker
--------------------------------------------------------------------------------
{-# INLINE tcError' #-}
tcError' :: CaoMonad m => ErrorCode Var -> m a
tcError' a = tcError a
--Typechecking Prog ------------------------------------------------------------
mapWithLoc :: CaoMonad m => (a -> m b) -> Located a -> m (Located b)
mapWithLoc f (L loc e) = setSrcLoc loc >> liftM (L loc) (f e)
tcCaoAST :: CaoMonad m => RunMode -> Prog Name -> m (Prog Var, Heap)
tcCaoAST m ast = withTcST $ do
setMode m
ast' <- tcProg ast
h <- getHeap
return (ast', h)
-- Prog ------------------------------------------------------------------------
tcProg :: CaoMonad m => Prog Name -> m (Prog Var)
tcProg (Prog defs _) = liftM2 Prog (mapM (mapWithLoc tcDef) defs) (return Nothing)
-- Definition ------------------------------------------------------------------
tcDef :: CaoMonad m => Def Name -> m (Def Var)
tcDef (VarDef v) = VarDef . fst <$> tcVarDecl Global v
tcDef (ConstDef c) = ensureDepMode $ ConstDef <$> tcConstDecl Global c
tcDef (FunDef f) = FunDef <$> tcFunc f
tcDef (TyDef t) = TyDef <$> tcTypeDef t
-- Func ------------------------------------------------------------------------
tcFunc :: CaoMonad m => Fun Name -> m (Fun Var)
tcFunc (Fun (L loc n) args rtype body) = keepGScope $ do
setSrcLoc loc
(args', ats) <- mapAndUnzipM tcArg args
(rtype', rt) <- tcTypeDecls rtype
(body', st, cc) <- tcStmts rt body
checkFuncReturn st rt
let tct = FuncSig ats rt cc
n' <- checkDecl Global tct n
return (Fun (L loc n') args' rtype' body')
-- Arg -------------------------------------------------------------------------
tcArg :: CaoMonad m => Arg Name -> m (Arg Var, Type Var)
tcArg (Arg (L loc v) td) = do
setSrcLoc loc
(td', tct) <- tcTypeDecl td
v' <- checkDecl Local tct v
return (Arg (L loc v') td', tct)
tcArg (ArgConst (L loc c) td cond) = ensureDepMode $ do
setSrcLoc loc
(td', tct) <- tcIndexDecl td
c' <- checkConstDecl Local c tct Nothing
(cond', i') <- withCond cond
return (ArgConst (L loc c') td' cond', Index c' i' tct)
where
withCond Nothing = return (Nothing, Nothing)
withCond (Just cnd) = do
(cnd', i) <- tcICond cnd
addHypothesis [i]
return (Just cnd', Just i)
--TypeDef ----------------------------------------------------------------------
tcTypeDef :: CaoMonad m => TyDef Name -> m (TyDef Var)
tcTypeDef (TySynDef (L loc n) d) = do
setSrcLoc loc
(st', t) <- tcTypeDecl d
n' <- checkTySyn n t
return (TySynDef (L loc n') st')
tcTypeDef (StructDecl ln@(L loc _) sfs) = do
setSrcLoc loc
(tds, tys) <- mapAndUnzipM tcStructFldDecl sfs
checkStructDecl ln (map fst sfs) tds tys
where
tcStructFldDecl :: CaoMonad m => (Located Name, TyDecl Name) -> m (TyDecl Var, Type Var)
tcStructFldDecl (L lc _, td) = setSrcLoc lc >> tcTypeDecl td
--TypeDecl ---------------------------------------------------------------------
tcTypeDecls :: CaoMonad m => [TyDecl Name] -> m ([TyDecl Var], Type Var)
tcTypeDecls = liftM (mapSnd toTuple) . mapAndUnzipM tcTypeDecl
tcTypeDeclsL :: CaoMonad m => [LTyDecl Name] -> m ([LTyDecl Var], Type Var)
tcTypeDeclsL = liftM (mapSnd toTuple) . mapAndUnzipM (\ (L loc d) -> setSrcLoc loc >> liftM (mapFst (L loc)) (tcTypeDecl d))
tcTypeDecl :: CaoMonad m => TyDecl Name -> m (TyDecl Var, Type Var)
tcTypeDecl IntD =
return (IntD, Int)
tcTypeDecl RIntD =
return (RIntD, RInt)
tcTypeDecl BoolD =
return (BoolD, Bool)
tcTypeDecl (BitsD sign es) = do
(es', i) <- tcIExpr RInt es
checkBitsSize i
return (BitsD sign es', Bits sign i)
tcTypeDecl (ModD (ModNum eb)) = do
(eb', b) <- tcIExpr Int eb
checkModBase b
let tct = Mod Nothing Nothing $ Pol [Mon (CoefI b) EZero]
return (ModD (ModNum eb'), tct)
tcTypeDecl (ModD (ModPol td ti poly)) = do
(td', tct) <- tcTypeDecl td
checkMod tct
tcWarn (PolExtensionWarn poly)
when (isModInt tct) $
tcWarn $ BaseExtensionWarn (getPoly tct)
(ti', poly', mpt) <- checkPolynomial tct ti poly
checkPolyLit poly mpt
return (ModD (ModPol td' ti' poly'), mpt)
tcTypeDecl (VectorD ei td) = do
(ei', i) <- tcIExpr RInt ei
checkVectorSize i
(td', tct) <- tcTypeDecl td
return (VectorD ei' td', Vector i tct)
tcTypeDecl (MatrixD er ec td) = do
(er', r) <- tcIExpr RInt er
(ec', c) <- tcIExpr RInt ec
checkMatrixSize r c
(td', tct) <- tcTypeDecl td
checkAlgebraic tct
let tct' = Matrix r c tct
return (MatrixD er' ec' td', tct')
tcTypeDecl (TySynD (L loc n)) = do
setSrcLoc loc
v <- checkScopeType n
return (TySynD (L loc v), synType $ varType v)
-- Statement -------------------------------------------------------------------
-- XXX: The use of maximumClass in this way is not very efficient
tcStmts :: CaoMonad m => Type Var -> [LStmt Name] -> m ([LStmt Var], Type Var, Class Var)
tcStmts rt stmts = do
(s, t, c) <- fold3M (tcLStmt rt) (:) goTy (\ x y -> maximumClass [x, y]) ([], Bullet, Pure) stmts
s' <- cleanDeadCode s
return (s', t, c)
where
goTy :: Type Var -> Type Var -> Type Var
goTy Bullet t = t
goTy t _ = t
cleanDeadCode :: CaoMonad m => [LStmt id] -> m [LStmt id]
cleanDeadCode [] = return []
cleanDeadCode (e@(L l (Ret _)) : r) = do
unless (null r) $ withSrcLoc l (tcWarn (DeadCodeReturn :: WarningCode Var))
return [e]
cleanDeadCode (e : r) = liftM (e :) $ cleanDeadCode r
tcLStmt :: CaoMonad m => Type Var -> LStmt Name -> m (LStmt Var, Type Var, Class Var)
tcLStmt rt (L loc stmt) = withSrcLoc loc $ do
(stmt', ty, c) <- tcStmt stmt rt
return (L loc stmt', ty, c)
tcStmt :: CaoMonad m => Stmt Name -> Type Var -> m (Stmt Var, Type Var, Class Var)
tcStmt (VDecl vd) _ = do
(vd', c) <- tcVarDecl Local vd
return (VDecl vd', Bullet, c)
tcStmt (CDecl cd) _ = ensureDepMode $ do
cd' <- tcConstDecl Local cd
return (CDecl cd', Bullet, Pure)
tcStmt (Assign lvs es@[L fcl (TyE _ (FunCall (L nl n) exps))]) _ = do
v <- checkScopeFunc n
if isProc (varType v)
then tcProc lvs fcl nl v exps
else tcAssign lvs es
tcStmt (Assign lvs es) _ =
tcAssign lvs es
tcStmt (FCallS fid exps) _ = do
fid' <- checkScopeProc fid
let FuncSig pts frt (Proc wvars) = varType fid'
unless (isNil frt) $ tcError (BadUseException fid' ProcScope)
(exps', pts', cc, _) <- checkArgs exps pts
let wvars' = Set.toList $ wVars cc `Set.union` Set.fromList wvars
fid'' = setType (FuncSig pts' Bullet (Proc wvars)) fid'
return (FCallS fid'' exps', Bullet, Proc wvars')
tcStmt (Ret exps) rt = do
(exps'', ccs) <- checkMultipleAssign exps (fromTuple rt)
return (Ret exps'', rt, maximumClass ccs)
tcStmt (Ite cond istms Nothing) rt = do
(cond', c1) <- tcExpTyp cond Bool
(istms', _, c2) <- keepScope $ tcStmts rt istms
return (Ite cond' istms' Nothing, Bullet, maximumClass [c1, c2])
tcStmt (Ite cond istms (Just estms)) rt = do
(cond', cb) <- tcExpTyp cond Bool
(istms', ist, cc1) <- keepScope $ tcStmts rt istms
(estms', est, cc2) <- keepScope $ tcStmts rt estms
return ( Ite cond' istms' (Just estms')
, if not ((isNil ist) || (isNil est)) then rt else Bullet
, maximumClass [cb, cc1, cc2]
)
tcStmt (While cond wstms) rt = do
(cond', cb) <- tcExpTyp cond Bool
(wstms', _, cc) <- keepScope $ tcStmts rt wstms
return (While cond' wstms', Bullet, maximum [cb, cc])
tcStmt (Seq (SeqIter ivar estart eend eby _) sstms) rt = do
(estart', start) <- tcIExpr RInt estart
(eend', end) <- tcIExpr RInt eend
(eby', by) <- tcBy eby
keepScope $ do
ivar' <- checkConstDecl Local ivar RInt Nothing
let i = IInd $ ivar'
(start', end') = if by > 0 then (start, end) else (end, start)
addHypothesis [start' .<=. i, i .<=. end']
(sstms', sst, c) <- tcStmts rt sstms
return (Seq (SeqIter ivar' estart' eend' eby' (SimpleRng [])) sstms', sst, c)
tcStmt (Nop a) _ = return (Nop a, Bullet, Pure)
tcBy :: CaoMonad m => Maybe (LExpr Name) -> m (Maybe (LExpr Var), Integer)
tcBy Nothing = return (Nothing, 1)
tcBy (Just eb) = do
(eby', rby) <- tcIExpr RInt eb
case rby of
IInt n
| n < 0 -> return (Just (L (getLoc eby') (Lit (ILit n))), n)
| n > 0 -> return (Just eby', n)
_ -> tcError' SeqRangeErr
tcProc :: CaoMonad m => [LVal Name]
-> SrcLoc
-> SrcLoc
-> Var
-> [TLExpr Name]
-> m (Stmt Var, Type Var, Class Var)
tcProc lvs fcl loc fid es = do
(lvs', lvts, cc') <- tcLValues lvs
let FuncSig pts frt (Proc wvars) = varType fid
(es', pts', cc, s) <- checkArgs es pts
let wvars' = Set.toList $ wVars (cc ++ cc') `Set.union` (Set.fromList wvars)
frt' = applySubst s frt
fid' = setType (FuncSig pts' frt' (Proc wvars)) fid
expr = L fcl $ annTyE frt' $ FunCall (L loc fid') es'
sbs <- check (zipWith (.=?>.) (fromTuple frt') lvts) []
let lvts' = case lvts of
[t] -> t
_ -> Tuple lvts
expr' = annotE sbs lvts' expr
return (Assign lvs' [expr'], Bullet, Proc wvars')
tcAssign :: CaoMonad m => [LVal Name] -> [TLExpr Name] -> m (Stmt Var, Type Var, Class Var)
tcAssign lvs es = do
(lvs', tslvs, ca) <- tcLValues lvs
(es'', ccs) <- checkAssign es tslvs
return (Assign lvs' es'', Bullet, maximumClass (ccs ++ ca))
checkAssign :: CaoMonad m => [TLExpr Name] -> [Type Var] -> m ([TLExpr Var], [Class Var])
checkAssign ets lvts
| length lvts > 1 && length ets == 1 = checkTupleAssign (head ets) lvts
| otherwise = checkMultipleAssign ets lvts
checkTupleAssign :: CaoMonad m => TLExpr Name -> [Type Var] -> m ([TLExpr Var], [Class Var])
checkTupleAssign es lvts = do
(es', tes, c, cnstr, tp) <- tcTLExpr es
let ets = fromTuple tes
unless (length lvts == length ets) $ tcError' $ CardinalityException $ AssignCardinalityException TupleAssign
sbs <- withSrcLoc (getLoc es) $ check (cnstr ++ (zipWith (.=?>.) ets lvts)) tp
let es'' = annotE sbs (toTuple lvts) es'
return ([es''], c)
checkMultipleAssign :: CaoMonad m => [TLExpr Name] -> [Type Var] -> m ([TLExpr Var], [Class Var])
checkMultipleAssign ets lvts
| length lvts == length ets = zipWithAndUnzipM tcExpTyp ets lvts
| otherwise = tcError' $ CardinalityException $ AssignCardinalityException MultipleAssign
-- LValue ----------------------------------------------------------------------
tcLValues :: CaoMonad m => [LVal Name] -> m ([LVal Var], [Type Var], [Class Var])
tcLValues = mapAndUnzip3M tcLValue
tcLValue :: CaoMonad m => LVal Name -> m (LVal Var, Type Var, Class Var)
tcLValue (LVVar (L loc x)) = do
setSrcLoc loc
(v, c) <- checkScopeLVar x
return (LVVar (L loc v), varType v, c)
tcLValue (LVStruct lv fi) = do
(lv', lvt, c) <- tcLValue lv
fi' <- checkScopeSField lvt fi
return (LVStruct lv' fi', sfType $ varType fi', c)
tcLValue (LVCont _ lv pat) = do
(lv', lvt, c1) <- tcLValue lv
(pat', tct, c2, cnstr) <- tcAPat lvt pat
unless (null cnstr) $ tcError' $ UnknownErr "Non-empty constraints in a lvalue"
return (LVCont tct lv' pat', tct, maximumClass [c1, c2])
-- XXX: Check literals despite the mode
tcAPat :: CaoMonad m => Type Var -> APat Name -> m (APat Var, Type Var, Class Var, [Constraint])
tcAPat lvt (VectP (CElem e)) = do
(e', mi, c) <- tcAccess e
(tct, cnstr) <- checkVBAccess lvt mi
return (VectP (CElem e'), tct, c, cnstr)
tcAPat lvt (VectP (CRange ei ej)) = do
(ei', i) <- tcIExpr RInt (unTypL ei)
(ej', j) <- tcIExpr RInt (unTypL ej)
(tct, cnstr) <- checkVBRange lvt i j
return (VectP (CRange (annL RInt ei') (annL RInt ej')), tct, Pure, cnstr)
tcAPat lvt (MatP (CElem ei) (CElem ej)) = do
(ei', mi, c1) <- tcAccess ei
(ej', mj, c2) <- tcAccess ej
(tct, cnstr) <- checkMAccess lvt (joinJust mi mj)
return (MatP (CElem ei') (CElem ej'), tct, maximumClass [c1,c2], cnstr)
tcAPat lvt (MatP (CRange ei ej) (CRange ek el)) = do
(ei', i) <- tcIExpr RInt (unTypL ei)
(ej', j) <- tcIExpr RInt (unTypL ej)
(ek', k) <- tcIExpr RInt (unTypL ek)
(el', l) <- tcIExpr RInt (unTypL el)
(tct, cnstr) <- checkMRange lvt i j k l
return (MatP (CRange (annL RInt ei') (annL RInt ej')) (CRange (annL RInt ek') (annL RInt el')), tct, Pure, cnstr)
tcAPat lvt (MatP (CElem ei) (CRange ek el)) = do
(ei', mi, c) <- tcAccess ei
(ek', k) <- tcIExpr RInt (unTypL ek)
(el', l) <- tcIExpr RInt (unTypL el)
(tct, cnstr) <- checkMRow lvt k l mi
return (MatP (CElem ei') (CRange (annL RInt ek') (annL RInt el')), tct, c, cnstr)
tcAPat lvt (MatP (CRange ek el) (CElem ei)) = do
(ei', mi, c) <- tcAccess ei
(ek', k) <- tcIExpr RInt (unTypL ek)
(el', l) <- tcIExpr RInt (unTypL el)
(tct, cnstr) <- checkMCol lvt k l mi
return (MatP (CRange (annL RInt ek') (annL RInt el')) (CElem ei'), tct, c, cnstr)
tcAccess :: CaoMonad m => TLExpr Name -> m (TLExpr Var, Maybe (IExpr Var), Class Var)
tcAccess e = withStrictMode
(do (e', i) <- tcIExpr RInt (unTypL e)
return (annL RInt e', Just i, Pure))
(do (e', c) <- tcExpTyp e RInt
return (e', Nothing, c))
-- VarDeclaration --------------------------------------------------------------
tcVarDecl :: CaoMonad m => Scope -> VarDecl Name -> m (VarDecl Var, Class Var)
tcVarDecl s (VarD (L loc x) b me) = do
(b', tct) <- tcTypeDecl b
(me', c) <- checkMExp tct me
setSrcLoc loc
x' <- checkDecl s tct x
return (VarD (L loc x') b' me', c)
where
checkMExp :: CaoMonad m => Type Var
-> Maybe (TLExpr Name)
-> m (Maybe (TLExpr Var), Class Var)
checkMExp _ Nothing =
return (Nothing, Pure)
checkMExp tct (Just e) = do
(e', cc) <- tcExpTyp e tct
return (Just e', cc)
tcVarDecl s (MultiD xs b) = do
(b', tct) <- tcTypeDecl b
xs' <- mapM (mapWithLoc (checkDecl s tct)) xs
return (MultiD xs' b', Pure)
tcVarDecl s (ContD (L loc x) b es) = do
(b', tct) <- tcTypeDecl b
it <- checkContainerInit tct (genericLength es)
(es'', ccs) <- zipWithAndUnzipM tcExpTyp es (repeat it)
setSrcLoc loc
x' <- checkDecl s tct x
return (ContD (L loc x') b' es'', maximumClass ccs)
-- ConstDeclaration ------------------------------------------------------------
tcConstDecl :: CaoMonad m => Scope -> ConstDecl Name -> m (ConstDecl Var)
tcConstDecl s (ConstD (L loc x) b mo) = do
unless globalOrLocalDef $ tcError' $ UnknownErr "Symbolic constants without value are not allowed in local definitions."
(b', tct) <- tcIndexDecl b
setSrcLoc loc
(x', mo') <- checkMExp tct
return (ConstD (L loc x') b' mo')
where
globalOrLocalDef = case (s, mo) of
(Global, _) -> True
(Local, ConstInit _) -> True
_ -> False
checkMExp :: CaoMonad m => Type Var -> m (Var, ConstAnn Var)
checkMExp tct = case mo of
None -> do
x' <- checkConstDecl s x tct Nothing
return (x', None)
ConstInit me -> do
(e, i) <- tcIExpr tct me
x' <- checkConstDecl s x tct (Just i)
return (x', ConstInit e)
ConstCond me -> do
x' <- checkConstDecl s x tct Nothing
e <- tcCond me
return (x', ConstCond e)
tcConstDecl Global (MultiConstD xs b me) = do
(b', tct) <- tcIndexDecl b
xs' <- mapM (checkVs tct) xs
me' <- mapMaybeM tcCond me
return (MultiConstD xs' b' me')
where
checkVs :: CaoMonad m => Type Var -> Located Name -> m (Located Var)
checkVs tct (L loc x) =
setSrcLoc loc >> L loc <$> checkConstDecl Global x tct Nothing
tcConstDecl Local (MultiConstD _ _ _) =
tcError' $ UnknownErr "Symbolic constants without value are not allowed in local definitions."
tcCond :: CaoMonad m => LExpr Name -> m (LExpr Var)
tcCond e = do
(e', i) <- tcICond e
addHypothesis [i]
return e'
-- Exp -------------------------------------------------------------------------
tcExpTyp :: CaoMonad m => TLExpr Name -> Type Var -> m (TLExpr Var, Class Var)
tcExpTyp e t = do
(e', et, c, cnstr, tp) <- tcTLExpr e
sbs <- withSrcLoc (getLoc e) $ check (cnstr ++ [et .=?>. t]) tp
let e'' = annotE sbs t e'
return (e'', maximumClass c)
tcTLExpr :: CaoMonad m => TLExpr Name -> m (TLExpr Var, Type Var, [Class Var], [Constraint], [TypePred])
tcTLExpr (L loc (TyE _ e)) = do
setSrcLoc loc
(e', tct, c, cnst, tp) <- tcExpr e
return (L loc (annTyE tct e'), tct, c, cnst, tp)
-- Empty list means pure functions
tcExpr :: CaoMonad m => Expr Name -> m (Expr Var, Type Var, [Class Var], [Constraint], [TypePred])
tcExpr (Lit lit) = do
(lit', tct) <- tcLiteral lit
return (Lit lit', tct, [], [], [])
tcExpr (Var x) = do
(x', c) <- checkScopeVar x
return (Var x', varType x', [c], [], [])
tcExpr (FunCall (L loc fid) exps) = do
setSrcLoc loc
fid' <- checkScopeFunc fid
unless (nsFunName fid') $ tcError (ScopeException fid FuncScope)
let ft@(FuncSig pts frt c) = varType fid'
when (isProc ft) $ tcError (BadUseException fid' ProcScope)
(exps', pts', cs, s) <- checkArgs exps pts
let frt' = applySubst s frt
fid'' = setType (FuncSig pts' frt' c) fid'
return ( FunCall (L loc fid'') exps'
, frt'
, c:cs
, [], []
)
tcExpr (StructProj sexp fi) = do
(sexp', sexpt, c, cnstr, tp) <- tcTLExpr sexp
fi' <- checkScopeSField sexpt fi
return ( StructProj sexp' fi'
, sfType $ varType fi'
, c
, cnstr, tp
)
tcExpr (UnaryOp op e) = tcUnaryExpr op e
tcExpr (BinaryOp op e1 e2) = tcBinaryExpr op e1 e2
tcExpr (Access e1 pat) = do
(e1', et1, c1, cnstr1, tp1) <- tcTLExpr e1
(pat', tct, c2, cnstrp) <- tcAPat et1 pat
return ( Access e1' pat'
, tct
, c2 : c1
, cnstr1 ++ cnstrp
, tp1
)
tcExpr (Cast b td e1) = do
(e1', et1, c, cnstr, tp) <- tcTLExpr e1
(td', tct) <- tcTypeDeclsL td
return (Cast b td' e1', tct, c, cnstr ++ [et1 .=>>. tct], tp)
tcUnaryExpr :: CaoMonad m => UOp -> TLExpr Name
-> m (Expr Var, Type Var, [Class Var], [Constraint], [TypePred])
tcUnaryExpr op e = case op of
Sym -> do
tct <- TyVar <$> nextTyVarId
(e', et, c, cnstr, tp) <- tcTLExpr e
return (UnaryOp Sym e', tct, c, cnstr ++ [ToAlgebraic tct et], Algebraic tct : tp)
Not -> do
(e', et, c, cnstr, tp) <- tcTLExpr e
return (UnaryOp Not e', Bool, c, cnstr ++ [et .=?>. Bool], tp)
BNot -> do
(e', et, c, cnstr, tp) <- tcTLExpr e
return (UnaryOp BNot e', et, c, cnstr, BitsT et : tp)
tcBinaryExpr :: CaoMonad m => BinOp Name -> TLExpr Name -> TLExpr Name
-> m (Expr Var, Type Var, [Class Var], [Constraint], [TypePred])
tcBinaryExpr bop e1 e2 = case bop of
ArithOp op -> do
(e1', et1, c1, cnstr1, tp1) <- tcTLExpr e1
(e2', et2, c2, cnstr2, tp2) <- tcTLExpr e2
(tct, cnstr', tp') <- checkAOp op et1 et2
return ( BinaryOp (ArithOp op) e1' e2'
, tct
, c1 ++ c2
, cnstr1 ++ cnstr2 ++ cnstr'
, tp1 ++ tp2 ++ tp'
)
CmpOp _ op -> do
(e1', et1, c1, cnstr1, tp1) <- tcTLExpr e1
(e2', et2, c2, cnstr2, tp2) <- tcTLExpr e2
tct <- nextTyVarId
let (cnstr, tct') = if isEqNeq op
then ([UnifiesC (TyVar tct) et1 et2], TyVar tct)
else ([et1 .=?>. IntVar tct, et2 .=?>. IntVar tct], IntVar tct)
return ( BinaryOp (CmpOp tct' op) e1' e2'
, Bool, c1 ++ c2
, cnstr1 ++ cnstr2 ++ cnstr
, tp1 ++ tp2
)
BoolOp op -> do
(e1', et1, c1, cnstr1, tp1) <- tcTLExpr e1
(e2', et2, c2, cnstr2, tp2) <- tcTLExpr e2
return (BinaryOp (BoolOp op) e1' e2'
, Bool
, c1 ++ c2
, cnstr1 ++ cnstr2 ++ [et1 .=?>. Bool, et2 .=?>. Bool]
, tp1 ++ tp2)
BitOp op -> do
(e1', et1, c1, cnstr1, tp1) <- tcTLExpr e1
(e2', et2, c2, cnstr2, tp2) <- tcTLExpr e2
return ( BinaryOp (BitOp op) e1' e2'
, et1 -- Arbitrary choice (et1 == et2)
, c1 ++ c2
, cnstr1 ++ cnstr2 ++ [et1 .=?=. et2]
, tp1 ++ tp2
)
BitsSROp op -> do
(e1', et1, c1, cnstr1, tp1) <- tcTLExpr e1
(e2', et2, c2, cnstr2, tp2) <- tcTLExpr e2
return ( BinaryOp (BitsSROp op) e1' e2'
, et1
, c1 ++ c2
, cnstr1 ++ cnstr2 ++ [et2 .=?>. RInt]
, tp1 ++ tp2 ++ [BitsOrVector et1]
)
Concat -> do
(e1', et1, c1, cnstr1, tp1) <- tcTLExpr e1
(e2', et2, c2, cnstr2, tp2) <- tcTLExpr e2
tct <- TyVar <$> nextTyVarId
return (BinaryOp Concat e1' e2'
, tct
, c1 ++ c2
, cnstr1 ++ cnstr2 ++ [Conc tct et1 et2]
, tp1 ++ tp2)
-- Literal ---------------------------------------------------------------------
-- XXX: Introduce signed literals
tcLiteral :: CaoMonad m => Literal Name -> m (Literal Var, Type Var)
tcLiteral (BLit b) =
return (BLit b, Bool)
tcLiteral (ILit i) = do
tvi <- IntVar <$> nextTyVarId
return (ILit i, tvi)
tcLiteral (BSLit sig val) =
return (BSLit sig val, Bits sig $ IInt $ genericLength val)
tcLiteral (PLit pol) = do
(pol', tct) <- tcPolynomial pol
return (PLit pol', tct)
-- Polynomial ------------------------------------------------------------------
tcPolynomial :: CaoMonad m => Pol Name -> m (Pol Var, Type Var)
tcPolynomial (Pol ms) = do
(ms', mst, cnts) <- tcMonomials ms
tct <- TyVar <$> nextTyVarId
sbs <- check (UnifiesL tct mst : cnts) []
let tct' = subst' sbs tct
return (Pol ms', tct')
-- Monomial --------------------------------------------------------------------
tcMonomials :: CaoMonad m => [Mon Name] -> m ([Mon Var], [Type Var], [Constraint])
tcMonomials = fold3M tcMonomial (:) (:) (++) ([], [], [])
-- XXX: It is possible to check if the mod value is a valid one.
-- Something like 0 <= i' < mod(tct)
tcMonomial :: CaoMonad m => Mon Name -> m (Mon Var, Type Var, [Constraint])
tcMonomial (Mon (CoefI i) EZero) = do
tct <- ModVar <$> nextTyVarId
i' <- wellFormedExpr i
return (Mon (CoefI i') EZero, tct, [])
tcMonomial (Mon (CoefI i) (MExpI n e)) = do
(n', tct) <- checkScopeInd n
i' <- wellFormedExpr i
return (Mon (CoefI i') (MExpI n' e), tct, [])
tcMonomial (Mon (CoefP p) EZero) = do
(p', tct) <- tcPolynomial p
return (Mon (CoefP p') EZero, tct, [])
tcMonomial (Mon (CoefP p) (MExpI n e))= do
(n', tct) <- checkScopeInd n
(p', pt) <- tcPolynomial p
return(Mon (CoefP p') (MExpI n' e), tct, [pt .=?>. extractBaseType tct])
--------------------------------------------------------------------------------
-- Checking functions
--------------------------------------------------------------------------------
checkPolyLit :: CaoMonad m => Pol Name -> Type Var -> m ()
checkPolyLit pol tp = do
(_, tct) <- tcPolynomial pol
_ <- check [tct .=?>. tp] []
return ()
checkArgs :: CaoMonad m => [TLExpr Name] -> [Type Var] -> m ([TLExpr Var], [Type Var], [Class Var], [(Var, IExpr Var)])
checkArgs [] [] = return ([], [], [], [])
checkArgs (e:exps) (ti:tis) = do
(e', te, c, s) <- checkArg e ti
(exps', tes, cs, ss) <- checkArgs exps (substAux s tis)
return (e':exps', te:tes, c:cs, maybe ss (:ss) s)
where
-- XXX: inline this?
substAux s t = maybe t (\s' -> map (Language.CAO.Syntax.Utils.subst s') t) s
-- length exps == length pts
checkArgs _ _ = tcError' $ CardinalityException ParamsCardinalityException
--TODO: Apply the substitution to the returned type
-- The callee function 'checkArgs' already applies the substitution to the expected type
checkArg :: CaoMonad m => TLExpr Name -> Type Var -> m (TLExpr Var, Type Var, Class Var, Maybe (Var, IExpr Var))
checkArg e (Index v cond ti) = do -- TODO: How to improve this?
(e', c) <- tcExpTyp e ti
(_, i) <- tcIExpr ti (unTypL e)
let s = (v, i)
case cond of
Just cond' ->
valid [Language.CAO.Syntax.Utils.subst s cond'] (error "<>: Not satisfied condition")
_ -> return ()
return (e', ti, c, Just s)
checkArg e ti = do
(e', c) <- tcExpTyp e ti
return (e', ti, c, Nothing)
--------------------------------------------------------------------------------
-- TODO: Refactor to somewhere
applySubst :: [(Var, IExpr Var)] -> Type Var -> Type Var
applySubst [] t = t
applySubst (s:xs) t = applySubst xs (Language.CAO.Syntax.Utils.subst s t)