packages feed

g2-0.1.0.0: src/G2/Liquid/AddLHTC.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

module G2.Liquid.AddLHTC ( addLHTC
                                   , addLHTCExprPasses ) where

import G2.Language
import G2.Language.Monad
import G2.Liquid.Types

import qualified Data.Map as M

-- | Adds the LiquidHaskell typeclass to all functions in the ExprEnv, and to
-- the current expression.  This requires:
--   1. Adding Lambda bindings for the LH TC
--   2. Passing the LH TC typeclass to functions
--   3. Updating all type information
addLHTC :: LHStateM ()
addLHTC = do
    mapME addLHTCExprEnv

    (CurrExpr er ce) <- currExpr
    ce' <- addLHTCExprPasses M.empty ce
    putCurrExpr (CurrExpr er ce')

addLHTCExprEnv :: Expr -> LHStateM Expr
addLHTCExprEnv e = do
    e' <- addTypeLams e
    e'' <- addTypeLamsLet e'
    (e''', m) <- addLHTCExprEnvLams [] e''
    addLHTCExprEnvPasses m e'''

-- Rewrites a type to make type lambdas explicit
-- This is needed so that addLHTCExprEnvLams can insert the LH Dict after the type correctly.
-- In generally, it's not always correct to eta-expand Haskell functions, but
-- it is fine here because the type arguments are guaranteed to not be undefined
addTypeLams :: Expr -> LHStateM Expr
addTypeLams e = 
    let
        t = typeOf e
    in
    addTypeLams' t e

addTypeLams' :: Type -> Expr -> LHStateM Expr
addTypeLams' (TyForAll _ t) (Lam TypeL i e) = return . Lam TypeL i =<< addTypeLams' t e
addTypeLams' (TyForAll (NamedTyBndr i) t) e =
    return . Lam TypeL i =<< addTypeLams' t (App e (Type (TyVar i)))
addTypeLams' _ e = return e

-- | Let bindings may be passed Type parameters, but have no type lambdas,
-- so we have to add Lambdas to Let's as well. 
addTypeLamsLet :: Expr -> LHStateM Expr
addTypeLamsLet = modifyM addTypeLamsLet'

addTypeLamsLet' :: Expr -> LHStateM Expr
addTypeLamsLet' (Let be e) = do
    be' <- mapM (\(b, e') -> do
            e'' <- addTypeLams e'
            return (b, e'')
        ) be
    return (Let be' e)
addTypeLamsLet' e = return e

-- Updates a function definition with Lambdas to take the LH TC for each type argument.
addLHTCExprEnvLams :: [Id] -> Expr -> LHStateM (Expr, M.Map Name Id)
addLHTCExprEnvLams is (Lam TypeL i e) = do
    (e', m) <- addLHTCExprEnvLams (i:is) e
    return (Lam TypeL i e', m)
addLHTCExprEnvLams is e = do
    lh <- lhTCM

    let is' = reverse is
    let is'' = map (TyApp (TyCon lh (TyApp TYPE TYPE)) . TyVar) $ is'
    is''' <- freshIdsN is''

    -- Lambdas may be nested in an Expr (for example, if the lambda is in a Let)
    -- So hear we dig down, and recursively apply addLHTCExprEnvLams to any
    -- nested Lambdas
    (le, m') <- addLHTCExprEnvNextLams e

    let e' = foldr (Lam TermL) le is'''

    let m = M.fromList $ zip (map idName is') is'''

    return (e', M.union m m')

addLHTCExprEnvNextLams :: Expr -> LHStateM (Expr, M.Map Name Id)
addLHTCExprEnvNextLams e@(Var _) = return (e, M.empty)
addLHTCExprEnvNextLams e@(Lit _) = return (e, M.empty)
addLHTCExprEnvNextLams e@(Prim _ _) = return (e, M.empty)
addLHTCExprEnvNextLams e@(Data _) = return (e, M.empty)
addLHTCExprEnvNextLams (App e1 e2) = do
    (e1', m1) <- addLHTCExprEnvNextLams e1
    (e2', m2) <- addLHTCExprEnvNextLams e2
    return (App e1' e2', M.union m1 m2)
addLHTCExprEnvNextLams e@(Lam TypeL _ _) = addLHTCExprEnvLams [] e
addLHTCExprEnvNextLams (Lam TermL i e) = do
    (e', m) <- addLHTCExprEnvNextLams e
    return (Lam TermL i e', m)
addLHTCExprEnvNextLams (Let b e) = do
    (b', ms) <- return . unzip =<< mapM (\(b', be) -> do
                                (be', m) <- addLHTCExprEnvNextLams be
                                return ((b', be'), m)
                            ) b
    (e', m) <- addLHTCExprEnvNextLams e

    return (Let b' e', foldr M.union M.empty (m:ms))
addLHTCExprEnvNextLams (Case e i a) = do
    (e', m) <- addLHTCExprEnvNextLams e

    (a', ms) <- return . unzip =<< mapM addLHTCExprEnvNextLamsAlt a

    return (Case e' i a', foldr M.union M.empty (m:ms))
addLHTCExprEnvNextLams e@(Type _) = return (e, M.empty)
addLHTCExprEnvNextLams (Cast e c) = do
    (e', m) <- addLHTCExprEnvNextLams e
    return (Cast e' c, m)
addLHTCExprEnvNextLams e@(Coercion _) = return (e, M.empty)
addLHTCExprEnvNextLams (Tick t e) = do
    (e', m) <- addLHTCExprEnvNextLams e
    return (Tick t e', m)
addLHTCExprEnvNextLams (NonDet es) = do
    (es', ms) <- return . unzip =<< mapM addLHTCExprEnvNextLams es
    return (NonDet es', foldr M.union M.empty ms)
addLHTCExprEnvNextLams e@(SymGen _) = return (e, M.empty)
addLHTCExprEnvNextLams (Assume fc e1 e2) = do
    (e1', m1) <- addLHTCExprEnvNextLams e1
    (e2', m2) <- addLHTCExprEnvNextLams e2
    return (Assume fc e1' e2', M.union m1 m2)
addLHTCExprEnvNextLams (Assert fc e1 e2) = do
    (e1', m1) <- addLHTCExprEnvNextLams e1
    (e2', m2) <- addLHTCExprEnvNextLams e2
    return (Assert fc e1' e2', M.union m1 m2)

addLHTCExprEnvNextLamsAlt :: Alt -> LHStateM (Alt, M.Map Name Id)
addLHTCExprEnvNextLamsAlt (Alt am e) = do
    (e', m) <- addLHTCExprEnvNextLams e
    return (Alt am e', m)

-- Updates each function call, so that it is passed the appropriate LH TC.
-- This requires both:
-- (1) Modifying the expression, to pass the appropriate arguments
-- (2) Modifying the type of the function variable
addLHTCExprEnvPasses :: M.Map Name Id -> Expr -> LHStateM Expr
addLHTCExprEnvPasses m e =
    addLHTCExprPasses m =<< addLHDictToTypes m e

-- We only want to pass the LH TC to Var's (aka function calls)
-- We DO NOT want to put it in DataCons
addLHTCExprPasses :: M.Map Name Id -> Expr -> LHStateM Expr
addLHTCExprPasses m = modifyAppTopE (addLHTCExprPasses' m)

addLHTCExprPasses' :: M.Map Name Id -> Expr -> LHStateM Expr
addLHTCExprPasses' m a@(App _ _)
    | (Data _:_) <- unApp a  = return a
    | otherwise = do
        let a' = unApp a
        a'' <- addLHTCExprPasses'' m [] a'
        return $ mkApp a''

addLHTCExprPasses' _ e = return e

addLHTCExprPasses'' :: M.Map Name Id -> [Expr] -> [Expr] -> LHStateM [Expr]
addLHTCExprPasses'' _ es [] = return $ reverse es
addLHTCExprPasses'' m es (te@(Type t):es') = do
    dict <- lhTCDict m t
    as <- addLHTCExprPasses'' m (dict:es) es'
    return $ te:as
addLHTCExprPasses'' m es (e:es')
    | Var (Id n _) <- e
    , Just dict <- M.lookup n m = do
        as <- addLHTCExprPasses'' m (Var dict:es) es'
        return $ e:as
    | otherwise = do
        as <- addLHTCExprPasses'' m [] es'
        return $ reverse es ++ e:as

-- We want to add a LH Dict Type argument to Var's, but not DataCons or Lambdas.
-- That is: function calls need to be passed the LH Dict but it
-- doesn't need to be passed around in DataCons
addLHDictToTypes :: ASTContainerM e Expr => M.Map Name Id -> e -> LHStateM e
addLHDictToTypes m = modifyASTsM (addLHDictToTypes' m)

addLHDictToTypes' :: M.Map Name Id -> Expr -> LHStateM Expr
addLHDictToTypes' m (Var (Id n t)) = return . Var . Id n =<< addLHDictToTypes'' m t
addLHDictToTypes' _ e = return e

addLHDictToTypes'' :: M.Map Name Id -> Type -> LHStateM Type
addLHDictToTypes'' m t@(TyForAll (NamedTyBndr _) _) = addLHDictToTypes''' m [] t
addLHDictToTypes'' m t = modifyChildrenM (addLHDictToTypes'' m) t

addLHDictToTypes''' :: M.Map Name Id -> [Id] -> Type -> LHStateM Type
addLHDictToTypes''' m is (TyForAll (NamedTyBndr b) t) =
    return . TyForAll (NamedTyBndr b) =<< addLHDictToTypes''' m (b:is) t
addLHDictToTypes''' m is t = do
    lh <- lhTCM
    let is' = reverse is
    let dictT = map (TyApp (TyCon lh (TyApp TYPE TYPE)) . TyVar) is'

    -- The recursive step in addLHDictToTypes'' only kicks in when it is not
    -- at a TyForAll.  So we have to perform recursion here, on the type nested
    -- in the TyForAll's
    t' <- addLHDictToTypes'' m t

    return $ foldr TyFun t' dictT

lhTCDict :: M.Map Name Id -> Type -> LHStateM Expr
lhTCDict m t = do
    lh <- lhTCM
    tc <- typeClassInstTC m lh t
    case tc of
        Just e -> return $ dropAppedLH e
        Nothing -> return $ Var (Id (Name "BAD 2" Nothing 0 Nothing) TyUnknown)
    where
        -- typeClassInstTC adds any needed LH Dict arguments for us.
        -- Unfortunately, the LH Dicts are then added AGAIN, by addLHTCExprEnvPasses
        -- So we just drop the LH Dicts added by typeClassInstTC, and everything works out
        dropAppedLH :: Expr -> Expr
        dropAppedLH (App e t'@(Type _)) = App (dropAppedLH e) t'
        dropAppedLH (App e _) = dropAppedLH e
        dropAppedLH e = e