[project @ 2000-11-22 10:56:53 by simonmar]
[ghc-hetmet.git] / ghc / compiler / compMan / CompManager.lhs
index eb19468..8041e61 100644 (file)
@@ -5,7 +5,9 @@
 
 \begin{code}
 module CompManager ( cmInit, cmLoadModule,
-                     cmGetExpr, cmRunExpr,
+#ifdef GHCI
+                     cmGetExpr, cmTypeExpr, cmRunExpr,
+#endif
                      CmState, emptyCmState  -- abstract
                    )
 where
@@ -15,31 +17,39 @@ where
 import CmLink
 import CmTypes
 import HscTypes
-import HscMain         ( hscExpr )
-import Interpreter     ( HValue )
 import Module          ( ModuleName, moduleName,
-                         isModuleInThisPackage, moduleEnvElts,
+                         isHomeModule, moduleEnvElts,
                          moduleNameUserString )
-import CmStaticInfo    ( PackageConfigInfo, GhciMode(..) )
+import CmStaticInfo    ( GhciMode(..) )
 import DriverPipeline
 import GetImports
 import HscTypes                ( HomeSymbolTable, HomeIfaceTable, 
                          PersistentCompilerState, ModDetails(..) )
+import Type            ( Type )
 import Name            ( lookupNameEnv )
-import RdrName
 import Module
 import PrelNames       ( mainName )
 import HscMain         ( initPersistentCompilerState )
-import Finder          ( findModule, emptyHomeDirCache )
+import Finder
 import UniqFM          ( emptyUFM, lookupUFM, addToUFM, delListFromUFM,
                          UniqFM, listToUFM )
 import Unique          ( Uniquable )
 import Digraph         ( SCC(..), stronglyConnComp )
+import DriverFlags     ( getDynFlags )
+import DriverPhases
 import DriverUtil      ( BarfKind(..), splitFilename3 )
-import CmdLineOpts     ( DynFlags )
+import ErrUtils                ( showPass )
 import Util
 import Outputable
 import Panic           ( panic )
+import CmdLineOpts     ( DynFlags(..) )
+
+#ifdef GHCI
+import Interpreter     ( HValue )
+import HscMain         ( hscExpr, hscTypeExpr )
+import RdrName
+import PrelGHC         ( unsafeCoerce# )
+#endif
 
 -- lang
 import Exception       ( throwDyn )
@@ -48,17 +58,18 @@ import Exception    ( throwDyn )
 import Time             ( ClockTime )
 import Directory        ( getModificationTime, doesFileExist )
 import IO
+import Monad
 import List            ( nub )
 import Maybe           ( catMaybes, fromMaybe, isJust )
-import PrelGHC         ( unsafeCoerce# )
 \end{code}
 
 
 \begin{code}
-cmInit :: PackageConfigInfo -> GhciMode -> IO CmState
-cmInit raw_package_info gmode
-   = emptyCmState raw_package_info gmode
+cmInit :: GhciMode -> IO CmState
+cmInit gmode
+   = emptyCmState gmode
 
+#ifdef GHCI
 cmGetExpr :: CmState
          -> DynFlags
           -> ModuleName
@@ -66,7 +77,7 @@ cmGetExpr :: CmState
           -> IO (CmState, Maybe HValue)
 cmGetExpr cmstate dflags modname expr
    = do (new_pcs, maybe_unlinked_iexpr) <- 
-          hscExpr dflags hst hit pcs (mkModuleInThisPackage modname) expr
+          hscExpr dflags hst hit pcs (mkHomeModule modname) expr
         case maybe_unlinked_iexpr of
           Nothing     -> return (cmstate{ pcs=new_pcs }, Nothing)
           Just uiexpr -> do
@@ -78,11 +89,25 @@ cmGetExpr cmstate dflags modname expr
        CmState{ pcs=pcs, pcms=pcms, pls=pls } = cmstate
        PersistentCMState{ hst=hst, hit=hit } = pcms
 
+cmTypeExpr :: CmState
+         -> DynFlags
+          -> ModuleName
+          -> String
+          -> IO (CmState, Maybe (PrintUnqualified, Type))
+cmTypeExpr cmstate dflags modname expr
+   = do (new_pcs, expr_type) <- 
+          hscTypeExpr dflags hst hit pcs (mkHomeModule modname) expr
+        return (cmstate{ pcs=new_pcs }, expr_type)
+   where
+       CmState{ pcs=pcs, pcms=pcms, pls=pls } = cmstate
+       PersistentCMState{ hst=hst, hit=hit } = pcms
+
 -- The HValue should represent a value of type IO () (Perhaps IO a?)
 cmRunExpr :: HValue -> IO ()
 cmRunExpr hval
    = do unsafeCoerce# hval :: IO ()
        -- putStrLn "done."
+#endif
 
 -- Persistent state just for CM, excluding link & compile subsystems
 data PersistentCMState
@@ -91,15 +116,14 @@ data PersistentCMState
         hit   :: HomeIfaceTable,     -- home interface table
         ui    :: UnlinkedImage,      -- the unlinked images
         mg    :: ModuleGraph,        -- the module graph
-        pci   :: PackageConfigInfo,  -- NEVER CHANGES
         gmode :: GhciMode            -- NEVER CHANGES
      }
 
-emptyPCMS :: PackageConfigInfo -> GhciMode -> PersistentCMState
-emptyPCMS pci gmode
+emptyPCMS :: GhciMode -> PersistentCMState
+emptyPCMS gmode
   = PersistentCMState { hst = emptyHST, hit = emptyHIT,
                         ui  = emptyUI,  mg  = emptyMG, 
-                        pci = pci, gmode = gmode }
+                        gmode = gmode }
 
 emptyHIT :: HomeIfaceTable
 emptyHIT = emptyUFM
@@ -116,9 +140,9 @@ data CmState
         pls    :: PersistentLinkerState    -- link's persistent state
      }
 
-emptyCmState :: PackageConfigInfo -> GhciMode -> IO CmState
-emptyCmState pci gmode
-    = do let pcms = emptyPCMS pci gmode
+emptyCmState :: GhciMode -> IO CmState
+emptyCmState gmode
+    = do let pcms = emptyPCMS gmode
          pcs     <- initPersistentCompilerState
          pls     <- emptyPLS
          return (CmState { pcms   = pcms,
@@ -142,8 +166,10 @@ the system state at the same time.
 
 \begin{code}
 cmLoadModule :: CmState 
-             -> ModuleName
-             -> IO (CmState, Maybe ModuleName)
+             -> FilePath
+             -> IO (CmState,           -- new state
+                   Bool,               -- was successful
+                   [ModuleName])       -- list of modules loaded
 
 cmLoadModule cmstate1 rootname
    = do -- version 1's are the original, before downsweep
@@ -155,14 +181,7 @@ cmLoadModule cmstate1 rootname
         let hit1      = hit    pcms1
         let ui1       = ui     pcms1
    
-        let pcii      = pci   pcms1 -- this never changes
-        let ghci_mode = gmode pcms1 -- ToDo: fix!
-
-        -- During upsweep, look at new summaries to see if source has
-        -- changed.  Here's a function to pass down; it takes a new
-        -- summary.
-        let source_changed :: ModSummary -> Bool
-            source_changed = summary_indicates_source_changed mg1
+        let ghci_mode = gmode pcms1 -- this never changes
 
         -- Do the downsweep to reestablish the module graph
         -- then generate version 2's by removing from HIT,HST,UI any
@@ -171,7 +190,13 @@ cmLoadModule cmstate1 rootname
         -- Throw away the old home dir cache
         emptyHomeDirCache
 
-        hPutStr stderr "cmLoadModule: downsweep begins\n"
+       dflags <- getDynFlags
+        let verb = verbosity dflags
+
+       showPass dflags "Chasing dependencies"
+        when (verb >= 1 && ghci_mode == Batch) $
+           hPutStrLn stderr ("ghc: chasing modules from: " ++ rootname)
+
         mg2unsorted <- downsweep [rootname]
 
         let modnames1   = map name_of_summary mg1
@@ -191,8 +216,8 @@ cmLoadModule cmstate1 rootname
         let reachable_from :: ModuleName -> [ModuleName]
             reachable_from = downwards_closure_of_module mg2unsorted
 
-        hPutStrLn stderr "after tsort:\n"
-        hPutStrLn stderr (showSDoc (vcat (map ppr mg2)))
+        --hPutStrLn stderr "after tsort:\n"
+        --hPutStrLn stderr (showSDoc (vcat (map ppr mg2)))
 
         -- Because we don't take into account source imports when doing
         -- the topological sort, there shouldn't be any cycles in mg2.
@@ -205,7 +230,7 @@ cmLoadModule cmstate1 rootname
         let threaded2 = CmThreaded pcs1 hst2 hit2
 
         (upsweep_complete_success, threaded3, modsDone, newLis)
-           <- upsweep_mods ghci_mode ui2 reachable_from source_changed threaded2 mg2
+           <- upsweep_mods ghci_mode ui2 reachable_from threaded2 mg2
 
         let ui3 = add_to_ui ui2 newLis
         let (CmThreaded pcs3 hst3 hit3) = threaded3
@@ -221,26 +246,29 @@ cmLoadModule cmstate1 rootname
 
          then 
            -- Easy; just relink it all.
-           do hPutStrLn stderr "UPSWEEP COMPLETELY SUCCESSFUL"
+           do when (verb >= 2) $ 
+               hPutStrLn stderr "Upsweep completely successful."
               linkresult 
-                 <- link ghci_mode (any exports_main (moduleEnvElts hst3)) 
-                         newLis pls1
+                 <- link ghci_mode dflags 
+                       (any exports_main (moduleEnvElts hst3)) 
+                        newLis pls1
               case linkresult of
                  LinkErrs _ _
                     -> panic "cmLoadModule: link failed (1)"
                  LinkOK pls3 
                     -> do let pcms3 = PersistentCMState { hst=hst3, hit=hit3, 
                                                           ui=ui3, mg=modsDone, 
-                                                          pci=pcii, gmode=ghci_mode }
+                                                          gmode=ghci_mode }
                           let cmstate3 
                                  = CmState { pcms=pcms3, pcs=pcs3, pls=pls3 }
-                          return (cmstate3, Just rootname)
+                          return (cmstate3, True, map name_of_summary modsDone)
 
          else 
            -- Tricky.  We need to back out the effects of compiling any
            -- half-done cycles, both so as to clean up the top level envs
            -- and to avoid telling the interactive linker to link them.
-           do hPutStrLn stderr "UPSWEEP PARTIALLY SUCCESSFUL"
+           do when (verb >= 2) $
+               hPutStrLn stderr "Upsweep partially successful."
 
               let modsDone_names
                      = map name_of_summary modsDone
@@ -258,53 +286,17 @@ cmLoadModule cmstate1 rootname
                      = map (unJust "linkables_to_link" . findModuleLinkable_maybe ui4)
                            mods_to_keep_names
 
-              linkresult <- link ghci_mode False linkables_to_link pls1
+              linkresult <- link ghci_mode dflags False linkables_to_link pls1
               case linkresult of
                  LinkErrs _ _
                     -> panic "cmLoadModule: link failed (2)"
                  LinkOK pls4
                     -> do let pcms4 = PersistentCMState { hst=hst4, hit=hit4, 
                                                           ui=ui4, mg=mods_to_keep,
-                                                          pci=pcii, gmode=ghci_mode }
+                                                          gmode=ghci_mode }
                           let cmstate4 
                                  = CmState { pcms=pcms4, pcs=pcs3, pls=pls4 }
-                          return (cmstate4, 
-                                  -- choose rather arbitrarily who to return
-                                  if null mods_to_keep then Nothing 
-                                     else Just (last mods_to_keep_names))
-
-
--- Given a bunch of old summaries and a new summary, try and
--- find the corresponding old summary, and, if found, compare
--- its source timestamp with that of the new summary.  If in
--- doubt say True.
-summary_indicates_source_changed :: [ModSummary] -> ModSummary -> Bool
-summary_indicates_source_changed old_summaries new_summary
-   = panic "SISC"
-#if 0
-   = case [old | old <- old_summaries, 
-                 name_of_summary old == name_of_summary new_summary] of
-
-        (_:_:_) -> panic "summary_indicates_newer_source"
-                   
-        []      -> -- can't find a corresponding old summary, so
-                   -- compare source and iface dates in the new summary.
-                   trace (showSDoc (text "SISC: no old summary, new =" 
-                                    <+> pprSummaryTimes new_summary)) (
-                   case (ms_hs_date new_summary, ms_hi_date new_summary) of
-                      (Just hs_t, Just hi_t) -> hs_t > hi_t
-                      other                  -> True
-                   )
-
-        [old]   -> -- found old summary; compare source timestamps
-                   trace (showSDoc (text "SISC: old =" 
-                                    <+> pprSummaryTimes old
-                                    <+> pprSummaryTimes new_summary)) (
-                   case (ms_hs_date old, ms_hs_date new_summary) of
-                      (Just old_t, Just new_t) -> new_t > old_t
-                      other                    -> True
-                   )
-#endif
+                          return (cmstate4, False, mods_to_keep_names)
 
 
 -- Return (names of) all those in modsDone who are part of a cycle
@@ -360,7 +352,6 @@ data CmThreaded  -- stuff threaded through individual module compilations
 upsweep_mods :: GhciMode
              -> UnlinkedImage         -- old linkables
              -> (ModuleName -> [ModuleName])  -- to construct downward closures
-             -> (ModSummary -> Bool)  -- has source changed?
              -> CmThreaded            -- PCS & HST & HIT
              -> [SCC ModSummary]      -- mods to do (the worklist)
                                       -- ...... RETURNING ......
@@ -369,28 +360,27 @@ upsweep_mods :: GhciMode
                     [ModSummary],     -- mods which succeeded
                     [Linkable])       -- new linkables
 
-upsweep_mods ghci_mode oldUI reachable_from source_changed threaded 
+upsweep_mods ghci_mode oldUI reachable_from threaded 
      []
    = return (True, threaded, [], [])
 
-upsweep_mods ghci_mode oldUI reachable_from source_changed threaded 
+upsweep_mods ghci_mode oldUI reachable_from threaded 
      ((CyclicSCC ms):_)
-   = do hPutStrLn stderr ("ghc: module imports form a cycle for modules:\n\t" ++
+   = do hPutStrLn stderr ("Module imports form a cycle for modules:\n\t" ++
                           unwords (map (moduleNameUserString.name_of_summary) ms))
         return (False, threaded, [], [])
 
-upsweep_mods ghci_mode oldUI reachable_from source_changed threaded 
+upsweep_mods ghci_mode oldUI reachable_from threaded 
      ((AcyclicSCC mod):mods)
    = do (threaded1, maybe_linkable) 
            <- upsweep_mod ghci_mode oldUI threaded mod 
                           (reachable_from (name_of_summary mod)) 
-                          (source_changed mod)
         case maybe_linkable of
            Just linkable 
               -> -- No errors; do the rest
                  do (restOK, threaded2, modOKs, linkables) 
                        <- upsweep_mods ghci_mode oldUI reachable_from 
-                                       source_changed threaded1 mods
+                                       threaded1 mods
                     return (restOK, threaded2, mod:modOKs, linkable:linkables)
            Nothing -- we got a compilation error; give up now
               -> return (False, threaded1, [], [])
@@ -418,15 +408,26 @@ upsweep_mod :: GhciMode
             -> CmThreaded
             -> ModSummary
             -> [ModuleName]
-            -> Bool
             -> IO (CmThreaded, Maybe Linkable)
 
-upsweep_mod ghci_mode oldUI threaded1 summary1 
-            reachable_from_here source_might_have_changed
-   = do let mod_name = name_of_summary summary1
+upsweep_mod ghci_mode oldUI threaded1 summary1 reachable_from_here
+   = do hPutStr stderr ("ghc: module " 
+                        ++ moduleNameUserString (name_of_summary summary1) ++ ": ")
+        let mod_name = name_of_summary summary1
         let (CmThreaded pcs1 hst1 hit1) = threaded1
         let old_iface = lookupUFM hit1 (name_of_summary summary1)
 
+        -- We *have* to compile it if we're in batch mode and we can't see
+        -- a previous linkable for it on disk.
+        compilation_mandatory 
+           <- if ghci_mode /= Batch then return False 
+              else case ml_obj_file (ms_location summary1) of
+                      Nothing     -> do --putStrLn "cmcm: object?!"
+                                        return True
+                      Just obj_fn -> do --putStrLn ("cmcm: old obj " ++ obj_fn)
+                                        b <- doesFileExist obj_fn
+                                        return (not b)
+
         let maybe_oldUI_linkable = findModuleLinkable_maybe oldUI mod_name
         maybe_oldDisk_linkable
            <- case ml_obj_file (ms_location summary1) of
@@ -465,19 +466,19 @@ upsweep_mod ghci_mode oldUI threaded1 summary1
 
         case compresult of
 
-           -- Compilation "succeeded", but didn't return a new iface or
+           -- Compilation "succeeded", but didn't return a new
            -- linkable, meaning that compilation wasn't needed, and the
            -- new details were manufactured from the old iface.
-           CompOK details Nothing pcs2
-              -> let hst2         = addToUFM hst1 mod_name details
-                     hit2         = hit1
+           CompOK pcs2 new_details new_iface Nothing
+              -> let hst2         = addToUFM hst1 mod_name new_details
+                     hit2         = addToUFM hit1 mod_name new_iface
                      threaded2    = CmThreaded pcs2 hst2 hit2
                  in  return (threaded2, Just old_linkable)
 
            -- Compilation really did happen, and succeeded.  A new
            -- details, iface and linkable are returned.
-           CompOK details (Just (new_iface, new_linkable)) pcs2
-              -> let hst2      = addToUFM hst1 mod_name details
+           CompOK pcs2 new_details new_iface (Just new_linkable)
+              -> let hst2      = addToUFM hst1 mod_name new_details
                      hit2      = addToUFM hit1 mod_name new_iface
                      threaded2 = CmThreaded pcs2 hst2 hit2
                  in  return (threaded2, Just new_linkable)
@@ -521,10 +522,10 @@ downwards_closure_of_module summaries root
              = (name_of_summary summ, ms_srcimps summ ++ ms_imps summ)
          res = simple_transitive_closure (map toEdge summaries) [root]             
      in
-         trace (showSDoc (text "DC of mod" <+> ppr root
-                          <+> text "=" <+> ppr res)) (
+         --trace (showSDoc (text "DC of mod" <+> ppr root
+         --                 <+> text "=" <+> ppr res)) (
          res
-         )
+         --)
 
 -- Calculate transitive closures from a set of roots given an adjacency list
 simple_transitive_closure :: Eq a => [(a,[a])] -> [a] -> [a]
@@ -567,14 +568,30 @@ topological_sort include_source_imports summaries
 -- Chase downwards from the specified root set, returning summaries
 -- for all home modules encountered.  Only follow source-import
 -- links.
-downsweep :: [ModuleName] -> IO [ModSummary]
+downsweep :: [FilePath] -> IO [ModSummary]
 downsweep rootNm
-   = do rootSummaries <- mapM getSummary rootNm
-        loop (filter (isModuleInThisPackage.ms_mod) rootSummaries)
+   = do rootSummaries <- mapM getRootSummary rootNm
+        loop (filter (isHomeModule.ms_mod) rootSummaries)
      where
+       getRootSummary :: FilePath -> IO ModSummary
+       getRootSummary file
+          | haskellish_file file
+          = do exists <- doesFileExist file
+               if exists then summariseFile file else do
+               throwDyn (OtherError ("can't find file `" ++ file ++ "'"))      
+          | otherwise
+          = do exists <- doesFileExist hs_file
+               if exists then summariseFile hs_file else do
+               exists <- doesFileExist lhs_file
+               if exists then summariseFile lhs_file else do
+               getSummary (mkModuleName file)
+           where 
+                hs_file = file ++ ".hs"
+                lhs_file = file ++ ".lhs"
+
         getSummary :: ModuleName -> IO ModSummary
         getSummary nm
-           | trace ("getSummary: "++ showSDoc (ppr nm)) True
+           -- | trace ("getSummary: "++ showSDoc (ppr nm)) True
            = do found <- findModule nm
                case found of
                    -- Be sure not to use the mod and location passed in to 
@@ -584,7 +601,7 @@ downsweep rootNm
                    -- These will then conflict with the passed-in versions.
                   Just (mod, location) -> summarise mod location
                   Nothing -> throwDyn (OtherError 
-                                   ("no signs of life for module `" 
+                                   ("can't find module `" 
                                      ++ showSDoc (ppr nm) ++ "'"))
                                  
         -- loop invariant: homeSummaries doesn't contain package modules
@@ -599,16 +616,50 @@ downsweep rootNm
                 neededSummaries
                        <- mapM getSummary neededImps
                 let newHomeSummaries
-                       = filter (isModuleInThisPackage.ms_mod) neededSummaries
+                       = filter (isHomeModule.ms_mod) neededSummaries
                 if null newHomeSummaries
                  then return homeSummaries
                  else loop (newHomeSummaries ++ homeSummaries)
 
 
+-----------------------------------------------------------------------------
+-- Summarising modules
+
+-- We have two types of summarisation:
+--
+--    * Summarise a file.  This is used for the root module passed to
+--     cmLoadModule.  The file is read, and used to determine the root
+--     module name.  The module name may differ from the filename.
+--
+--    * Summarise a module.  We are given a module name, and must provide
+--     a summary.  The finder is used to locate the file in which the module
+--     resides.
+
+summariseFile :: FilePath -> IO ModSummary
+summariseFile file
+   = do hspp_fn <- preprocess file
+        modsrc <- readFile hspp_fn
+
+        let (srcimps,imps,mod_name) = getImports modsrc
+           (path, basename, ext) = splitFilename3 file
+
+       Just (mod, location)
+          <- mkHomeModuleLocn mod_name (path ++ '/':basename) file
+          
+        maybe_src_timestamp
+           <- case ml_hs_file location of 
+                 Nothing     -> return Nothing
+                 Just src_fn -> maybe_getModificationTime src_fn
+
+        return (ModSummary mod
+                           location{ml_hspp_file=Just hspp_fn}
+                           srcimps imps
+                           maybe_src_timestamp)
+
 -- Summarise a module, and pick up source and interface timestamps.
 summarise :: Module -> ModuleLocation -> IO ModSummary
 summarise mod location
-   | isModuleInThisPackage mod
+   | isHomeModule mod
    = do let hs_fn = unJust "summarise" (ml_hs_file location)
         hspp_fn <- preprocess hs_fn
         modsrc <- readFile hspp_fn
@@ -619,44 +670,24 @@ summarise mod location
                  Nothing     -> return Nothing
                  Just src_fn -> maybe_getModificationTime src_fn
 
-        -- If the module name is Main, allow it to be in a file
-        -- different from Main.hs, and mash the mod and loc 
-        -- to match.  Otherwise just moan.
-        (mashed_mod, mashed_loc)
-           <- case () of
-              () |  mod_name == moduleName mod
-                 -> return (mod, location)
-                 |  mod_name /= moduleName mod && mod_name == mkModuleName "Main"
-                 -> return (mash mod location "Main")
-                 |  otherwise
-                 -> do hPutStrLn stderr (showSDoc (
-                          text "ghc: warning: file name - module name mismatch:" <+> 
-                          ppr (moduleName mod) <+> text "vs" <+> ppr mod_name))
-                       return (mash mod location (moduleNameUserString (moduleName mod)))
-               where
-                 mash old_mod old_loc new_nm
-                    = (mkHomeModule (mkModuleName new_nm), 
-                       old_loc{ml_hi_file = maybe_swizzle_basename new_nm 
-                                                (ml_hi_file old_loc)})
-
-                 maybe_swizzle_basename new Nothing = Nothing
-                 maybe_swizzle_basename new (Just old) 
-                    = case splitFilename3 old of 
-                         (dir, name, ext) -> Just (dir ++ new ++ ext)
-
-        return (ModSummary mashed_mod 
-                           mashed_loc{ml_hspp_file=Just hspp_fn} 
-                           srcimps imps
-                           maybe_src_timestamp)
+       if mod_name == moduleName mod
+               then return ()
+               else throwDyn (OtherError 
+                       (showSDoc (text "file name does not match module name: "
+                          <+> ppr (moduleName mod) <+> text "vs" 
+                          <+> ppr mod_name)))
+
+        return (ModSummary mod location{ml_hspp_file=Just hspp_fn} 
+                               srcimps imps
+                               maybe_src_timestamp)
 
    | otherwise
    = return (ModSummary mod location [] [] Nothing)
 
-   where
-      maybe_getModificationTime :: FilePath -> IO (Maybe ClockTime)
-      maybe_getModificationTime fn
-         = (do time <- getModificationTime fn
-               return (Just time)) 
-           `catch`
-           (\err -> return Nothing)
+maybe_getModificationTime :: FilePath -> IO (Maybe ClockTime)
+maybe_getModificationTime fn
+   = (do time <- getModificationTime fn
+         return (Just time)) 
+     `catch`
+     (\err -> return Nothing)
 \end{code}