packages feed

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

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

module G2.Liquid.Interface ( LiquidData (..)
                           , LiquidReadyState
                           , lr_state
                           , lr_binding
                           , lrsMeasures
                           , Abstracted (..)
                           , AbstractedInfo (..)
                           , findCounterExamples
                           
                           , runLHG2
                           , onlyMinimalStates
                           , cleanupResults

                           , runLHCore
                           , liquidStateWithCall
                           , liquidStateWithCall'
                           , liquidStateFromSimpleStateWithCall
                           , liquidStateFromSimpleStateWithCall'

                           , cleanReadyState
                           , fromLiquidNoCleaning
                           , createLiquidReadyState
                           , processLiquidReadyState
                           , processLiquidReadyStateWithCall
                           , extractWithoutSpecs

                           , processLiquidReadyStateCleaning

                           , reqNames

                           , lhStateToCE
                           , printLHOut
                           , printCE) where

import G2.Config.Config

import G2.Translation
import G2.Interface
import G2.Language as Lang
import G2.Language.KnownValues as KV
import qualified G2.Language.ExprEnv as E

import G2.Execution

import G2.Initialization.MkCurrExpr

import G2.Liquid.AddCFBranch
import G2.Liquid.AddLHTC
import G2.Liquid.AddOrdToNum
import G2.Liquid.AddTyVars
import G2.Liquid.Config
import G2.Liquid.Conversion
import G2.Liquid.ConvertCurrExpr
import G2.Liquid.Helpers
import G2.Liquid.LHReducers
import G2.Liquid.Measures
import G2.Liquid.MkLHVals
import G2.Liquid.G2Calls
import G2.Liquid.Simplify
import G2.Liquid.SpecialAsserts
import G2.Liquid.TCGen
import G2.Liquid.TCValues
import G2.Liquid.Types
import G2.Liquid.TyVarBags
import G2.Solver hiding (solve)

import G2.Lib.Printers

import Language.Haskell.Liquid.Types hiding (Config, cls, names, measures)
import Language.Haskell.Liquid.UX.CmdLine hiding (config)

import Control.Exception
import Control.Monad.Extra
import Control.Monad.IO.Class
import qualified Control.Monad.State as SM
import Data.List
import qualified Data.HashSet as S
import qualified Data.HashMap.Lazy as HM
import qualified Data.Map as M
import qualified Data.Text as T
import qualified Data.Text.IO as TI

import G2.Language.Monad

data LHReturn = LHReturn { calledFunc :: FuncInfo
                         , violating :: Maybe FuncInfo
                         , abstracted :: [FuncInfo] } deriving (Eq, Show)

data FuncInfo = FuncInfo { func :: T.Text
                         , funcArgs :: T.Text
                         , funcReturn :: T.Text } deriving (Eq, Show)

-- | findCounterExamples
-- Given (several) LH sources, and a string specifying a function name,
-- attempt to find counterexamples to the functions liquid type
findCounterExamples :: [FilePath] -> [FilePath] -> T.Text -> Config -> LHConfig -> IO (([ExecRes AbstractedInfo], Bindings), Lang.Id)
findCounterExamples proj fp entry config lhconfig = do
    let config' = config { mode = Liquid }

    lh_config <- getOpts []

    ghci <- try $ getGHCInfos lh_config proj fp :: IO (Either SomeException [GhcInfo])
    
    let ghci' = case ghci of
                  Right g_c -> g_c
                  Left e -> error $ "ERROR OCCURRED IN LIQUIDHASKELL\n" ++ show e

    tgt_trans <- translateLoaded proj fp (simplTranslationConfig { simpl = False }) config'

    runLHCore entry tgt_trans ghci' config' lhconfig

runLHCore :: T.Text
          -> (Maybe T.Text, ExtractedG2)
          -> [GhcInfo]
          -> Config
          -> LHConfig
          -> IO (([ExecRes AbstractedInfo], Bindings), Lang.Id)
runLHCore entry (mb_modname, exg2) ghci config lhconfig = do
    LiquidData { ls_state = final_st
               , ls_bindings = bindings
               , ls_id = ifi
               , ls_counterfactual_name = cfn
               , ls_memconfig = pres_names } <- liquidStateWithCall entry (mb_modname, exg2) ghci config lhconfig mempty

    SomeSolver solver <- initSolver config
    let simplifier = IdSimplifier

    let (red, hal, ord) = lhReducerHalterOrderer config lhconfig solver simplifier entry mb_modname cfn final_st
    (exec_res, final_bindings) <- SM.evalStateT (runLHG2 config red hal ord solver simplifier pres_names ifi final_st bindings) (mkPrettyGuide ())

    close solver

    return ((exec_res, final_bindings), ifi)

{-# INLINE liquidStateWithCall #-}
liquidStateWithCall :: T.Text -> (Maybe T.Text, ExtractedG2)
                      -> [GhcInfo]
                      -> Config
                      -> LHConfig
                      -> MemConfig
                      -> IO LiquidData
liquidStateWithCall entry (mb_modname, exg2) ghci config lhconfig memconfig =
    liquidStateWithCall' entry (mb_modname, exg2) ghci config lhconfig memconfig (mkCurrExpr Nothing Nothing) mkArgTys

{-# INLINE liquidStateWithCall' #-}
liquidStateWithCall' :: T.Text -> (Maybe T.Text, ExtractedG2)
                       -> [GhcInfo]
                       -> Config
                       -> LHConfig
                       -> MemConfig
                       -> (Lang.Id -> MkCurrExpr)
                       -> (Lang.Expr -> MkArgTypes)
                       -> IO LiquidData
liquidStateWithCall' entry (mb_m, exg2) ghci config lhconfig memconfig mkCurr argTys = do
    let simp_s = initSimpleState exg2
    liquidStateFromSimpleStateWithCall' simp_s ghci entry mb_m config lhconfig memconfig mkCurr argTys

{-# INLINE liquidStateFromSimpleStateWithCall #-}
liquidStateFromSimpleStateWithCall :: SimpleState
                                   -> [GhcInfo]
                                   -> T.Text
                                   -> Maybe T.Text
                                   -> Config
                                   -> LHConfig
                                   -> MemConfig
                                   -> IO LiquidData
liquidStateFromSimpleStateWithCall simp_s ghci entry mb_m config lhconfig memconfig =
    liquidStateFromSimpleStateWithCall' simp_s ghci entry mb_m config lhconfig memconfig (mkCurrExpr Nothing Nothing) mkArgTys

{-# INLINE liquidStateFromSimpleStateWithCall' #-}
liquidStateFromSimpleStateWithCall' :: SimpleState
                                    -> [GhcInfo]
                                    -> T.Text
                                    -> Maybe T.Text
                                    -> Config
                                    -> LHConfig
                                    -> MemConfig
                                    -> (Lang.Id -> MkCurrExpr)
                                    -> (Lang.Expr -> MkArgTypes)
                                    -> IO LiquidData
liquidStateFromSimpleStateWithCall' simp_s ghci entry mb_m config lhconfig memconfig mkCurr argTys = do
    let (simp_s', ph_tyvars) = if add_tyvars lhconfig
                                  then fmap Just $ addTyVarsEEnvTEnv simp_s
                                  else (simp_s, Nothing)
        (s, i, bindings') = initStateFromSimpleStateWithCall simp_s' True entry mb_m mkCurr argTys config
    
    fromLiquidReadyState s i bindings' ghci ph_tyvars lhconfig memconfig

{-# INLINE fromLiquidReadyState #-}
fromLiquidReadyState :: State ()
                     -> Lang.Id
                     -> Bindings
                     -> [GhcInfo]
                     -> Maybe PhantomTyVars
                     -> LHConfig
                     -> MemConfig
                     -> IO LiquidData
fromLiquidReadyState init_state ifi bindings ghci ph_tyvars lhconfig memconfig = do
    let (init_state', bindings') = (markAndSweepPreserving (reqNames init_state `mappend` memconfig) init_state bindings)
        cleaned_state = init_state' { type_env = type_env init_state } 
    fromLiquidNoCleaning cleaned_state ifi bindings' ghci ph_tyvars lhconfig memconfig

data LiquidReadyState = LiquidReadyState { lr_state :: LHState
                                         , lr_binding :: Bindings
                                         , lr_known_values :: KnownValues
                                         , lr_type_classes :: TypeClasses
                                         , lr_higher_ord_insts :: S.HashSet Name }

data LiquidData = LiquidData { ls_state :: State LHTracker
                             , ls_bindings :: Bindings
                             , ls_id :: Lang.Id
                             , ls_counterfactual_name :: CounterfactualName
                             , ls_counterfactual_funcs :: S.HashSet Name
                             , ls_measures :: Measures
                             , ls_assumptions :: Assumptions
                             , ls_posts :: Posts
                             , ls_tcv :: TCValues
                             , ls_memconfig :: MemConfig }

cleanReadyState :: LiquidReadyState -> MemConfig -> LiquidReadyState
cleanReadyState lrs@(LiquidReadyState { lr_state = lhs@(LHState { state = s }), lr_binding = b }) memconfig =
    let
        (s', b') = (markAndSweepPreserving (reqNames s `mappend` memconfig) s b)
        s'' = s' { type_env = type_env s }
    in
    lrs { lr_state = lhs { state = s'' }, lr_binding = b' }

fromLiquidNoCleaning :: State ()
                     -> Lang.Id
                     -> Bindings
                     -> [GhcInfo]
                     -> Maybe PhantomTyVars
                     -> LHConfig
                     -> MemConfig
                     -> IO LiquidData
fromLiquidNoCleaning init_state ifi bindings ghci ph_tyvars lhconfig memconfig = do
    let lrs = createLiquidReadyState init_state bindings ghci ph_tyvars lhconfig
    processLiquidReadyState lrs ifi ghci lhconfig memconfig

createLiquidReadyState :: State () -> Bindings -> [GhcInfo] -> Maybe PhantomTyVars -> LHConfig -> LiquidReadyState
createLiquidReadyState s bindings ghci ph_tyvars lhconfig =
    let
        np_ng = name_gen bindings

        (meenv, mkv, mtc, minst, mexported, ng') = mkLHVals s (higher_order_inst bindings) (exported_funcs bindings) np_ng 

        s' = s { track = [] }
        bindings' = bindings { exported_funcs = mexported ++ exported_funcs bindings, name_gen = ng' }

        (lh_state, lh_bindings) = createLHState meenv mkv mtc s' bindings'

        (data_state, data_bindings) = execLHStateM (initializeLHData ghci ph_tyvars lhconfig) lh_state lh_bindings
    in
    LiquidReadyState { lr_state = data_state
                     , lr_binding = data_bindings
                     , lr_known_values = mkv
                     , lr_type_classes = mtc
                     , lr_higher_ord_insts = minst } -- (mkv, mtc, minst, data_state, data_bindings)

processLiquidReadyStateCleaning :: LiquidReadyState -> Lang.Id -> [GhcInfo] -> LHConfig -> MemConfig -> IO LiquidData
processLiquidReadyStateCleaning lrs ifi ghci lhconfig memconfig =
    let
        lrs' = cleanReadyState lrs memconfig
    in
    processLiquidReadyState lrs' ifi ghci lhconfig memconfig

processLiquidReadyState :: LiquidReadyState -> Lang.Id -> [GhcInfo] -> LHConfig -> MemConfig -> IO LiquidData
processLiquidReadyState lrs@(LiquidReadyState { lr_state = lh_state
                                              , lr_binding = lh_bindings }) ifi ghci lhconfig memconfig = do
    let ((cfn, mc, cff), (merged_state, bindings')) = runLHStateM (initializeLHSpecs (counterfactual lhconfig) ghci ifi lh_bindings) lh_state lh_bindings
        lrs' = lrs { lr_state = merged_state, lr_binding = bindings'}

    lhs <- extractWithoutSpecs lrs' ifi ghci memconfig
    
    let lh_s = if only_top lhconfig
                  then elimNonTop (S.insert (idName mc) cff) (ls_state lhs)
                  else ls_state lhs

    return $ lhs { ls_state = lh_s
                 , ls_id = mc
                 , ls_counterfactual_name = cfn
                 , ls_counterfactual_funcs = cff }

extractWithoutSpecs :: LiquidReadyState -> Lang.Id -> [GhcInfo] -> MemConfig -> IO LiquidData
extractWithoutSpecs lrs@(LiquidReadyState { lr_state = s
                                          , lr_binding = bindings
                                          , lr_known_values = mkv
                                          , lr_type_classes = mtc
                                          , lr_higher_ord_insts = minst}) ifi ghci memconfig = do
    let (lh_s, bindings') = execLHStateM (return ()) s bindings
    let bindings'' = bindings' { higher_order_inst = minst }

    let tcv = tcvalues lh_s
    let lh_s' = deconsLHState lh_s

    let annm = annots lh_s
        pres_names = addSearchNames (namesList tcv ++ namesList mkv) $ reqNames lh_s'
        pres_names' = addSearchNames (namesList annm) pres_names

    let track_state = lh_s' {track = LHTracker { abstract_calls = []
                                               , last_var = Nothing
                                               , annotations = annm
                                               , all_calls = []
                                               , higher_order_calls = [] } }

    -- We replace certain function name lists in the final State with names
    -- mapping into the measures from the LHState.  These functions do not
    -- need to be passed the LH typeclass, so this ensures use of Names from
    -- these lists will work, without us having to modify all of G2 to account
    -- for the LH typeclass.
    let final_st = track_state { known_values = mkv
                               , type_classes = unionTypeClasses mtc (type_classes track_state)}

    let real_meas = lrsMeasures ghci lrs


    return $ LiquidData { ls_state = final_st
                        , ls_bindings = bindings''
                        , ls_id = ifi
                        , ls_counterfactual_name = error "No counterfactual name"
                        , ls_counterfactual_funcs = error "No counterfactual funcs"
                        , ls_measures = real_meas
                        , ls_assumptions = assumptions lh_s
                        , ls_posts = posts lh_s
                        , ls_tcv = tcv
                        , ls_memconfig = pres_names' `mappend` memconfig }

lrsMeasures :: [GhcInfo] -> LiquidReadyState -> Measures
lrsMeasures ghci lrs = 
    let
        meas_names = measureNames ghci
        meas_nameOcc = map (\(Name n md _ _) -> (n, md)) $ map symbolName meas_names

        real_meas = E.filterWithKey (\(Name n md i _) _ -> 
                                (n, md) `elem` meas_nameOcc && i == 0) . measures $ lr_state lrs
    in
    real_meas

processLiquidReadyStateWithCall :: LiquidReadyState -> [GhcInfo] -> T.Text -> Maybe T.Text -> Config -> LHConfig -> MemConfig -> IO LiquidData
processLiquidReadyStateWithCall lrs@(LiquidReadyState { lr_state = lhs@(LHState { state = s })
                                                      , lr_binding = bindings})
                                                                ghci f m_mod config lhconfig memconfig = do

    let (ie, _) = case findFunc f m_mod (expr_env s) of
                          Left ie' -> ie'
                          Right errs -> error errs

        (ce, is, f_i, ng') = mkCurrExpr Nothing Nothing ie (type_classes s) (name_gen bindings)
                                      (expr_env s) (type_env s) (deepseq_walkers bindings) (known_values s) config

        lhs' = lhs { state = s { expr_env = foldr E.insertSymbolic (expr_env s) is
                               , curr_expr = CurrExpr Evaluate ce }
                   }
        (lhs'', bindings') = execLHStateM (addLHTCCurrExpr) lhs' (bindings { name_gen = ng' })

        lrs' = lrs { lr_state = lhs''
                   , lr_binding = bindings' { fixed_inputs = f_i
                                            , input_names = map idName is
                                            }
                   }

    processLiquidReadyStateCleaning lrs' ie ghci lhconfig memconfig

runLHG2 :: (MonadIO m, Solver solver, Simplifier simplifier)
        => Config
        -> SomeReducer m LHTracker
        -> SomeHalter m LHTracker
        -> SomeOrderer m LHTracker
        -> solver
        -> simplifier
        -> MemConfig
        -> Lang.Id
        -> State LHTracker
        -> Bindings
        -> m ([ExecRes AbstractedInfo], Bindings)
runLHG2 config red hal ord solver simplifier pres_names init_id final_st bindings = do
    let only_abs_st = addTicksToDeepSeqCases (deepseq_walkers bindings) final_st
    (ret, final_bindings) <- runG2WithSomes red hal ord solver simplifier pres_names only_abs_st bindings

    let ret' = onlyMinimalStates ret

    cleanupResults solver simplifier config init_id final_st final_bindings ret'

onlyMinimalStates :: [ExecRes LHTracker] -> [ExecRes LHTracker]
onlyMinimalStates ers =
    let
        mi = case length ers of
                  0 -> 0
                  _ -> minimum $ map (\(ExecRes {final_state = s}) -> abstractCallsNum s) ers
    in
    filter (\(ExecRes {final_state = s}) -> mi == (abstractCallsNum s)) ers

cleanupResults :: (MonadIO m, Solver solver, Simplifier simplifier) =>
                  solver
               -> simplifier
               -> Config
               -> Lang.Id
               -> State LHTracker
               -> Bindings
               -> [ExecRes LHTracker]
               -> m ([ExecRes AbstractedInfo], Bindings)
cleanupResults solver simplifier config init_id init_state bindings ers = do
    let ers2 = map (\er -> er { final_state = putSymbolicExistentialInstInExprEnv (final_state er) }) ers
        ers3 = map (\er -> if fmap funcName (violated er) == Just initiallyCalledFuncName
                                  then er { violated = Nothing }
                                  else er) ers2

    (bindings', ers4) <- liftIO $ mapAccumM (reduceCalls runG2WithSomes solver simplifier config) bindings ers3
    ers5 <- liftIO $ mapM (checkAbstracted runG2WithSomes solver simplifier config init_id bindings') ers4
    let ers6 = 
          map (\er@(ExecRes { final_state = s }) ->
                (er { final_state =
                              s {track = 
                                    mapAbstractedInfoFCs (subVarFuncCall True (model s) (expr_env init_state) (type_classes s))
                                    $ track s
                                }
                    })) ers5
    return (ers6, bindings')

lhReducerHalterOrderer :: (MonadIO m, Solver solver, Simplifier simplifier)
                       => Config
                       -> LHConfig
                       -> solver
                       -> simplifier
                       -> T.Text
                       -> Maybe T.Text
                       -> CounterfactualName
                       -> State t
                       -> ( SomeReducer (SM.StateT PrettyGuide m) LHTracker
                          , SomeHalter (SM.StateT PrettyGuide m) LHTracker
                          , SomeOrderer (SM.StateT PrettyGuide m) LHTracker)
lhReducerHalterOrderer config lhconfig solver simplifier entry mb_modname cfn st =
    let

        share = sharing config

        state_name = Name "state" Nothing 0 Nothing

        abs_ret_name = Name "abs_ret" Nothing 0 Nothing

        non_red = nonRedPCRed .|. nonRedPCRedConst

        m_logger = fmap SomeReducer $ getLogger config

        lh_std_red = existentialInstRed :== NoProgress .--> lhRed cfn :== Finished --> stdRed share retReplaceSymbFuncVar solver simplifier
        opt_logger_red = case m_logger of
                            Just logger -> logger .~> lh_std_red
                            Nothing -> lh_std_red
    in
    if higherOrderSolver config == AllFuncs then
        (opt_logger_red .== Finished .-->
            (taggerRed abs_ret_name :== Finished --> nonRedAbstractReturnsRed) .== Finished .-->
            SomeReducer non_red
        , SomeHalter
                (maxOutputsHalter (maxOutputs config)
                  <~> zeroHalter (steps config)
                  <~> lhAbsHalter entry mb_modname (expr_env st)
                  <~> lhLimitByAcceptedHalter (cut_off lhconfig)
                  <~> switchEveryNHalter (switch_after lhconfig)
                  <~> lhAcceptIfViolatedHalter)
        , SomeOrderer lhLimitByAcceptedOrderer)
    else
        (opt_logger_red .== Finished .-->
            ((taggerRed state_name :== Finished --> non_red)) .== Finished .-->
            (taggerRed abs_ret_name :== Finished --> nonRedAbstractReturnsRed)
        , SomeHalter
            (discardIfAcceptedTagHalter state_name
              <~> discardIfAcceptedTagHalter abs_ret_name
              <~> maxOutputsHalter (maxOutputs config)
              <~> zeroHalter (steps config)
              <~> lhAbsHalter entry mb_modname (expr_env st)
              <~> lhLimitByAcceptedHalter (cut_off lhconfig)
              <~> switchEveryNHalter (switch_after lhconfig)
              <~> lhAcceptIfViolatedHalter)
        , SomeOrderer lhLimitByAcceptedOrderer)

initializeLHData :: [GhcInfo] -> Maybe PhantomTyVars -> LHConfig -> LHStateM ()
initializeLHData ghcInfos m_ph_tyvars config = do
    addLHTC
    addOrdToNum

    addErrorAssumes config

    let lh_measures = measureSpecs ghcInfos

    meenv <- measuresM
    nt <- return . M.fromList =<< mapMaybeM measureTypeMappings lh_measures
    fil_meenv <- filterMeasures' meenv (M.keys nt)

    createMeasures lh_measures

    case m_ph_tyvars of
      Just ph_tyvars -> addTyVarsMeasures ph_tyvars
      Nothing -> return ()

    meenv' <- measuresM
    putMeasuresM (fil_meenv `E.union` meenv')

-- | Returns the name of the Tick on the counterfactual branches, and the names
-- of all functions with counterfactual branches
initializeLHSpecs :: Counterfactual -> [GhcInfo] -> Lang.Id -> Bindings -> LHStateM (Lang.Name, Lang.Id, S.HashSet Lang.Name)
initializeLHSpecs counter ghcInfos ifi bindings = do
    let specs = funcSpecs ghcInfos
    mergeLHSpecState specs

    addSpecialAsserts
    addTrueAsserts (idName ifi)

    -- Most of the simplification works less well on some of the Core generated by convertCurrExpr,
    -- so we apply the simplification first.  However, we do want to stamp out as much redundancy
    -- from convertCurrExpr as we can, so we do further simplificiation after
    simplify
    (main_call, ns) <- convertCurrExpr ifi bindings
    furtherSimplifyCurrExpr

    (cfn, ns') <- case counter of
                    Counterfactual cf_mods ->
                        return . (,ns) =<< addCounterfactualBranch cf_mods ns
                    NotCounterfactual -> return (Name "" Nothing 0 Nothing, [])

    mapME (return . flattenLets)

    return (cfn, main_call, S.fromList ns')

reqNames :: State t -> MemConfig
reqNames (State { expr_env = eenv
                , type_env = tenv
                , type_classes = tc
                , known_values = kv}) =
    let ns = Lang.namesList
                   [ mkGe kv eenv
                   , mkGt kv eenv
                   , mkEq kv eenv
                   , mkNeq kv eenv
                   , mkLt kv eenv
                   , mkLe kv eenv
                   , mkAnd kv eenv
                   , mkOr kv eenv
                   , mkNot kv eenv
                   , mkPlus kv eenv
                   , mkMinus kv eenv
                   , mkMult kv eenv
                   -- , mkDiv kv eenv
                   , mkMod kv eenv
                   , mkNegate kv eenv
                   , mkImplies kv eenv
                   , mkIff kv eenv
                   , mkFromInteger kv eenv
                   -- , mkToInteger kv eenv

                   , mkJust kv tenv
                   , mkNothing kv tenv

                   , mkUnit kv tenv

                   , mkIntegralExtactReal kv
                   , mkRealExtractNum kv 
                   ]
          ++
          Lang.namesList 
            (HM.filterWithKey 
                (\k _ -> k == eqTC kv || k == numTC kv || k == ordTC kv || k == integralTC kv || k == fractionalTC kv) 
                (toMap tc)
            )
          ++
          Lang.namesList (filter (\(Name _ m _ _) -> m == Just "Data.Set.Internal") (E.keys eenv))
    in
    MemConfig { search_names = ns
              , pres_func = pf }
    where
        pf _ (Bindings { deepseq_walkers = dsw }) a =
            S.fromList . map idName . M.elems $ M.filterWithKey (\n _ -> n `S.member` a) dsw

printLHOut :: Lang.Id -> [ExecRes AbstractedInfo] -> IO ()
printLHOut entry =
    mapM_ (\lhr -> do printParsedLHOut lhr; putStrLn "") . map (parseLHOut entry)

printCE :: State t -> [CounterExample] -> IO ()
printCE s =
    mapM_ (\lhr -> do printParsedLHOut lhr; putStrLn "") . map (counterExampleToLHReturn s)

printParsedLHOut :: LHReturn -> IO ()
printParsedLHOut (LHReturn { calledFunc = FuncInfo {func = f, funcArgs = call, funcReturn = output}
                           , violating = Nothing
                           , abstracted = abstr}) = do
    putStrLn "The call"
    TI.putStrLn $ call `T.append` " = " `T.append` output
    TI.putStrLn $ "violates " `T.append` f `T.append` "'s refinement type"
    printAbs abstr
printParsedLHOut (LHReturn { calledFunc = FuncInfo {funcArgs = call, funcReturn = output}
                           , violating = Just (FuncInfo {func = f, funcArgs = call', funcReturn = output'})
                           , abstracted = abstr }) = do
    TI.putStrLn $ call `T.append` " = " `T.append` output
    putStrLn "makes a call to"
    TI.putStrLn $ call' `T.append` " = " `T.append` output'
    TI.putStrLn $ "violating " `T.append` f `T.append` "'s refinement type"
    printAbs abstr

printAbs :: [FuncInfo] -> IO ()
printAbs fi = do
    let fn = T.intercalate ", " $ map func fi

    if length fi > 0 then do
        putStrLn "if"
        mapM_ printFuncInfo fi
        if length fi > 1 then do
            TI.putStrLn $ "Strengthen the refinement types of " `T.append`
                          fn `T.append` " to eliminate these possibilities"
            putStrLn "Abstract"
        else do
            TI.putStrLn $ "Strengthen the refinement type of " `T.append`
                          fn `T.append` " to eliminate this possibility"
            putStrLn "Abstract"
    else
        putStrLn "Concrete"

printFuncInfo :: FuncInfo -> IO ()
printFuncInfo (FuncInfo {funcArgs = call, funcReturn = output}) =
    TI.putStrLn $ call `T.append` " = " `T.append` output

parseLHOut :: Lang.Id -> ExecRes AbstractedInfo -> LHReturn
parseLHOut entry (ExecRes { final_state = s
                          , conc_args = inArg
                          , conc_out = ex
                          , violated = ais}) =
  let
      called = funcCallToFuncInfo  (printHaskell s)
             $ FuncCall { funcName = idName entry, arguments = inArg, returns = ex}
      viFunc = fmap (parseLHFuncTuple s) ais

      abstr = map (parseLHFuncTuple s) . map abstract . abs_calls $ track s
  in
  LHReturn { calledFunc = called
           , violating = viFunc
           , abstracted = abstr}

counterExampleToLHReturn :: State t -> CounterExample -> LHReturn
counterExampleToLHReturn s (DirectCounter fc abstr _) =
    let
        called = funcCallToFuncInfo (printHaskell s) . abstract $ fc
        abstr' = map (funcCallToFuncInfo (printHaskell s) . abstract) abstr
    in
    LHReturn { calledFunc = called
             , violating = Nothing
             , abstracted = abstr'}
counterExampleToLHReturn s (CallsCounter fc viol_fc abstr _) =
    let
        called = funcCallToFuncInfo (printHaskell s) . abstract $ fc
        viol_called = funcCallToFuncInfo (printHaskell s) . abstract $ viol_fc
        abstr' = map (funcCallToFuncInfo (printHaskell s) . abstract) abstr
    in
    LHReturn { calledFunc = called
             , violating = Just viol_called
             , abstracted = abstr'}

funcCallToFuncInfo :: (Expr -> T.Text) -> FuncCall -> FuncInfo
funcCallToFuncInfo t (FuncCall { funcName = f, arguments = inArg, returns = ret }) =
    let
        funcCall = t . foldl (\a a' -> App a a') (Var (Id f TyUnknown)) $ inArg
        funcOut = t ret
    in
    FuncInfo {func = nameOcc f, funcArgs = funcCall, funcReturn = funcOut}

lhStateToCE :: ExecRes AbstractedInfo -> CounterExample
lhStateToCE (ExecRes { final_state = State { track = t } })
    | Just c <-  abs_violated t = CallsCounter (init_call t) c (abs_calls t) (ai_higher_order_calls t)
    | otherwise = DirectCounter (init_call t) (abs_calls t) (ai_higher_order_calls t)

parseLHFuncTuple :: State t -> FuncCall -> FuncInfo
parseLHFuncTuple s (FuncCall {funcName = n, arguments = ars, returns = out}) =
    let
        t = case fmap typeOf $ E.lookup n (expr_env s) of
                  Just t' -> t'
                  Nothing -> error $ "Unknown type for abstracted function " ++ show n
    in
    FuncInfo { func = nameOcc n
             , funcArgs = printHaskell s (foldl' App (Var (Id n t)) ars)
             , funcReturn = printHaskell s out }