{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RecordWildCards #-}
module Cryptol.ModuleSystem.Env where
#ifndef RELOCATABLE
import Paths_cryptol (getDataDir)
#endif
import Cryptol.Eval (EvalEnv)
import Cryptol.ModuleSystem.Fingerprint
import Cryptol.ModuleSystem.Interface
import Cryptol.ModuleSystem.Name (Name,Supply,emptySupply)
import qualified Cryptol.ModuleSystem.NamingEnv as R
import Cryptol.Parser.AST
import qualified Cryptol.TypeCheck as T
import qualified Cryptol.TypeCheck.AST as T
import Cryptol.Utils.PP (PP(..),text,parens,NameDisp)
import Data.ByteString(ByteString)
import Control.Monad (guard,mplus)
import qualified Control.Exception as X
import Data.Function (on)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Semigroup
import System.Directory (getAppUserDataDirectory, getCurrentDirectory)
import System.Environment(getExecutablePath)
import System.FilePath ((</>), normalise, joinPath, splitPath, takeDirectory)
import qualified Data.List as List
import GHC.Generics (Generic)
import Control.DeepSeq
import Prelude ()
import Prelude.Compat
import Cryptol.Utils.Panic(panic)
import Cryptol.Utils.PP(pp)
data ModuleEnv = ModuleEnv
{ meLoadedModules :: LoadedModules
, meNameSeeds :: T.NameSeeds
, meSolverConfig :: T.SolverConfig
, meEvalEnv :: EvalEnv
, meCoreLint :: CoreLint
, meMonoBinds :: !Bool
, meFocusedModule :: Maybe ModName
, meSearchPath :: [FilePath]
, meDynEnv :: DynamicEnv
, meSupply :: !Supply
} deriving Generic
instance NFData ModuleEnv where
rnf x = meLoadedModules x `seq` meEvalEnv x `seq` meDynEnv x `seq` ()
data CoreLint = NoCoreLint
| CoreLint
deriving (Generic, NFData)
resetModuleEnv :: ModuleEnv -> ModuleEnv
resetModuleEnv env = env
{ meLoadedModules = mempty
, meNameSeeds = T.nameSeeds
, meEvalEnv = mempty
, meFocusedModule = Nothing
, meDynEnv = mempty
}
initialModuleEnv :: IO ModuleEnv
initialModuleEnv = do
curDir <- getCurrentDirectory
#ifndef RELOCATABLE
dataDir <- getDataDir
#endif
binDir <- takeDirectory `fmap` getExecutablePath
let instDir = normalise . joinPath . init . splitPath $ binDir
let handle :: X.IOException -> IO String
handle _e = return ""
userDir <- X.catch (getAppUserDataDirectory "cryptol") handle
let searchPath = [ curDir
, userDir
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
, instDir </> "cryptol"
#else
, instDir </> "share" </> "cryptol"
#endif
#ifndef RELOCATABLE
, dataDir
#endif
]
return ModuleEnv
{ meLoadedModules = mempty
, meNameSeeds = T.nameSeeds
, meEvalEnv = mempty
, meFocusedModule = Nothing
, meSearchPath = searchPath
, meDynEnv = mempty
, meMonoBinds = True
, meSolverConfig = T.SolverConfig
{ T.solverPath = "z3"
, T.solverArgs = [ "-smt2", "-in" ]
, T.solverVerbose = 0
, T.solverPreludePath = searchPath
}
, meCoreLint = NoCoreLint
, meSupply = emptySupply
}
focusModule :: ModName -> ModuleEnv -> Maybe ModuleEnv
focusModule n me = do
guard (isLoaded n (meLoadedModules me))
return me { meFocusedModule = Just n }
loadedModules :: ModuleEnv -> [T.Module]
loadedModules = map lmModule . getLoadedModules . meLoadedModules
loadedNonParamModules :: ModuleEnv -> [T.Module]
loadedNonParamModules = map lmModule . lmLoadedModules . meLoadedModules
hasParamModules :: ModuleEnv -> Bool
hasParamModules = not . null . lmLoadedParamModules . meLoadedModules
allDeclGroups :: ModuleEnv -> [T.DeclGroup]
allDeclGroups = concatMap T.mDecls . loadedNonParamModules
data ModContext = ModContext
{ mctxParams :: IfaceParams
, mctxDecls :: IfaceDecls
, mctxNames :: R.NamingEnv
, mctxNameDisp :: NameDisp
, mctxTypeProvenace :: Map Name DeclProvenance
, mctxValueProvenance :: Map Name DeclProvenance
}
data DeclProvenance =
NameIsImportedFrom ModName
| NameIsLocalPublic
| NameIsLocalPrivate
| NameIsParameter
| NameIsDynamicDecl
deriving (Eq,Ord)
focusedEnv :: ModuleEnv -> ModContext
focusedEnv me =
ModContext
{ mctxParams = parameters
, mctxDecls = mconcat (dynDecls : localDecls : importedDecls)
, mctxNames = namingEnv
, mctxNameDisp = R.toNameDisp namingEnv
, mctxTypeProvenace = fst provenance
, mctxValueProvenance = snd provenance
}
where
(importedNames,importedDecls,importedProvs) = unzip3 (map loadImport imports)
localDecls = publicDecls `mappend` privateDecls
localNames = R.unqualifiedEnv localDecls `mappend`
R.modParamsNamingEnv parameters
dynDecls = deIfaceDecls (meDynEnv me)
dynNames = deNames (meDynEnv me)
namingEnv = dynNames `R.shadowing`
localNames `R.shadowing`
mconcat importedNames
provenance = shadowProvs
$ declsProv NameIsDynamicDecl dynDecls
: declsProv NameIsLocalPublic publicDecls
: declsProv NameIsLocalPrivate privateDecls
: paramProv parameters
: importedProvs
(imports, parameters, publicDecls, privateDecls) =
case meFocusedModule me of
Nothing -> (mempty, noIfaceParams, mempty, mempty)
Just fm ->
case lookupModule fm me of
Just lm ->
let Iface { .. } = lmInterface lm
in (T.mImports (lmModule lm), ifParams, ifPublic, ifPrivate)
Nothing -> panic "focusedEnv" ["Focused module is not loaded."]
loadImport imp =
case lookupModule (iModule imp) me of
Just lm ->
let decls = ifPublic (lmInterface lm)
in ( R.interpImport imp decls
, decls
, declsProv (NameIsImportedFrom (iModule imp)) decls
)
Nothing -> panic "focusedEnv"
[ "Missing imported module: " ++ show (pp (iModule imp)) ]
shadowProvs ps = let (tss,vss) = unzip ps
in (Map.unions tss, Map.unions vss)
paramProv IfaceParams { .. } = (doMap ifParamTypes, doMap ifParamFuns)
where doMap mp = const NameIsParameter <$> mp
declsProv prov IfaceDecls { .. } =
( Map.unions [ doMap ifTySyns, doMap ifNewtypes, doMap ifAbstractTypes ]
, doMap ifDecls
)
where doMap mp = const prov <$> mp
data ModulePath = InFile FilePath
| InMem String ByteString
deriving (Show, Generic, NFData)
instance Eq ModulePath where
p1 == p2 =
case (p1,p2) of
(InFile x, InFile y) -> x == y
(InMem a _, InMem b _) -> a == b
_ -> False
instance PP ModulePath where
ppPrec _ e =
case e of
InFile p -> text p
InMem l _ -> parens (text l)
modulePathLabel :: ModulePath -> String
modulePathLabel p =
case p of
InFile path -> path
InMem lab _ -> lab
data LoadedModules = LoadedModules
{ lmLoadedModules :: [LoadedModule]
, lmLoadedParamModules :: [LoadedModule]
} deriving (Show, Generic, NFData)
getLoadedModules :: LoadedModules -> [LoadedModule]
getLoadedModules x = lmLoadedParamModules x ++ lmLoadedModules x
instance Semigroup LoadedModules where
l <> r = LoadedModules
{ lmLoadedModules = List.unionBy ((==) `on` lmName)
(lmLoadedModules l) (lmLoadedModules r)
, lmLoadedParamModules = lmLoadedParamModules l ++ lmLoadedParamModules r }
instance Monoid LoadedModules where
mempty = LoadedModules { lmLoadedModules = []
, lmLoadedParamModules = []
}
mappend l r = l <> r
data LoadedModule = LoadedModule
{ lmName :: ModName
, lmFilePath :: ModulePath
, lmModuleId :: String
, lmInterface :: Iface
, lmModule :: T.Module
, lmFingerprint :: Fingerprint
} deriving (Show, Generic, NFData)
isLoaded :: ModName -> LoadedModules -> Bool
isLoaded mn lm = any ((mn ==) . lmName) (getLoadedModules lm)
isLoadedParamMod :: ModName -> LoadedModules -> Bool
isLoadedParamMod mn ln = any ((mn ==) . lmName) (lmLoadedParamModules ln)
lookupModule :: ModName -> ModuleEnv -> Maybe LoadedModule
lookupModule mn me = search lmLoadedModules `mplus` search lmLoadedParamModules
where
search how = List.find ((mn ==) . lmName) (how (meLoadedModules me))
addLoadedModule ::
ModulePath -> String -> Fingerprint -> T.Module -> LoadedModules -> LoadedModules
addLoadedModule path ident fp tm lm
| isLoaded (T.mName tm) lm = lm
| T.isParametrizedModule tm = lm { lmLoadedParamModules = loaded :
lmLoadedParamModules lm }
| otherwise = lm { lmLoadedModules =
lmLoadedModules lm ++ [loaded] }
where
loaded = LoadedModule
{ lmName = T.mName tm
, lmFilePath = path
, lmModuleId = ident
, lmInterface = genIface tm
, lmModule = tm
, lmFingerprint = fp
}
removeLoadedModule :: (LoadedModule -> Bool) -> LoadedModules -> LoadedModules
removeLoadedModule rm lm =
LoadedModules
{ lmLoadedModules = filter (not . rm) (lmLoadedModules lm)
, lmLoadedParamModules = filter (not . rm) (lmLoadedParamModules lm)
}
data DynamicEnv = DEnv
{ deNames :: R.NamingEnv
, deDecls :: [T.DeclGroup]
, deEnv :: EvalEnv
} deriving Generic
instance Semigroup DynamicEnv where
de1 <> de2 = DEnv
{ deNames = deNames de1 <> deNames de2
, deDecls = deDecls de1 <> deDecls de2
, deEnv = deEnv de1 <> deEnv de2
}
instance Monoid DynamicEnv where
mempty = DEnv
{ deNames = mempty
, deDecls = mempty
, deEnv = mempty
}
mappend de1 de2 = de1 <> de2
deIfaceDecls :: DynamicEnv -> IfaceDecls
deIfaceDecls DEnv { deDecls = dgs } =
mconcat [ IfaceDecls
{ ifTySyns = Map.empty
, ifNewtypes = Map.empty
, ifAbstractTypes = Map.empty
, ifDecls = Map.singleton (ifDeclName ifd) ifd
}
| decl <- concatMap T.groupDecls dgs
, let ifd = mkIfaceDecl decl
]