packages feed

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

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}

module G2.Liquid.Helpers ( MeasureSymbols (..)
                         , getGHCInfos
                         , funcSpecs
                         
                         , getTySigs
                         , putTySigs
                         , getAssumedSigs
                         , putAssumedSigs
                         , getQualifiers
                         , putQualifiers
                         , findFuncSpec
                         , measureSpecs
                         , measureSymbols
                         , measureNames
                         , varToName
                         , varEqName
                         , namesEq
                         , fillLHDictArgs ) where

import G2.Language as G2
import G2.Liquid.Types
import G2.Translation.Haskell

#if MIN_VERSION_liquidhaskell(0,9,0)
import qualified Liquid.GHC.Interface as LHI
#else
import qualified Language.Haskell.Liquid.GHC.Interface as LHI
#endif
import Language.Fixpoint.Types.Names
#if MIN_VERSION_liquidhaskell(0,8,10)
import Language.Haskell.Liquid.Types hiding
        (Config, TargetInfo (..), TargetSpec (..), GhcSpec (..), cls, names)
#else
import Language.Haskell.Liquid.Types
#endif
import qualified Language.Haskell.Liquid.UX.Config as LHC
import Language.Fixpoint.Types (Qualifier (..))

import Data.List
import qualified Data.Map as M
import qualified Data.Text as T

import GHC as GHC
#if MIN_VERSION_GLASGOW_HASKELL(9,0,2,0)
import GHC.Types.Name
import GHC.Types.Var as V
#else
import Name
import Var as V
#endif

-- | Interface with LH
getGHCInfos :: LHC.Config -> [FilePath] -> [FilePath] -> IO [GhcInfo]
getGHCInfos config proj fp = do
    let config' = config {idirs = idirs config ++ proj
                         , files = files config
                         , ghcOptions = ["-v"]}

    -- GhcInfo
#if MIN_VERSION_liquidhaskell(0,8,10)
    (ghci, _) <- LHI.getTargetInfos Nothing config' fp
#else
    (ghci, _) <- LHI.getGhcInfos Nothing config' fp
#endif

    return ghci
    
funcSpecs :: [GhcInfo] -> [(Var, LocSpecType)]
funcSpecs fs =
    let
        asserted = concatMap getTySigs fs
        assumed = concatMap getAssumedSigs fs
    in
    asserted ++ assumed

-- | Functions asserted in LH
getTySigs :: GhcInfo -> [(Var, LocSpecType)]
#if MIN_VERSION_liquidhaskell(0,8,6)
getTySigs = gsTySigs . gsSig . giSpec
#else
getTySigs = gsTySigs . spec
#endif

putTySigs :: GhcInfo -> [(Var, LocSpecType)] -> GhcInfo
#if MIN_VERSION_liquidhaskell(0,8,6)
putTySigs gi@(GI {
                    giSpec = sp@(SP { gsSig = sp_sig })
                 }
             ) new_ty_sigs = 
    gi { giSpec = sp { gsSig = sp_sig { gsTySigs = new_ty_sigs } } }
#else
putTySigs gi@(GI { spec = sp }) new_ty_sigs = 
    gi { spec = sp { gsTySigs = new_ty_sigs }}
#endif

-- | Functions assumed in LH
getAssumedSigs :: GhcInfo -> [(Var, LocSpecType)]
#if MIN_VERSION_liquidhaskell(0,8,6)
getAssumedSigs = gsAsmSigs . gsSig . giSpec
#else
getAssumedSigs = gsAsmSigs . spec
#endif

putAssumedSigs :: GhcInfo -> [(Var, LocSpecType)] -> GhcInfo
#if MIN_VERSION_liquidhaskell(0,8,6)
putAssumedSigs gi@(GI {
                    giSpec = sp@(SP { gsSig = sp_sig })
                 }
             ) new_ty_sigs = 
    gi { giSpec = sp { gsSig = sp_sig { gsTySigs = new_ty_sigs } } }
#else
putAssumedSigs gi@(GI { spec = sp }) new_ty_sigs = 
    gi { spec = sp { gsTySigs = new_ty_sigs }}
#endif

getQualifiers :: GhcInfo -> [Qualifier]
#if MIN_VERSION_liquidhaskell(0,8,6)
getQualifiers = gsQualifiers . gsQual . giSpec
#else
getQualifiers = gsQualifiers . spec
#endif

putQualifiers :: GhcInfo -> [Qualifier] -> GhcInfo
#if MIN_VERSION_liquidhaskell(0,8,6)
putQualifiers gi@(GI {
                    giSpec = sp@(SP { gsQual = quals })
                 }
             ) new_quals = 
    gi { giSpec = sp { gsQual = quals { gsQualifiers = new_quals } } }
#else
putQualifiers gi@(GI { spec = sp }) new_quals = 
    gi { spec = sp { gsQualifiers = new_quals }}
#endif


findFuncSpec :: [GhcInfo] -> G2.Name -> Maybe SpecType
findFuncSpec ghci g2_n =
    let
        fs = funcSpecs ghci
        fs' = map (\(v, lst) -> (V.varName v, lst)) fs
    in
    case find (\(n, _) -> namesEq n g2_n) fs' of
        Just st -> Just . val . snd $ st
        Nothing -> Nothing

varToName :: V.Var -> G2.Name
varToName = mkName . V.varName

varEqName :: V.Var -> G2.Name -> Bool
varEqName v = namesEq (V.varName v)

namesEq :: GHC.Name -> G2.Name -> Bool
namesEq ghc_n (Name n m _ _) =
    T.pack (occNameString $ nameOccName ghc_n) == n
        && (case nameModule_maybe ghc_n of
                Just m' ->
                    Just (T.pack . moduleNameString . moduleName $ m') == m
                Nothing -> m == Nothing)

measureSpecs :: [GhcInfo] -> [Measure SpecType GHC.DataCon]
#if MIN_VERSION_liquidhaskell(0,8,6)
measureSpecs = concatMap (gsMeasures . gsData . giSpec)
#else
measureSpecs = concatMap (gsMeasures . spec)
#endif

newtype MeasureSymbols = MeasureSymbols { symbols :: [Symbol] }

measureSymbols :: [GhcInfo] -> MeasureSymbols
measureSymbols = MeasureSymbols . measureNames

measureNames :: [GhcInfo] -> [Symbol]
#if MIN_VERSION_liquidhaskell(0,8,6)
measureNames = map (val . msName) . measureSpecs
#else
measureNames = map (val . name) . measureSpecs
#endif

-- The walk function takes lhDict arguments that are not correctly accounted for by mkStrict.
-- The arguments are not actually used, so, here, we fill them in with undefined. 
fillLHDictArgs :: Walkers -> Expr -> Expr
fillLHDictArgs w = modifyAppTop (fillLHDictArgs' w)

fillLHDictArgs' :: Walkers -> Expr -> Expr
fillLHDictArgs' w e
    | f@(Var i):xs <- unApp e
    , any (\(_, i') -> i == i') (M.toList w) = mkApp $ f:fillLHDictArgs'' 0 xs
    | otherwise = e

fillLHDictArgs'' :: Int -> [Expr] -> [Expr]
fillLHDictArgs'' !n [] = replicate n (Prim Undefined TyBottom)
fillLHDictArgs'' !n (t@(Type _):xs) = t:fillLHDictArgs'' (n + 1) xs
fillLHDictArgs'' !n xs = replicate n (Prim Undefined TyBottom) ++ xs