Skip to content

Commit d7efbc3

Browse files
committed
Two recompilation avoidance related bugs
1. Recompilation avoidance regresses in GHC 9.4 due to interactions between GHC and HLS's implementations. Avoid this by filtering out the information that causes the conflict See https://gitlab.haskell.org/ghc/ghc/-/issues/22744. 2. The recompilation avoidance info GHC stores in interfaces can blow up to be extremely large when deserialised from disk. See https://gitlab.haskell.org/ghc/ghc/-/issues/22744 Deduplicate these filepaths.
1 parent ddc67b2 commit d7efbc3

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

ghcide/src/Development/IDE/Core/Compile.hs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ import qualified Language.LSP.Types as LSP
102102
import System.Directory
103103
import System.FilePath
104104
import System.IO.Extra (fixIO, newTempFileWithin)
105+
import System.IO.Unsafe
105106
import Unsafe.Coerce
106107

107108
#if MIN_VERSION_ghc(9,0,1)
@@ -435,6 +436,42 @@ tcRnModule hsc_env tc_helpers pmod = do
435436
-- anywhere. So we zero it out.
436437
-- The field is not serialized or deserialised from disk, so we don't need to remove it
437438
-- while reading an iface from disk, only if we just generated an iface in memory
439+
--
440+
441+
442+
443+
-- | See https://github.com/haskell/haskell-language-server/issues/3450
444+
-- GHC's recompilation avoidance in the presense of TH is less precise than
445+
-- HLS. To avoid GHC from pessimising HLS, we filter out certain dependency information
446+
-- that we track ourselves. See also Note [Recompilation avoidance in the presence of TH]
447+
filterUsages :: [Usage] -> [Usage]
448+
#if MIN_VERSION_ghc(9,3,0)
449+
filterUsages = filter $ \case UsageHomeModuleInterface{} -> False
450+
_ -> True
451+
#else
452+
filterUsages = id
453+
#endif
454+
455+
-- | Mitigation for https://gitlab.haskell.org/ghc/ghc/-/issues/22744
456+
shareUsages :: ModIface -> ModIface
457+
shareUsages iface = iface {mi_usages = usages}
458+
where usages = map go (mi_usages iface)
459+
go usg = usg {usg_file_path = fp}
460+
where !fp = shareFilePath (usg_file_path usg)
461+
462+
filePathMap :: IORef (HashMap.HashMap FilePath FilePath)
463+
filePathMap = unsafePerformIO $ newIORef HashMap.empty
464+
{-# NOINLINE filePathMap #-}
465+
466+
shareFilePath :: FilePath -> FilePath
467+
shareFilePath k = unsafePerformIO $ do
468+
atomicModifyIORef' filePathMap $ \km ->
469+
let new_key = HashMap.lookup k km
470+
in case new_key of
471+
Just v -> (km, v)
472+
Nothing -> (HashMap.insert k k km, k)
473+
{-# NOINLINE shareFilePath #-}
474+
438475

439476
mkHiFileResultNoCompile :: HscEnv -> TcModuleResult -> IO HiFileResult
440477
mkHiFileResultNoCompile session tcm = do
@@ -444,7 +481,7 @@ mkHiFileResultNoCompile session tcm = do
444481
details <- makeSimpleDetails hsc_env_tmp tcGblEnv
445482
sf <- finalSafeMode (ms_hspp_opts ms) tcGblEnv
446483
iface' <- mkIfaceTc hsc_env_tmp sf details ms tcGblEnv
447-
let iface = iface' { mi_globals = Nothing } -- See Note [Clearing mi_globals after generating an iface]
484+
let iface = iface' { mi_globals = Nothing, mi_usages = filterUsages (mi_usages iface') } -- See Note [Clearing mi_globals after generating an iface]
448485
pure $! mkHiFileResult ms iface details (tmrRuntimeModules tcm) Nothing
449486

450487
mkHiFileResultCompile
@@ -486,7 +523,7 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do
486523
let !partial_iface = force (mkPartialIface session details simplified_guts)
487524
final_iface' <- mkFullIface session partial_iface
488525
#endif
489-
let final_iface = final_iface' {mi_globals = Nothing} -- See Note [Clearing mi_globals after generating an iface]
526+
let final_iface = final_iface' {mi_globals = Nothing, mi_usages = filterUsages (mi_usages final_iface')} -- See Note [Clearing mi_globals after generating an iface]
490527

491528
-- Write the core file now
492529
core_file <- case mguts of
@@ -1462,7 +1499,8 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do
14621499
regenerate linkableNeeded
14631500

14641501
case (mb_checked_iface, recomp_iface_reqd) of
1465-
(Just iface, UpToDate) -> do
1502+
(Just iface', UpToDate) -> do
1503+
let iface = shareUsages iface'
14661504
details <- liftIO $ mkDetailsFromIface sessionWithMsDynFlags iface
14671505
-- parse the runtime dependencies from the annotations
14681506
let runtime_deps

ghcide/src/Development/IDE/GHC/Compat.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ module Development.IDE.GHC.Compat(
4343
myCoreToStgExpr,
4444
#endif
4545

46+
Usage(..),
47+
4648
FastStringCompat,
4749
bytesFS,
4850
mkFastStringByteString,
@@ -167,7 +169,7 @@ import GHC.Runtime.Context (icInteractiveModule)
167169
import GHC.Unit.Home.ModInfo (HomePackageTable,
168170
lookupHpt)
169171
#if MIN_VERSION_ghc(9,3,0)
170-
import GHC.Unit.Module.Deps (Dependencies(dep_direct_mods))
172+
import GHC.Unit.Module.Deps (Dependencies(dep_direct_mods), Usage(..))
171173
#else
172174
import GHC.Unit.Module.Deps (Dependencies(dep_mods))
173175
#endif

0 commit comments

Comments
 (0)