packages feed

lhc-0.6.20081127: src/FrontEnd/Exports.hs

-- | determine export\/imports for modules via fixpoint recursion

module FrontEnd.Exports(determineExports,ModInfo(..),modInfoHsModule_s) where

import Data.DeriveTH
import Data.Derive.All
import Control.Monad.Identity
import Data.Monoid
import List
import Maybe
import Prelude hiding (putStr,putStrLn)
import qualified Data.Map as Map
import qualified Data.Set as Set

import CharIO
import Doc.DocLike
import FindFixpoint
import FlagDump as FD
import FrontEnd.SrcLoc
import FrontEnd.HsSyn
import Name.Name as Name
import Options
import Util.Relation as R
import FrontEnd.Warning

data ModInfo = ModInfo {
    modInfoName :: Module,
    modInfoDefs :: [(Name,SrcLoc,[Name])],
    modInfoConsArity :: [(Name,Int)],
    modInfoExport :: [Name],
    modInfoImport :: [(Name,[Name])],
    modInfoHsModule :: HsModule,
    modInfoOptions :: Opt
    }
$(derive makeUpdate ''ModInfo)

instance Eq ModInfo where
    a == b = modInfoName a == modInfoName b

instance Ord ModInfo where
    compare a b = compare (modInfoName a) (modInfoName b)

modInfoModImports m =  mp  [ i | i <- hsModuleImports (modInfoHsModule m)] where
    mp xs
        | any ((== Module "Prelude") . hsImportDeclModule) xs = xs
        | optPrelude (modInfoOptions m) = (prelude:xs)
        | otherwise = xs
    prelude = HsImportDecl { hsImportDeclSrcLoc = bogusASrcLoc, hsImportDeclModule = Module "Prelude", hsImportDeclSpec = Nothing, hsImportDeclAs = Nothing, hsImportDeclQualified = False }

--doExports :: [(Module,[Name])] -> [[ModInfo]] -> [[ModInfo]] -> IO [[ModInfo]]

determineExports :: [(Name,SrcLoc,[Name])] -> [(Module,[Name])] -> [ModInfo]  -> IO [ModInfo]
determineExports defs ae ms = do
    wdump FD.Progress $ do
        putErrLn $ "Determining Exports/Imports: " ++ show (sort [ m | m <- map modInfoName ms])
        --mapM_ CharIO.print [ (modInfoName m, map hsImportDeclModule $ modInfoModImports m) | m <- ms]
    let ds = [ (n,cs) | (n,_,cs) <- defs ++ concatMap modInfoDefs ms]
    ms <- determineExports' ds ae ms
    let g m = do
            when (dump FD.Imports) $ do
                putStrLn $ " -- Imports -- " ++  show (modInfoName m)
                putStr $ unlines  (map show $ sort (modInfoImport m))
            when (dump FD.Exports) $ do
                putStrLn $ " -- Exports -- " ++  show (modInfoName m)
                putStr $ unlines (map show $ sort (modInfoExport m))
    mapM_ g ms
    processIOErrors
    return ms

determineExports' :: [(Name,[Name])] -> [(Module,[Name])] -> [ModInfo] -> IO [ModInfo]
determineExports' owns doneMods todoMods = mdo
    rs <- solve Nothing  mempty [ x |(_,_,x) <- ms]
    let lf m = maybe (fail $ "determineExports'.lf: " ++ show m) return $  Map.lookup m  $ dmodMap `mappend` Map.fromList [ (modInfoName x,Set.fromList [(toUnqualified x,x) | x <- modInfoExport x]) |  x  <- xs]
    let g  (mi,ne) = do
            ne' <- ce mi ne
            return mi { modInfoExport = ne', modInfoImport = toRelationList $ runIdentity $  getImports mi lf  }
    xs <- mapM g $ zip todoMods rs
    return xs
    where
    ms = [ (i,mi, getExports mi le ) | mi <- todoMods | i <- [0..]]
    dmodMap = Map.fromList  [ ( x,Set.fromList [(toUnqualified n,n) | n <- xs]) |  (x,xs) <- doneMods ]
    modMap = fmap return dmodMap `mappend` (Map.fromList [ (modInfoName n,getVal i) | (i,n,_) <- ms])
    ownsMap = Map.fromList owns
    le m = runIdentity $ maybe (fail $ "determineExports'.le: " ++ show m) return $ Map.lookup m modMap
    ce m x = mapM f (toRelationList x) where
        f (x,[y]) = return y
        f (_,[]) = error "can't happen"
        f (x,ys) = warn bogusASrcLoc "ambiguous-export" ("module " <> fromModule (modInfoName m) <> " has ambiguous exports: " ++ show ys) >> return (head ys)

    getExports :: Monad m => ModInfo -> (Module -> m (Rel Name Name)) -> m (Rel Name Name)
    getExports mi@ModInfo { modInfoHsModule = m@HsModule { hsModuleExports = Nothing } } _ = return $ defsToRel (modInfoDefs mi)
    getExports mi le | HsModule { hsModuleExports = Just es } <- modInfoHsModule mi = do
        is <- getImports mi le
        let f (HsEModuleContents m) = mapDomain g unqs `R.intersection` qs where
                (qs,unqs) = partitionDomain (isJust . getModule ) is
                g x = Name.qualifyName m x
            f z = entSpec False is z
        return $ mapDomain toUnqualified (R.unions $ map f es)

    -- | determine what is visible in a module
    getImports :: Monad m => ModInfo -> (Module -> m (Rel Name Name)) -> m (Rel Name Name)
    getImports mi le = mapM f is >>= \xs -> return (mconcat (ls:xs))  where
        f x = do
            es <- le (hsImportDeclModule x)
            Just as <- return $  hsImportDeclAs x `mplus` Just (hsImportDeclModule x)
            es' <- case hsImportDeclSpec x of
                Nothing -> return es -- return $ (mapDomain ((Name.qualifyName as)) es `mappend` if hsImportDeclQualified x then mempty else es)
                Just (isHiding,xs) -> do
                    let listed = mconcat $ map (entSpec isHiding es . importToExport) xs
                    return $ if isHiding then es Set.\\ listed else listed
            return $ (mapDomain ((Name.qualifyName as)) es' `mappend` if hsImportDeclQualified x then mempty else es')
        is = modInfoModImports mi
        ls = R.fromList $  concat [ [(toUnqualified z,z),(z,z)]| (z, _, _) <- modInfoDefs mi]

    entSpec ::
        Bool     -- ^ is it a hiding import?
        -> Rel Name Name  -- ^ the original relation
        -> HsExportSpec   -- ^ the specification
        -> Rel Name Name  -- ^ the subset satisfying the specification
    entSpec isHiding rel (HsEVar n) = restrictDomain (== toName Val n) rel
    entSpec isHiding rel (HsEAbs n) = restrictDomain (`elem` [ toName x n | x <- ts]) rel  where
        ts = TypeConstructor:ClassName:if isHiding then [DataConstructor] else []
    entSpec isHiding rel (HsEThingWith n xs) = restrictDomain (\x -> x `elem` concat (ct:(map (cd) xs)))  rel where
        ct = [toName TypeConstructor n, toName ClassName n]
        cd n =  [toName DataConstructor n, toName Val n, toName FieldLabel n ]
    entSpec isHiding rel (HsEThingAll n) = restrictDomain (`elem` ct ) rel `mappend` restrictRange (`elem` ss) rel where
        ct = [toName TypeConstructor n, toName ClassName n]
        ss = concat $ concat [ maybeToList (Map.lookup x ownsMap) | x <- Set.toList $ range (restrictDomain (`elem` ct) rel)]
        cd n =  [toName DataConstructor n, toName Val n, toName FieldLabel n ]


defsToRel xs = R.fromList $ map f xs where
    f (n,_,_) = (toUnqualified n,n)

importToExport :: HsImportSpec -> HsExportSpec
importToExport x = f x where
    f (HsIVar n) = HsEVar n
    f (HsIAbs n) = HsEAbs n
    f (HsIThingAll n) = HsEThingAll n
    f (HsIThingWith n xs) = HsEThingWith n xs