diff options
author | Alec Theriault <alec.theriault@gmail.com> | 2018-12-11 13:47:35 -0500 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2018-12-11 14:23:22 -0500 |
commit | f582379de2c4ff7577235c926ad953debdae3cac (patch) | |
tree | df39b7a00d1730be04da120ca452517043478809 /compiler/main | |
parent | 21339c9f6bfb952a3a0b8de5ee649d46dfbf0d9b (diff) | |
download | haskell-f582379de2c4ff7577235c926ad953debdae3cac.tar.gz |
Support generating HIE files
Adds a `-fenable-ide-info` flag which instructs GHC to generate `.hie`
files (see the wiki page:
https://ghc.haskell.org/trac/ghc/wiki/HIEFiles).
This is a rebased version of Zubin Duggal's (@wz1000) GHC changes for
his GSOC project, as posted here:
https://gist.github.com/wz1000/5ed4ddd0d3e96d6bc75e095cef95363d.
Test Plan: ./validate
Reviewers: bgamari, gershomb, nomeata, alanz, sjakobi
Reviewed By: alanz, sjakobi
Subscribers: alanz, hvr, sjakobi, rwbarton, wz1000, carter
Differential Revision: https://phabricator.haskell.org/D5239
Diffstat (limited to 'compiler/main')
-rw-r--r-- | compiler/main/DriverPipeline.hs | 38 | ||||
-rw-r--r-- | compiler/main/DynFlags.hs | 22 | ||||
-rw-r--r-- | compiler/main/Finder.hs | 23 | ||||
-rw-r--r-- | compiler/main/GhcMake.hs | 13 | ||||
-rw-r--r-- | compiler/main/HscMain.hs | 68 | ||||
-rw-r--r-- | compiler/main/HscTypes.hs | 4 |
6 files changed, 134 insertions, 34 deletions
diff --git a/compiler/main/DriverPipeline.hs b/compiler/main/DriverPipeline.hs index 295d36284f..f1a5cb46e0 100644 --- a/compiler/main/DriverPipeline.hs +++ b/compiler/main/DriverPipeline.hs @@ -75,6 +75,8 @@ import Data.Maybe import Data.Version import Data.Either ( partitionEithers ) +import Data.Time ( UTCTime ) + -- --------------------------------------------------------------------------- -- Pre-process @@ -1016,6 +1018,7 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn dflags0 let o_file = ml_obj_file location -- The real object file hi_file = ml_hi_file location + hie_file = ml_hie_file location dest_file | writeInterfaceOnlyMode dflags = hi_file | otherwise @@ -1023,7 +1026,7 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn dflags0 -- Figure out if the source has changed, for recompilation avoidance. -- - -- Setting source_unchanged to True means that M.o seems + -- Setting source_unchanged to True means that M.o (or M.hie) seems -- to be up to date wrt M.hs; so no need to recompile unless imports have -- changed (which the compiler itself figures out). -- Setting source_unchanged to False tells the compiler that M.o is out of @@ -1037,13 +1040,14 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn dflags0 -- (b) we aren't going all the way to .o file (e.g. ghc -S) then return SourceModified -- Otherwise look at file modification dates - else do dest_file_exists <- doesFileExist dest_file - if not dest_file_exists - then return SourceModified -- Need to recompile - else do t2 <- getModificationUTCTime dest_file - if t2 > src_timestamp - then return SourceUnmodified - else return SourceModified + else do dest_file_mod <- sourceModified dest_file src_timestamp + hie_file_mod <- if gopt Opt_WriteHie dflags + then sourceModified hie_file + src_timestamp + else pure False + if dest_file_mod || hie_file_mod + then return SourceModified + else return SourceUnmodified PipeState{hsc_env=hsc_env'} <- getPipeState @@ -1062,6 +1066,7 @@ runPhase (RealPhase (Hsc src_flavour)) input_fn dflags0 ms_obj_date = Nothing, ms_parsed_mod = Nothing, ms_iface_date = Nothing, + ms_hie_date = Nothing, ms_textual_imps = imps, ms_srcimps = src_imps } @@ -1634,8 +1639,9 @@ getLocation src_flavour mod_name = do location1 <- liftIO $ mkHomeModLocation2 dflags mod_name basename suff -- Boot-ify it if necessary - let location2 | HsBootFile <- src_flavour = addBootSuffixLocn location1 - | otherwise = location1 + let location2 + | HsBootFile <- src_flavour = addBootSuffixLocnOut location1 + | otherwise = location1 -- Take -ohi into account if present @@ -2251,6 +2257,18 @@ writeInterfaceOnlyMode dflags = gopt Opt_WriteInterface dflags && HscNothing == hscTarget dflags +-- | Figure out if a source file was modified after an output file (or if we +-- anyways need to consider the source file modified since the output is gone). +sourceModified :: FilePath -- ^ destination file we are looking for + -> UTCTime -- ^ last time of modification of source file + -> IO Bool -- ^ do we need to regenerate the output? +sourceModified dest_file src_timestamp = do + dest_file_exists <- doesFileExist dest_file + if not dest_file_exists + then return True -- Need to recompile + else do t2 <- getModificationUTCTime dest_file + return (t2 <= src_timestamp) + -- | What phase to run after one of the backend code generators has run hscPostBackendPhase :: DynFlags -> HscSource -> HscTarget -> Phase hscPostBackendPhase _ HsBootFile _ = StopLn diff --git a/compiler/main/DynFlags.hs b/compiler/main/DynFlags.hs index 9e93e47eeb..6c4ee86084 100644 --- a/compiler/main/DynFlags.hs +++ b/compiler/main/DynFlags.hs @@ -511,6 +511,7 @@ data GeneralFlag | Opt_OmitInterfacePragmas | Opt_ExposeAllUnfoldings | Opt_WriteInterface -- forces .hi files to be written even with -fno-code + | Opt_WriteHie -- generate .hie files -- profiling opts | Opt_AutoSccsOnIndividualCafs @@ -544,6 +545,7 @@ data GeneralFlag | Opt_GhciSandbox | Opt_GhciHistory | Opt_GhciLeakCheck + | Opt_ValidateHie | Opt_LocalGhciHistory | Opt_NoIt | Opt_HelpfulErrors @@ -942,12 +944,14 @@ data DynFlags = DynFlags { objectDir :: Maybe String, dylibInstallName :: Maybe String, hiDir :: Maybe String, + hieDir :: Maybe String, stubDir :: Maybe String, dumpDir :: Maybe String, objectSuf :: String, hcSuf :: String, hiSuf :: String, + hieSuf :: String, canGenerateDynamicToo :: IORef Bool, dynObjectSuf :: String, @@ -1910,12 +1914,14 @@ defaultDynFlags mySettings (myLlvmTargets, myLlvmPasses) = objectDir = Nothing, dylibInstallName = Nothing, hiDir = Nothing, + hieDir = Nothing, stubDir = Nothing, dumpDir = Nothing, objectSuf = phaseInputExt StopLn, hcSuf = phaseInputExt HCc, hiSuf = "hi", + hieSuf = "hie", canGenerateDynamicToo = panic "defaultDynFlags: No canGenerateDynamicToo", dynObjectSuf = "dyn_" ++ phaseInputExt StopLn, @@ -2493,10 +2499,10 @@ getVerbFlags dflags | verbosity dflags >= 4 = ["-v"] | otherwise = [] -setObjectDir, setHiDir, setStubDir, setDumpDir, setOutputDir, +setObjectDir, setHiDir, setHieDir, setStubDir, setDumpDir, setOutputDir, setDynObjectSuf, setDynHiSuf, setDylibInstallName, - setObjectSuf, setHiSuf, setHcSuf, parseDynLibLoaderMode, + setObjectSuf, setHiSuf, setHieSuf, setHcSuf, parseDynLibLoaderMode, setPgmP, addOptl, addOptc, addOptP, addCmdlineFramework, addHaddockOpts, addGhciScript, setInteractivePrint @@ -2506,18 +2512,24 @@ setOutputFile, setDynOutputFile, setOutputHi, setDumpPrefixForce setObjectDir f d = d { objectDir = Just f} setHiDir f d = d { hiDir = Just f} +setHieDir f d = d { hieDir = Just f} setStubDir f d = d { stubDir = Just f , includePaths = addGlobalInclude (includePaths d) [f] } -- -stubdir D adds an implicit -I D, so that gcc can find the _stub.h file -- \#included from the .hc file when compiling via C (i.e. unregisterised -- builds). setDumpDir f d = d { dumpDir = Just f} -setOutputDir f = setObjectDir f . setHiDir f . setStubDir f . setDumpDir f +setOutputDir f = setObjectDir f + . setHieDir f + . setHiDir f + . setStubDir f + . setDumpDir f setDylibInstallName f d = d { dylibInstallName = Just f} setObjectSuf f d = d { objectSuf = f} setDynObjectSuf f d = d { dynObjectSuf = f} setHiSuf f d = d { hiSuf = f} +setHieSuf f d = d { hieSuf = f} setDynHiSuf f d = d { dynHiSuf = f} setHcSuf f d = d { hcSuf = f} @@ -3062,8 +3074,10 @@ dynamic_flags_deps = [ , make_ord_flag defGhcFlag "dynosuf" (hasArg setDynObjectSuf) , make_ord_flag defGhcFlag "hcsuf" (hasArg setHcSuf) , make_ord_flag defGhcFlag "hisuf" (hasArg setHiSuf) + , make_ord_flag defGhcFlag "hiesuf" (hasArg setHieSuf) , make_ord_flag defGhcFlag "dynhisuf" (hasArg setDynHiSuf) , make_ord_flag defGhcFlag "hidir" (hasArg setHiDir) + , make_ord_flag defGhcFlag "hiedir" (hasArg setHieDir) , make_ord_flag defGhcFlag "tmpdir" (hasArg setTmpDir) , make_ord_flag defGhcFlag "stubdir" (hasArg setStubDir) , make_ord_flag defGhcFlag "dumpdir" (hasArg setDumpDir) @@ -4088,6 +4102,7 @@ fFlagsDeps = [ flagSpec "gen-manifest" Opt_GenManifest, flagSpec "ghci-history" Opt_GhciHistory, flagSpec "ghci-leak-check" Opt_GhciLeakCheck, + flagSpec "validate-ide-info" Opt_ValidateHie, flagGhciSpec "local-ghci-history" Opt_LocalGhciHistory, flagGhciSpec "no-it" Opt_NoIt, flagSpec "ghci-sandbox" Opt_GhciSandbox, @@ -4143,6 +4158,7 @@ fFlagsDeps = [ flagSpec "strictness" Opt_Strictness, flagSpec "use-rpaths" Opt_RPath, flagSpec "write-interface" Opt_WriteInterface, + flagSpec "write-ide-info" Opt_WriteHie, flagSpec "unbox-small-strict-fields" Opt_UnboxSmallStrictFields, flagSpec "unbox-strict-fields" Opt_UnboxStrictFields, flagSpec "version-macros" Opt_VersionMacros, diff --git a/compiler/main/Finder.hs b/compiler/main/Finder.hs index 57d608bbf7..2db0a5e0b4 100644 --- a/compiler/main/Finder.hs +++ b/compiler/main/Finder.hs @@ -482,23 +482,27 @@ mkHomeModLocation2 dflags mod src_basename ext = do obj_fn = mkObjPath dflags src_basename mod_basename hi_fn = mkHiPath dflags src_basename mod_basename + hie_fn = mkHiePath dflags src_basename mod_basename return (ModLocation{ ml_hs_file = Just (src_basename <.> ext), ml_hi_file = hi_fn, - ml_obj_file = obj_fn }) + ml_obj_file = obj_fn, + ml_hie_file = hie_fn }) mkHiOnlyModLocation :: DynFlags -> Suffix -> FilePath -> String -> IO ModLocation mkHiOnlyModLocation dflags hisuf path basename = do let full_basename = path </> basename obj_fn = mkObjPath dflags full_basename basename + hie_fn = mkHiePath dflags full_basename basename return ModLocation{ ml_hs_file = Nothing, ml_hi_file = full_basename <.> hisuf, -- Remove the .hi-boot suffix from -- hi_file, if it had one. We always -- want the name of the real .hi file -- in the ml_hi_file field. - ml_obj_file = obj_fn + ml_obj_file = obj_fn, + ml_hie_file = hie_fn } -- | Constructs the filename of a .o file for a given source file. @@ -532,6 +536,21 @@ mkHiPath dflags basename mod_basename = hi_basename <.> hisuf hi_basename | Just dir <- hidir = dir </> mod_basename | otherwise = basename +-- | Constructs the filename of a .hie file for a given source file. +-- Does /not/ check whether the .hie file exists +mkHiePath + :: DynFlags + -> FilePath -- the filename of the source file, minus the extension + -> String -- the module name with dots replaced by slashes + -> FilePath +mkHiePath dflags basename mod_basename = hie_basename <.> hiesuf + where + hiedir = hieDir dflags + hiesuf = hieSuf dflags + + hie_basename | Just dir <- hiedir = dir </> mod_basename + | otherwise = basename + -- ----------------------------------------------------------------------------- diff --git a/compiler/main/GhcMake.hs b/compiler/main/GhcMake.hs index 39b6427173..8b2bc01ffe 100644 --- a/compiler/main/GhcMake.hs +++ b/compiler/main/GhcMake.hs @@ -2186,6 +2186,8 @@ summariseFile hsc_env old_summaries file mb_phase obj_allowed maybe_buf then liftIO $ getObjTimestamp location NotBoot else return Nothing hi_timestamp <- maybeGetIfaceDate dflags location + let hie_location = ml_hie_file location + hie_timestamp <- modificationTimeIfExists hie_location -- We have to repopulate the Finder's cache because it -- was flushed before the downsweep. @@ -2193,7 +2195,8 @@ summariseFile hsc_env old_summaries file mb_phase obj_allowed maybe_buf (moduleName (ms_mod old_summary)) (ms_location old_summary) return old_summary{ ms_obj_date = obj_timestamp - , ms_iface_date = hi_timestamp } + , ms_iface_date = hi_timestamp + , ms_hie_date = hie_timestamp } else new_summary src_timestamp @@ -2232,6 +2235,7 @@ summariseFile hsc_env old_summaries file mb_phase obj_allowed maybe_buf else return Nothing hi_timestamp <- maybeGetIfaceDate dflags location + hie_timestamp <- modificationTimeIfExists (ml_hie_file location) extra_sig_imports <- findExtraSigImports hsc_env hsc_src mod_name required_by_imports <- implicitRequirements hsc_env the_imps @@ -2247,6 +2251,7 @@ summariseFile hsc_env old_summaries file mb_phase obj_allowed maybe_buf ms_textual_imps = the_imps ++ extra_sig_imports ++ required_by_imports, ms_hs_date = src_timestamp, ms_iface_date = hi_timestamp, + ms_hie_date = hie_timestamp, ms_obj_date = obj_timestamp }) findSummaryBySourceFile :: [ModSummary] -> FilePath -> Maybe ModSummary @@ -2304,8 +2309,10 @@ summariseModule hsc_env old_summary_map is_boot (L loc wanted_mod) then getObjTimestamp location is_boot else return Nothing hi_timestamp <- maybeGetIfaceDate dflags location + hie_timestamp <- modificationTimeIfExists (ml_hie_file location) return (Just (Right old_summary{ ms_obj_date = obj_timestamp - , ms_iface_date = hi_timestamp})) + , ms_iface_date = hi_timestamp + , ms_hie_date = hie_timestamp })) | otherwise = -- source changed: re-summarise. new_summary location (ms_mod old_summary) src_fn src_timestamp @@ -2389,6 +2396,7 @@ summariseModule hsc_env old_summary_map is_boot (L loc wanted_mod) else return Nothing hi_timestamp <- maybeGetIfaceDate dflags location + hie_timestamp <- modificationTimeIfExists (ml_hie_file location) extra_sig_imports <- findExtraSigImports hsc_env hsc_src mod_name required_by_imports <- implicitRequirements hsc_env the_imps @@ -2404,6 +2412,7 @@ summariseModule hsc_env old_summary_map is_boot (L loc wanted_mod) ms_textual_imps = the_imps ++ extra_sig_imports ++ required_by_imports, ms_hs_date = src_timestamp, ms_iface_date = hi_timestamp, + ms_hie_date = hie_timestamp, ms_obj_date = obj_timestamp }))) diff --git a/compiler/main/HscMain.hs b/compiler/main/HscMain.hs index 9b9edf7d21..c2c912451b 100644 --- a/compiler/main/HscMain.hs +++ b/compiler/main/HscMain.hs @@ -85,6 +85,7 @@ module HscMain import GhcPrelude import Data.Data hiding (Fixity, TyCon) +import Data.Maybe ( fromJust ) import Id import GHCi ( addSptEntry ) import GHCi.RemoteTypes ( ForeignHValue ) @@ -167,10 +168,15 @@ import Data.IORef import System.FilePath as FilePath import System.Directory import System.IO (fixIO) -import qualified Data.Map as Map +import qualified Data.Map as M import qualified Data.Set as S import Data.Set (Set) +import HieAst ( mkHieFile ) +import HieTypes ( getAsts, hie_asts ) +import HieBin ( readHieFile, writeHieFile ) +import HieDebug ( diffFile, validateScopes ) + #include "HsVersions.h" @@ -379,8 +385,8 @@ hscParse' mod_summary hpm_module = rdr_module, hpm_src_files = srcs2, hpm_annotations - = (Map.fromListWith (++) $ annotations pst, - Map.fromList $ ((noSrcSpan,comment_q pst) + = (M.fromListWith (++) $ annotations pst, + M.fromList $ ((noSrcSpan,comment_q pst) :(annotations_comments pst))) } @@ -392,15 +398,41 @@ hscParse' mod_summary -- ----------------------------------------------------------------------------- -- | If the renamed source has been kept, extract it. Dump it if requested. -extract_renamed_stuff :: TcGblEnv -> Hsc (TcGblEnv, RenamedStuff) -extract_renamed_stuff tc_result = do +extract_renamed_stuff :: ModSummary -> TcGblEnv -> Hsc RenamedStuff +extract_renamed_stuff mod_summary tc_result = do let rn_info = getRenamedStuff tc_result dflags <- getDynFlags liftIO $ dumpIfSet_dyn dflags Opt_D_dump_rn_ast "Renamer" $ showAstData NoBlankSrcSpan rn_info - return (tc_result, rn_info) + -- Create HIE files + when (gopt Opt_WriteHie dflags) $ do + hieFile <- mkHieFile mod_summary (tcg_binds tc_result) + (fromJust rn_info) + let out_file = ml_hie_file $ ms_location mod_summary + liftIO $ writeHieFile out_file hieFile + + -- Validate HIE files + when (gopt Opt_ValidateHie dflags) $ do + hs_env <- Hsc $ \e w -> return (e, w) + liftIO $ do + -- Validate Scopes + case validateScopes $ getAsts $ hie_asts hieFile of + [] -> putMsg dflags $ text "Got valid scopes" + xs -> do + putMsg dflags $ text "Got invalid scopes" + mapM_ (putMsg dflags) xs + -- Roundtrip testing + nc <- readIORef $ hsc_NC hs_env + (file', _) <- readHieFile nc out_file + case diffFile hieFile file' of + [] -> + putMsg dflags $ text "Got no roundtrip errors" + xs -> do + putMsg dflags $ text "Got roundtrip errors" + mapM_ (putMsg dflags) xs + return rn_info -- ----------------------------------------------------------------------------- @@ -408,22 +440,23 @@ extract_renamed_stuff tc_result = do hscTypecheckRename :: HscEnv -> ModSummary -> HsParsedModule -> IO (TcGblEnv, RenamedStuff) hscTypecheckRename hsc_env mod_summary rdr_module = runHsc hsc_env $ do - tc_result <- hscTypecheck True mod_summary (Just rdr_module) - extract_renamed_stuff tc_result + tc_result <- hsc_typecheck True mod_summary (Just rdr_module) + rn_info <- extract_renamed_stuff mod_summary tc_result + return (tc_result, rn_info) +-- | Rename and typecheck a module, but don't return the renamed syntax hscTypecheck :: Bool -- ^ Keep renamed source? -> ModSummary -> Maybe HsParsedModule -> Hsc TcGblEnv hscTypecheck keep_rn mod_summary mb_rdr_module = do - tc_result <- hscTypecheck' keep_rn mod_summary mb_rdr_module - _ <- extract_renamed_stuff tc_result + tc_result <- hsc_typecheck keep_rn mod_summary mb_rdr_module + _ <- extract_renamed_stuff mod_summary tc_result return tc_result - -hscTypecheck' :: Bool -- ^ Keep renamed source? +hsc_typecheck :: Bool -- ^ Keep renamed source? -> ModSummary -> Maybe HsParsedModule -> Hsc TcGblEnv -hscTypecheck' keep_rn mod_summary mb_rdr_module = do +hsc_typecheck keep_rn mod_summary mb_rdr_module = do hsc_env <- getHscEnv let hsc_src = ms_hsc_src mod_summary dflags = hsc_dflags hsc_env @@ -433,6 +466,7 @@ hscTypecheck' keep_rn mod_summary mb_rdr_module = do inner_mod = canonicalizeHomeModule dflags mod_name src_filename = ms_hspp_file mod_summary real_loc = realSrcLocSpan $ mkRealSrcLoc (mkFastString src_filename) 1 1 + keep_rn' = gopt Opt_WriteHie dflags || keep_rn MASSERT( moduleUnitId outer_mod == thisPackage dflags ) if hsc_src == HsigFile && not (isHoleModule inner_mod) then ioMsgMaybe $ tcRnInstantiateSignature hsc_env outer_mod' real_loc @@ -440,7 +474,7 @@ hscTypecheck' keep_rn mod_summary mb_rdr_module = do do hpm <- case mb_rdr_module of Just hpm -> return hpm Nothing -> hscParse' mod_summary - tc_result0 <- tcRnModule' mod_summary keep_rn hpm + tc_result0 <- tcRnModule' mod_summary keep_rn' hpm if hsc_src == HsigFile then do (iface, _, _) <- liftIO $ hscSimpleIface hsc_env tc_result0 Nothing ioMsgMaybe $ @@ -1411,7 +1445,8 @@ hscCompileCmmFile hsc_env filename output_filename = runHsc hsc_env $ do where no_loc = ModLocation{ ml_hs_file = Just filename, ml_hi_file = panic "hscCompileCmmFile: no hi file", - ml_obj_file = panic "hscCompileCmmFile: no obj file" } + ml_obj_file = panic "hscCompileCmmFile: no obj file", + ml_hie_file = panic "hscCompileCmmFile: no hie file"} -------------------- Stuff for new code gen --------------------- @@ -1591,7 +1626,8 @@ hscDeclsWithLocation hsc_env0 str source linenumber = -- We use a basically null location for iNTERACTIVE let iNTERACTIVELoc = ModLocation{ ml_hs_file = Nothing, ml_hi_file = panic "hsDeclsWithLocation:ml_hi_file", - ml_obj_file = panic "hsDeclsWithLocation:ml_hi_file"} + ml_obj_file = panic "hsDeclsWithLocation:ml_obj_file", + ml_hie_file = panic "hsDeclsWithLocation:ml_hie_file" } ds_result <- hscDesugar' iNTERACTIVELoc tc_gblenv {- Simplify -} diff --git a/compiler/main/HscTypes.hs b/compiler/main/HscTypes.hs index d0cf7e0dd8..456332daeb 100644 --- a/compiler/main/HscTypes.hs +++ b/compiler/main/HscTypes.hs @@ -2752,6 +2752,8 @@ data ModSummary -- ^ Timestamp of hi file, if we *only* are typechecking (it is -- 'Nothing' otherwise. -- See Note [Recompilation checking in -fno-code mode] and #9243 + ms_hie_date :: Maybe UTCTime, + -- ^ Timestamp of hie file, if we have one ms_srcimps :: [(Maybe FastString, Located ModuleName)], -- ^ Source imports of the module ms_textual_imps :: [(Maybe FastString, Located ModuleName)], @@ -2833,7 +2835,7 @@ showModMsg dflags target recomp mod_summary = showSDoc dflags $ {- ************************************************************************ * * -\subsection{Recmpilation} +\subsection{Recompilation} * * ************************************************************************ -} |