packages feed

g2-0.2.0.0: src/G2/Liquid/Types.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}

module G2.Liquid.Types ( LHOutput (..)
                       , CounterExample (..)
                       , HigherOrderFuncCall
                       , Measures
                       , LHState (..)
                       , LHStateM (..)
                       , NamingM (..)
                       , ExState (..)
                       , AnnotMap (..)
                       , Abstracted (..)
                       , AbstractedInfo (..)
                       , Assumptions
                       , Posts
                       , TyVarBags
                       , InstFuncs

                       -- For compatibility with new LH versions
#if MIN_VERSION_liquidhaskell(0,8,10)
                       , GhcInfo, GhcSrc, GhcSpec
                       , pattern GI, giSpec, giSrc
                       , pattern SP, gsSig, gsData, gsQual, gsVars, gsTerm
                       , pattern Src, giTarget, giCbs, giDefVars
#endif

                       , tcValuesM

                       , mapAbstractedFCs
                       , mapAbstractedInfoFCs
                       , consLHState
                       , deconsLHState
                       , measuresM
                       , lhKnownValuesM
                       , lhRenamedTCM
                       , assumptionsM
                       , postsM
                       , annotsM
                       , tyVarBagsM
                       , runLHStateM
                       , evalLHStateM
                       , execLHStateM
                       , lookupMeasure
                       , lookupMeasureM
                       , insertMeasureM
                       , mapMeasuresM
                       , mapMeasuresWithKeyM
                       , putMeasuresM
                       , lookupAssumptionM
                       , insertAssumptionM
                       , mapAssumptionsM
                       , lookupPostM
                       , insertPostM
                       , mapPostM
                       , lookupAnnotM
                       , insertAnnotM
                       , mapAnnotsExpr
                       , lookupRenamedTCDict
                       , insertTyVarBags
                       , lookupTyVarBags
                       , setTyVarBags
                       , getTyVarBags
                       , insertInstFuncs
                       , lookupInstFuncs
                       , setInstFuncs
                       , getInstFuncs

                       , andM
                       , orM
                       , notM
                       , iffM
                       , lhTCM
                       , lhOrdTCM
                       , lhEqM
                       , lhNeM
                       , lhLtM
                       , lhLeM
                       , lhGtM
                       , lhGeM
                       , lhLtE
                       , lhLeE
                       , lhGtE
                       , lhGeE

                       , lhPlusM
                       , lhMinusM
                       , lhTimesM
                       , lhDivM
                       , lhNegateM
                       , lhModM
                       , lhFromIntegerM
                       , lhToIntegerM

                       , lhFromRationalM

                       , lhToRatioFuncM
                       
                       , lhNumTCM
                       , lhNumOrdM

                       , lhAndE
                       , lhOrE
                       
                       , lhPPM
                       , lhOrdM ) where

import Data.Coerce
import qualified Data.HashMap.Lazy as HM
import Data.List
import qualified Data.Map as M
import qualified Data.Text as T
import qualified Control.Monad.State.Lazy as SM

import qualified G2.Language as L
import qualified G2.Language.ExprEnv as E
import qualified G2.Language.KnownValues as KV
import G2.Language.Monad
import G2.Language.TypeClasses

import G2.Liquid.TCValues

#if MIN_VERSION_liquidhaskell(0,8,10)
import qualified Language.Haskell.Liquid.Types as LT (TargetInfo (..), TargetSrc (..), TargetSpec (..))
#else
import Language.Haskell.Liquid.Types (GhcInfo)
#endif

import Language.Haskell.Liquid.Constraint.Types
import Language.Fixpoint.Types.Constraints

import Data.Monoid ((<>))

-- LiquidHaskell renamed these types.
-- This preserves compatibility with the old version of LiquidHaskell, and the new versions of LiquidHaskell.
#if MIN_VERSION_liquidhaskell(0,8,10)
type GhcInfo = LT.TargetInfo
type GhcSrc = LT.TargetSrc
type GhcSpec= LT.TargetSpec

pattern GI{giSrc,giSpec} = LT.TargetInfo giSrc giSpec


pattern Src
  { giIncDir
  , giTarget
  , giTargetMod
  , giCbs
  , gsTcs
  , gsCls
  , giDerVars
  , giImpVars
  , giDefVars
  , giUseVars
  , gsExports
  , gsFiTcs
  , gsFiDcs
  , gsPrimTcs
  , gsQualImps
  , gsAllImps
  , gsTyThings
  } = LT.TargetSrc giIncDir giTarget giTargetMod giCbs gsTcs gsCls giDerVars giImpVars
                   giDefVars giUseVars gsExports gsFiTcs gsFiDcs gsPrimTcs gsQualImps
                   gsAllImps gsTyThings




pattern SP 
  { gsSig
  , gsQual
  , gsData
  , gsName
  , gsVars
  , gsTerm
  , gsRefl
  , gsLaws
  , gsImps        
  , gsConfig                  
  } = LT.TargetSpec gsSig gsQual gsData gsName gsVars gsTerm gsRefl gsLaws gsImps gsConfig                  

#endif

data LHOutput = LHOutput { ghcI :: GhcInfo
                         , cgI :: CGInfo
                         , solution :: FixSolution }

type HigherOrderFuncCall = L.FuncCall

data CounterExample = DirectCounter Abstracted
                                    [Abstracted]
                                    [HigherOrderFuncCall] -- ^ Symbolic higher order functions evaluated while creating the counterexample 
                    | CallsCounter Abstracted -- ^ The caller, abstracted result
                                   Abstracted -- ^ The callee
                                  [Abstracted]
                                  [HigherOrderFuncCall] -- ^ Symbolic higher order functions evaluated while creating the counterexample
                    deriving (Eq, Show, Read)

type Measures = L.ExprEnv

-- | Assumptions include:
--  (1) a list of `(LamUse, Id)` used to refer to the expression arguments in the assumption
--  (2) assumptions to wrap individual arguments (in particular, higher order arguments).
--  (3) an assumption regarding the whole expression,
type Assumptions = M.Map L.Name ([(L.LamUse, L.Id)], [Maybe L.Expr], L.Expr)
type Posts = M.Map L.Name L.Expr

newtype AnnotMap =
    AM { unAnnotMap :: HM.HashMap L.Span [(Maybe T.Text, L.Expr)] }
    deriving (Eq, Show, Read)

-- Abstracted values
data Abstracted = Abstracted { abstract :: L.FuncCall
                             , real :: L.FuncCall
                             , hits_lib_err_in_real :: Bool
                             , func_calls_in_real :: [L.FuncCall] }
                             deriving (Eq, Show, Read)

data AbstractedInfo = AbstractedInfo { init_call :: Abstracted
                                     , abs_violated :: Maybe Abstracted
                                     , abs_calls :: [Abstracted]
                                     , ai_all_calls :: [L.FuncCall]

                                     , ai_higher_order_calls :: [L.FuncCall] }
                                     deriving (Eq, Show, Read)

mapAbstractedFCs :: (L.FuncCall -> L.FuncCall) ->  Abstracted -> Abstracted
mapAbstractedFCs f (Abstracted { abstract = a
                               , real = r
                               , hits_lib_err_in_real = err
                               , func_calls_in_real = fcr }) =
    Abstracted { abstract = f a
               , real = f r
               , hits_lib_err_in_real = err
               , func_calls_in_real = map f fcr}

mapAbstractedInfoFCs :: (L.FuncCall -> L.FuncCall) ->  AbstractedInfo -> AbstractedInfo
mapAbstractedInfoFCs f (AbstractedInfo { init_call = ic, abs_violated = av, abs_calls = ac, ai_all_calls= allc, ai_higher_order_calls = a_higher}) =
    AbstractedInfo { init_call = mapAbstractedFCs f ic
                   , abs_violated = fmap (mapAbstractedFCs f) av
                   , abs_calls = map (mapAbstractedFCs f) ac
                   , ai_all_calls = map f allc
                   , ai_higher_order_calls = map f a_higher }

instance L.ASTContainer Abstracted L.Expr where
    containedASTs ab = L.containedASTs (abstract ab) ++ L.containedASTs (real ab)
    modifyContainedASTs f (Abstracted { abstract = a
                                      , real = r
                                      , hits_lib_err_in_real = err
                                      , func_calls_in_real = fcir }) =
        Abstracted { abstract = L.modifyContainedASTs f a
                   , real = L.modifyContainedASTs f r
                   , hits_lib_err_in_real = err
                   , func_calls_in_real = L.modifyContainedASTs f fcir}

instance L.ASTContainer Abstracted L.Type where
    containedASTs ab = L.containedASTs (abstract ab) ++ L.containedASTs (real ab)
    modifyContainedASTs f (Abstracted { abstract = a
                                      , real = r
                                      , hits_lib_err_in_real = err
                                      , func_calls_in_real = fcir }) =
        Abstracted { abstract = L.modifyContainedASTs f a
                   , real = L.modifyContainedASTs f r
                   , hits_lib_err_in_real = err
                   , func_calls_in_real = L.modifyContainedASTs f fcir }

instance L.Named Abstracted where
    names a = L.names (abstract a) <> L.names (real a) <> L.names (func_calls_in_real a)
    rename old new a = a { abstract = L.rename old new (abstract a)
                         , real = L.rename old new (real a)
                         , func_calls_in_real = L.rename old new (func_calls_in_real a) }
    renames hm a = a { abstract = L.renames hm (abstract a)
                     , real = L.renames hm (real a)
                     , func_calls_in_real = L.renames hm (func_calls_in_real a) }

instance L.Named AbstractedInfo where
    names a = L.names (init_call a) <> L.names (abs_violated a) <> L.names (abs_calls a) <> L.names (ai_all_calls a) <> L.names (ai_higher_order_calls a)
    rename old new a = AbstractedInfo { init_call = L.rename old new (init_call a)
                                      , abs_violated = L.rename old new (abs_violated a)
                                      , abs_calls = L.rename old new (abs_calls a)
                                      , ai_all_calls = L.rename old new (ai_all_calls a)
                                      , ai_higher_order_calls = L.rename old new (ai_higher_order_calls a) }
    renames hm a = AbstractedInfo { init_call = L.renames hm (init_call a)
                                  , abs_violated = L.renames hm (abs_violated a)
                                  , abs_calls = L.renames hm (abs_calls a)
                                  , ai_all_calls = L.renames hm (ai_all_calls a)
                                  , ai_higher_order_calls = L.renames hm (ai_higher_order_calls a) }

-- | See G2.Liquid.TyVarBags
type TyVarBags = M.Map L.Name [L.Id]
type InstFuncs = M.Map L.Name L.Id

-- [LHState]
-- measures is an extra expression environment, used to build Assertions.
-- This distinction between functions for code, and functions for asserts is important because
-- Assertions should not themselves contain assertions.  A measure function
-- may be used both in code and in an assertion, but should only have it's
-- refinement type added in the code
--  
-- Invariant: Internally, functions in the State ExprEnv need to have LH Dict arguments added,
-- (see addLHTCExprEnv) whereas functions in the measures should be created with the LH Dicts
-- already accounted for.


-- | LHState
-- Wraps a State, along with the other information needed to parse
-- LiquidHaskell ASTs
data LHState = LHState { state :: L.State [L.FuncCall]
                       , measures :: Measures
                       , lh_known_values :: KV.KnownValues
                       , lh_tcs :: L.TypeClasses
                       , tcvalues :: TCValues
                       , assumptions :: Assumptions
                       , posts :: Posts
                       , annots :: AnnotMap
                       , tyvar_bags :: TyVarBags
                       , inst_funcs :: InstFuncs
                       } deriving (Eq, Show, Read)

consLHState :: L.State [L.FuncCall] -> Measures -> KV.KnownValues -> L.TypeClasses -> TCValues -> LHState
consLHState s meas kv tc tcv =
    LHState { state = s
            , measures = meas
            , lh_known_values = kv
            , lh_tcs = tc
            , tcvalues = tcv
            , assumptions = M.empty
            , posts = M.empty
            , annots = AM HM.empty
            , tyvar_bags = M.empty
            , inst_funcs = M.empty }

deconsLHState :: LHState -> L.State [L.FuncCall]
deconsLHState (LHState { state = s
                       , measures = meas }) =
    s { L.expr_env = E.union (L.expr_env s) meas }

measuresM :: LHStateM Measures
measuresM = do
    (lh_s, _) <- SM.get
    return $ measures lh_s

lhKnownValuesM :: LHStateM KV.KnownValues
lhKnownValuesM = do
    (lh_s, _) <- SM.get
    return $ lh_known_values lh_s

lhRenamedTCM :: LHStateM L.TypeClasses
lhRenamedTCM = do
    (lh_s, _) <- SM.get
    return $ lh_tcs lh_s

assumptionsM :: LHStateM Assumptions
assumptionsM = do
    (lh_s, _) <- SM.get
    return $ assumptions lh_s

postsM :: LHStateM Posts
postsM = do
    (lh_s, _) <- SM.get
    return $ posts lh_s

annotsM :: LHStateM AnnotMap
annotsM = do
    (lh_s, _) <- SM.get
    return $ annots lh_s

tyVarBagsM :: LHStateM TyVarBags
tyVarBagsM = do
    (lh_s, _) <- SM.get
    return $ tyvar_bags lh_s


newtype LHStateM a = LHStateM { unSM :: (SM.State (LHState, L.Bindings) a) } deriving (Applicative, Functor, Monad)

instance SM.MonadState (LHState, L.Bindings) LHStateM where
    state f = LHStateM (SM.state f) 

instance NamingM (LHState, L.Bindings) LHStateM where
    nameGen = readRecord $ L.name_gen . snd
    putNameGen = rep_name_genM

instance ExprEnvM (LHState, L.Bindings) LHStateM where
    exprEnv = readRecord $ expr_env . fst
    putExprEnv = rep_expr_envM

instance ExState (LHState, L.Bindings) LHStateM where
    typeEnv = readRecord $ type_env . fst
    putTypeEnv = rep_type_envM

    knownValues = readRecord $ known_values . fst
    putKnownValues = rep_known_valuesM

    typeClasses = readRecord $ type_classes . fst
    putTypeClasses = rep_type_classesM

instance FullState (LHState, L.Bindings) LHStateM where
    currExpr = readRecord $ curr_expr . fst
    putCurrExpr = rep_curr_exprM

    inputNames = readRecord $ L.input_names . snd
    fixedInputs = readRecord $ L.fixed_inputs . snd

    pathConds = readRecord $ path_conds . fst
    putPathConds = rep_path_condsM

runLHStateM :: LHStateM a -> LHState -> L.Bindings -> (a, (LHState, L.Bindings))
runLHStateM (LHStateM s) lh_s b = SM.runState s (lh_s, b)

evalLHStateM :: LHStateM a -> LHState -> L.Bindings -> a
evalLHStateM s = (\lh_s b -> fst (runLHStateM s lh_s b))

execLHStateM :: LHStateM a -> LHState -> L.Bindings -> (LHState, L.Bindings)
execLHStateM s = (\lh_s b -> snd (runLHStateM s lh_s b))

liftState :: (L.State [L.FuncCall] -> a) -> LHState -> a
liftState f = f . state

expr_env :: LHState -> L.ExprEnv
expr_env = liftState L.expr_env

rep_expr_envM :: L.ExprEnv -> LHStateM ()
rep_expr_envM eenv = do
    (lh_s, b) <- SM.get
    let s = state lh_s
    let s' = s {L.expr_env = eenv}
    SM.put $ (lh_s {state = s'}, b)

type_env :: LHState -> L.TypeEnv
type_env = liftState L.type_env

rep_type_envM :: L.TypeEnv -> LHStateM ()
rep_type_envM tenv = do
    (lh_s, b) <- SM.get
    let s = state lh_s
    let s' = s {L.type_env = tenv}
    SM.put $ (lh_s {state = s'}, b)

rep_name_genM :: L.NameGen -> LHStateM ()
rep_name_genM ng = do
    (lh_s, b) <- SM.get
    let s = state lh_s
    let b' = b {L.name_gen = ng}
    SM.put $ (lh_s {state = s}, b')

known_values :: LHState -> L.KnownValues
known_values = liftState L.known_values

rep_known_valuesM :: L.KnownValues -> LHStateM ()
rep_known_valuesM kv = do
    (lh_s, b) <- SM.get
    let s = state lh_s
    let s' = s {L.known_values = kv}
    SM.put $ (lh_s {state = s'}, b)

curr_expr :: LHState -> L.CurrExpr
curr_expr = liftState L.curr_expr

rep_curr_exprM :: L.CurrExpr -> LHStateM ()
rep_curr_exprM ce = do
    (lh_s, b) <- SM.get
    let s = state lh_s
    let s' = s {L.curr_expr = ce}
    SM.put $ (lh_s {state = s'}, b)

type_classes :: LHState -> L.TypeClasses
type_classes = liftState L.type_classes

rep_type_classesM :: L.TypeClasses -> LHStateM ()
rep_type_classesM tc = do
    (lh_s,b) <- SM.get
    let s = state lh_s
    let s' = s {L.type_classes = tc}
    SM.put $ (lh_s {state = s'}, b)

path_conds :: LHState -> L.PathConds
path_conds = liftState L.path_conds

rep_path_condsM :: L.PathConds -> LHStateM ()
rep_path_condsM ce = do
    (lh_s, b) <- SM.get
    let s = state lh_s
    let s' = s {L.path_conds = ce}
    SM.put $ (lh_s {state = s'}, b)

liftLHState :: (LHState -> a) -> LHStateM a
liftLHState f = do
    (lh_s, _) <- SM.get
    return (f lh_s) 

lookupMeasure :: L.Name -> LHState -> Maybe L.Expr
lookupMeasure n = E.lookup n . measures

lookupMeasureM :: L.Name -> LHStateM (Maybe L.Expr)
lookupMeasureM n = liftLHState (lookupMeasure n)

insertMeasureM :: L.Name -> L.Expr -> LHStateM ()
insertMeasureM n e = do
    (lh_s,b) <- SM.get
    let meas = measures lh_s
    let meas' = E.insert n e meas
    SM.put $ (lh_s {measures = meas'}, b)

mapMeasuresM :: (L.Expr -> LHStateM L.Expr) -> LHStateM ()
mapMeasuresM f = do
    (s@(LHState { measures = meas }), b) <- SM.get
    meas' <- E.mapM f meas
    SM.put $ (s { measures = meas' }, b)

mapMeasuresWithKeyM :: (L.Name -> L.Expr -> LHStateM L.Expr) -> LHStateM ()
mapMeasuresWithKeyM f = do
    (s@(LHState { measures = meas }), b) <- SM.get
    meas' <- E.mapWithKeyM f meas
    SM.put $ (s { measures = meas' }, b)

putMeasuresM :: Measures -> LHStateM ()
putMeasuresM meas = do
    (s, b) <- SM.get
    SM.put $ (s { measures = meas }, b)

lookupAssumptionM :: L.Name -> LHStateM (Maybe ([(L.LamUse, L.Id)], [Maybe L.Expr], L.Expr))
lookupAssumptionM n = liftLHState (M.lookup n . assumptions)

insertAssumptionM :: L.Name -> ([(L.LamUse, L.Id)], [Maybe L.Expr], L.Expr) -> LHStateM ()
insertAssumptionM n e = do
    (lh_s, b) <- SM.get
    let assumpt = assumptions lh_s
    let assumpt' = M.insert n e assumpt
    SM.put $ (lh_s {assumptions = assumpt'}, b)

mapAssumptionsM :: (L.Expr -> LHStateM L.Expr) -> LHStateM ()
mapAssumptionsM f = do
    (s@(LHState { assumptions = assumpt }), b) <- SM.get
    assumpt' <- mapM (modifyContainedASTsM f) assumpt
    SM.put $ (s { assumptions = assumpt' },b)

lookupPostM :: L.Name -> LHStateM (Maybe L.Expr)
lookupPostM n = liftLHState (M.lookup n . posts)

insertPostM :: L.Name -> L.Expr -> LHStateM ()
insertPostM n e = do
    (lh_s, b) <- SM.get
    let pst = posts lh_s
    let pst' = M.insert n e pst
    SM.put $ (lh_s {posts = pst'}, b)

mapPostM :: (L.Expr -> LHStateM L.Expr) -> LHStateM ()
mapPostM f = do
    (s@(LHState { posts = pst }), b) <- SM.get
    pst' <- mapM f pst
    SM.put $ (s { posts = pst' },b)

insertAnnotM :: L.Span -> Maybe T.Text -> L.Expr -> LHStateM ()
insertAnnotM spn t e = do
    (s@(LHState { annots = an }),b) <- SM.get
    let an' = AM . HM.insertWith (++) spn [(t, e)] . unAnnotMap $ an
    SM.put $ (s {annots = an'}, b)

lookupAnnotM :: L.Name -> LHStateM (Maybe [(Maybe T.Text, L.Expr)])
lookupAnnotM (L.Name _ _ _ (Just (L.Span {L.start = l}))) =
    return . Just
           . concatMap snd 
           . find (\(L.Span {L.start = l'}, _) -> l == l')
           . HM.toList
           . unAnnotMap
           =<< annotsM
lookupAnnotM _ = return Nothing

mapAnnotsExpr :: (L.Expr -> LHStateM L.Expr) -> LHStateM ()
mapAnnotsExpr f = do
    (lh_s, b) <- SM.get
    annots' <- modifyContainedASTsM f (annots lh_s)
    SM.put $ (lh_s {annots = annots'}, b)

lookupRenamedTCDict :: L.Name -> L.Type -> LHStateM (Maybe L.Id)
lookupRenamedTCDict n t = do
    tc <- lhRenamedTCM
    return $ lookupTCDict tc n t

insertTyVarBags :: L.Name -> [L.Id] -> LHStateM ()
insertTyVarBags n is = do
    (lh_s, b) <- SM.get
    let tyvar_bags' = M.insert n is (tyvar_bags lh_s)
    SM.put $ (lh_s {tyvar_bags = tyvar_bags'}, b)

lookupTyVarBags :: L.Name -> LHStateM (Maybe [L.Id])
lookupTyVarBags n = do
    (lh_s, _) <- SM.get
    return $ M.lookup n (tyvar_bags lh_s)

setTyVarBags :: TyVarBags -> LHStateM ()
setTyVarBags m = do
    (lh_s, b) <- SM.get
    SM.put (lh_s {tyvar_bags = m}, b)

getTyVarBags :: LHStateM TyVarBags
getTyVarBags = return . tyvar_bags . fst =<< SM.get

insertInstFuncs :: L.Name -> L.Id -> LHStateM ()
insertInstFuncs n i = do
    (lh_s, b) <- SM.get
    let inst_funcs' = M.insert n i (inst_funcs lh_s)
    SM.put $ (lh_s {inst_funcs = inst_funcs'}, b)

lookupInstFuncs :: L.Name -> LHStateM (Maybe L.Id)
lookupInstFuncs n = do
    (lh_s, _) <- SM.get
    return $ M.lookup n (inst_funcs lh_s)

setInstFuncs :: M.Map L.Name L.Id -> LHStateM ()
setInstFuncs m = do
    (lh_s, b) <- SM.get
    SM.put (lh_s {inst_funcs = m}, b)

getInstFuncs :: LHStateM InstFuncs
getInstFuncs = return . inst_funcs . fst =<< SM.get

-- | andM
-- The version of 'and' in the measures
andM :: LHStateM L.Expr
andM = do
    m <- measuresM
    n <- lhAndM
    return (m E.! n)

-- | orM
-- The version of 'or' in the measures
orM :: LHStateM L.Expr
orM = do
    m <- measuresM
    n <- lhOrM
    return (m E.! n)

-- | notM
-- The version of 'not' in the measures
notM :: LHStateM L.Expr
notM = do
    m <- measuresM
    n <- lhNotM
    return (m E.! n)

-- | iffM
-- The version of 'iff' in the measures
iffM :: LHStateM L.Expr
iffM = do
    m <- measuresM
    n <- lhIffM
    return (m E.! n)

tcValuesM :: LHStateM TCValues
tcValuesM = liftTCValues id

liftTCValues :: (TCValues -> a) -> LHStateM a
liftTCValues f = do
    (lh_s, _) <- SM.get
    return (f (tcvalues lh_s))

lhTCM :: LHStateM L.Name
lhTCM = liftTCValues lhTC

lhNumTCM :: LHStateM L.Name
lhNumTCM = liftTCValues lhNumTC 

lhOrdTCM :: LHStateM L.Name
lhOrdTCM = liftTCValues lhOrdTC 

lhEqM :: LHStateM L.Name
lhEqM = liftTCValues lhEq

lhNeM :: LHStateM L.Name
lhNeM = liftTCValues lhNe

lhLtM :: LHStateM L.Name
lhLtM = liftTCValues lhLt

lhLeM :: LHStateM L.Name
lhLeM = liftTCValues lhLe

lhGtM :: LHStateM L.Name
lhGtM = liftTCValues lhGt

lhGeM :: LHStateM L.Name
lhGeM = liftTCValues lhGe

lhAndM :: LHStateM L.Name
lhAndM = liftTCValues lhAnd

lhOrM :: LHStateM L.Name
lhOrM = liftTCValues lhOr

lhNotM :: LHStateM L.Name
lhNotM = liftTCValues lhNot

lhIffM :: LHStateM L.Name
lhIffM = liftTCValues lhIff

binT :: LHStateM L.Type
binT = do
    a <- freshIdN L.TYPE
    let tva = L.TyVar a
    lh <- lhTCM
    bool <- tyBoolT

    let lh' = L.TyCon lh (L.TyFun L.TYPE L.TYPE)

    return $ L.TyForAll a
                        (L.TyFun
                            lh'
                            (L.TyFun
                                tva
                                (L.TyFun
                                    tva
                                    bool
                                )
                            )
                        )

lhLtE :: LHStateM L.Id
lhLtE = do
    n <- liftTCValues lhLt
    return . L.Id n =<< binT 

lhLeE :: LHStateM L.Id
lhLeE = do
    n <- liftTCValues lhLe
    return . L.Id n =<< binT 

lhGtE :: LHStateM L.Id
lhGtE = do
    n <- liftTCValues lhGt
    return . L.Id n =<< binT 

lhGeE :: LHStateM L.Id
lhGeE = do
    n <- liftTCValues lhGe
    return . L.Id n =<< binT 

lhPlusM :: LHStateM L.Name
lhPlusM = liftTCValues lhPlus

lhMinusM :: LHStateM L.Name
lhMinusM = liftTCValues lhMinus

lhTimesM :: LHStateM L.Name
lhTimesM = liftTCValues lhTimes

lhDivM :: LHStateM L.Name
lhDivM = liftTCValues lhDiv

lhNegateM :: LHStateM L.Name
lhNegateM = liftTCValues lhNegate

lhModM :: LHStateM L.Name
lhModM = liftTCValues lhMod

lhFromIntegerM :: LHStateM L.Id
lhFromIntegerM = do
    n <- liftTCValues lhFromInteger
    return . L.Id n =<< numT 

numT :: LHStateM L.Type
numT = do
    a <- freshIdN L.TYPE
    let tva = L.TyVar a
    num <- lhNumTCM
    integerT <- tyIntegerT

    let num' = L.TyCon num L.TYPE

    return $ L.TyForAll a
                    (L.TyFun
                        num'
                        (L.TyFun
                            integerT
                            tva
                        )
                    )

lhToRatioFuncM :: LHStateM L.Id
lhToRatioFuncM = do
    n <- liftTCValues lhToRatioFunc
    return . L.Id n =<< ratioFuncT 

ratioFuncT :: LHStateM L.Type
ratioFuncT = do
    a <- freshIdN L.TYPE
    let tva = L.TyVar a
    integral <- return . KV.integralTC =<< knownValues

    let integral' = L.TyCon integral L.TYPE

    return $ L.TyForAll a
                    (L.TyFun
                        integral'
                        (L.TyFun
                            tva
                            (L.TyFun
                              tva
                              L.TyUnknown)
                        )
                    )

lhToIntegerM :: LHStateM L.Id
lhToIntegerM = do
    n <- liftTCValues lhToInteger
    return . L.Id n =<< integralT 

integralT :: LHStateM L.Type
integralT = do
    a <- freshIdN L.TYPE
    let tva = L.TyVar a
    integral <- return . KV.integralTC =<< knownValues
    integerT <- tyIntegerT

    let integral' = L.TyCon integral L.TYPE

    return $ L.TyForAll a
                    (L.TyFun
                        integral'
                        (L.TyFun
                            tva
                            integerT
                        )
                    )


lhFromRationalM :: LHStateM L.Id
lhFromRationalM = do
    n <- liftTCValues lhFromRational
    return . L.Id n =<< fractionalT

fractionalT :: LHStateM L.Type
fractionalT = do
    a <- freshIdN L.TYPE
    let tva = L.TyVar a
    fractional <- return . KV.fractionalTC =<< knownValues
    rationalT <- tyRationalT

    let fractional' = L.TyCon fractional L.TYPE

    return $ L.TyForAll a
                    (L.TyFun
                        fractional'
                        (L.TyFun
                            rationalT
                            tva
                        )
                    )

lhNumOrdM :: LHStateM L.Id
lhNumOrdM = do
    num <- lhNumTCM
    let num' = L.TyCon num L.TYPE

    ord <- lhOrdTCM
    let ord' = L.TyCon ord L.TYPE

    n <- liftTCValues lhNumOrd
    return $ L.Id n (L.TyFun num' ord') 

lhPPM :: LHStateM L.Name
lhPPM = liftTCValues lhPP

lhOrdM :: LHStateM L.Name
lhOrdM = liftTCValues lhOrd

lhAndE :: LHStateM L.Expr
lhAndE = do
    b <- tyBoolT

    n <- liftTCValues lhAnd
    return $ L.Var (L.Id n (L.TyFun b (L.TyFun b b)))

lhOrE :: LHStateM L.Expr
lhOrE = do
    b <- tyBoolT

    n <- liftTCValues lhOr
    return $ L.Var (L.Id n (L.TyFun b (L.TyFun b b)))

instance L.ASTContainer AnnotMap L.Expr where
    containedASTs =  map snd . concat . HM.elems . unAnnotMap
    modifyContainedASTs f = AM . HM.map (L.modifyContainedASTs f) . coerce

instance L.ASTContainer AnnotMap L.Type where
    containedASTs = L.containedASTs . map snd . concat . HM.elems . unAnnotMap
    modifyContainedASTs f = AM . HM.map (L.modifyContainedASTs f) . coerce

instance ASTContainerM AnnotMap L.Expr where
    modifyContainedASTsM f (AM am) = do
        am' <- mapM (modifyContainedASTsM f) am
        return (AM am')

instance L.Named AnnotMap where
    names = L.names . unAnnotMap
    rename old new = coerce . L.rename old new . unAnnotMap
    renames hm = coerce . L.renames hm . unAnnotMap