liquidhaskell-0.7.0.0: src/Language/Haskell/Liquid/Bare/Lookup.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE OverloadedStrings #-}
module Language.Haskell.Liquid.Bare.Lookup (
GhcLookup(..)
, lookupGhcThing
, lookupGhcVar
, lookupGhcTyCon
, lookupGhcDataCon
) where
import BasicTypes
import ConLike
import DataCon
import GHC (HscEnv)
import HscMain
import Name
import PrelInfo (wiredInThings)
import PrelNames (fromIntegerName, smallIntegerName, integerTyConName)
import Prelude hiding (error)
import RdrName (setRdrNameSpace)
import SrcLoc (SrcSpan, GenLocated(L))
import TcEnv
import TyCon
import TysWiredIn
import Module
import Finder
import TcRnMonad
import IfaceEnv
import Var
import Control.Monad.Except (catchError, throwError)
import Control.Monad.State
import Data.Maybe
import Text.PrettyPrint.HughesPJ (text)
import qualified Data.List as L
import qualified Data.HashMap.Strict as M
import qualified Data.Text as T
import Language.Fixpoint.Types.Names (symbolText, isPrefixOfSym, lengthSym, symbolString)
import Language.Fixpoint.Types (Symbol, Symbolic(..))
import Language.Fixpoint.Misc as F
import Language.Haskell.Liquid.GHC.Misc (splitModuleName, lookupRdrName, sourcePosSrcSpan, tcRnLookupRdrName)
import Language.Haskell.Liquid.Misc (firstMaybes)
import Language.Haskell.Liquid.Types
import Language.Haskell.Liquid.Bare.Env
-- import Debug.Trace (trace)
--------------------------------------------------------------------------------
-- | Querying GHC for Id, Type, Class, Con etc. --------------------------------
--------------------------------------------------------------------------------
class Symbolic a => GhcLookup a where
lookupName :: HscEnv -> ModName -> a -> IO [Name]
srcSpan :: a -> SrcSpan
instance GhcLookup (Located Symbol) where
lookupName e m = symbolLookup e m . val
srcSpan = sourcePosSrcSpan . loc
instance GhcLookup Name where
lookupName _ _ = return . (:[])
srcSpan = nameSrcSpan
lookupGhcThing :: (GhcLookup a) => String -> (TyThing -> Maybe b) -> a -> BareM b
lookupGhcThing name f x = lookupGhcThing' err f x >>= maybe (throwError err) return
where
err = ErrGhc (srcSpan x) (text msg)
msg = unwords [ "Not in scope:", name, "`", symbolicString x, "'"]
lookupGhcThing' :: (GhcLookup a) => TError e -> (TyThing -> Maybe b) -> a -> BareM (Maybe b)
lookupGhcThing' _err f x = do
be <- get
let env = hscEnv be
-- _ <- liftIO $ putStrLn ("lookupGhcThing: PRE " ++ symbolicString x)
ns <- liftIO $ lookupName env (modName be) x
-- _ <- liftIO $ putStrLn ("lookupGhcThing: POST " ++ symbolicString x ++ show ns)
mts <- liftIO $ mapM (fmap (join . fmap f) . hscTcRcLookupName env) ns
return $ firstMaybes mts
symbolicString :: Symbolic a => a -> String
symbolicString = symbolString . symbol
-- liftIOErr :: TError e -> IO a -> BareM a
-- liftIOErr e act = liftIO (act `catchError` \_ -> throwError e)
symbolLookup :: HscEnv -> ModName -> Symbol -> IO [Name]
symbolLookup env mod k
| k `M.member` wiredIn
= return $ maybeToList $ M.lookup k wiredIn
| otherwise
= symbolLookupEnv env mod k
wiredIn :: M.HashMap Symbol Name
wiredIn = M.fromList $ special ++ wiredIns
where
wiredIns = [ (symbol n, n) | thing <- wiredInThings, let n = getName thing ]
special = [ ("GHC.Integer.smallInteger", smallIntegerName)
, ("GHC.Integer.Type.Integer", integerTyConName)
, ("GHC.Num.fromInteger" , fromIntegerName ) ]
symbolLookupEnv :: HscEnv -> ModName -> Symbol -> IO [Name]
symbolLookupEnv env mod k = do
ns <- symbolLookupEnvOrig env mod k
case ns of
[] -> symbolLookupEnvFull env mod k
_ -> return ns
symbolLookupEnvOrig :: HscEnv -> ModName -> Symbol -> IO [Name]
symbolLookupEnvOrig env mod s
| isSrcImport mod
= do let modName = getModName mod
L _ rn <- hscParseIdentifier env $ ghcSymbolString s
res <- lookupRdrName env modName rn
-- 'hscParseIdentifier' defaults constructors to 'DataCon's, but we also
-- need to get the 'TyCon's for declarations like @data Foo = Foo Int@.
res' <- lookupRdrName env modName (setRdrNameSpace rn tcName)
return $ catMaybes [res, res']
| otherwise
= do rn <- hscParseIdentifier env $ ghcSymbolString s
(_, lookupres) <- tcRnLookupRdrName env rn
case lookupres of
Just ns -> return ns
_ -> return []
symbolLookupEnvFull :: HscEnv -> ModName -> Symbol -> IO [Name]
symbolLookupEnvFull hsc _m s = do
let (modName, occName) = ghcSplitModuleName s
mbMod <- lookupTheModule hsc modName
case mbMod of
Just mod -> liftIO $ F.singleton <$> lookupTheName hsc mod occName
Nothing -> return []
lookupTheModule :: HscEnv -> ModuleName -> IO (Maybe Module)
lookupTheModule hsc modName = do
r <- findImportedModule hsc modName Nothing
return $ case r of
Found _ mod -> Just mod
NotFound {fr_mods_hidden=(unitId:_)} -> Just (mkModule unitId modName)
_ -> Nothing -- error "i don't know what to do here"
lookupTheName :: HscEnv -> Module -> OccName -> IO Name
lookupTheName hsc mod name = initTcForLookup hsc (lookupOrig mod name)
ghcSplitModuleName :: Symbol -> (ModuleName, OccName)
ghcSplitModuleName x = (mkModuleName $ ghcSymbolString m, mkTcOcc $ ghcSymbolString s)
where
(m, s) = splitModuleName x
ghcSymbolString :: Symbol -> String
ghcSymbolString = T.unpack . fst . T.breakOn "##" . symbolText
-- ghcSymbolString = symbolString . dropModuleUnique
-- | It's possible that we have already resolved the 'Name' we are looking for,
-- but have had to turn it back into a 'String', e.g. to be used in an 'Expr',
-- as in @{v:Ordering | v = EQ}@. In this case, the fully-qualified 'Name'
-- (@GHC.Types.EQ@) will likely not be in scope, so we store our own mapping of
-- fully-qualified 'Name's to 'Var's and prefer pulling 'Var's from it.
lookupGhcVar :: GhcLookup a => a -> BareM Var
lookupGhcVar x
= do env <- gets varEnv
case L.lookup (symbol x) env of
Nothing -> lookupGhcThing "variable" fv x
Just v -> return v
where
fv (AnId x) = Just x
fv (AConLike (RealDataCon x)) = Just $ dataConWorkId x
fv _ = Nothing
lookupGhcTyCon :: GhcLookup a => a -> BareM TyCon
lookupGhcTyCon s = lookupGhcThing err ftc s `catchError` \_ ->
lookupGhcThing err fdc s
where
-- s = trace ("lookupGhcTyCon: " ++ symbolicString _s) _s
ftc (ATyCon x)
= Just x
ftc _
= Nothing
fdc (AConLike (RealDataCon x)) | isJust $ promoteDataCon_maybe x
= Just $ promoteDataCon x
fdc _
= Nothing
err = "type constructor or class"
lookupGhcDataCon :: Located Symbol -> BareM DataCon
lookupGhcDataCon dc
| Just n <- isTupleDC (val dc)
= return $ tupleCon BoxedTuple n
| val dc == "[]"
= return nilDataCon
| val dc == ":"
= return consDataCon
| otherwise
= lookupGhcDataCon' dc
isTupleDC :: Symbol -> Maybe Int
isTupleDC zs
| "(," `isPrefixOfSym` zs
= Just $ lengthSym zs - 1
| otherwise
= Nothing
lookupGhcDataCon' :: (GhcLookup a) => a -> BareM DataCon
lookupGhcDataCon' = lookupGhcThing "data constructor" fdc
where
fdc (AConLike (RealDataCon x)) = Just x
fdc _ = Nothing