Skip to content

Commit 00f4e61

Browse files
authored
Two recompilation avoidance related bugs (#3452)
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 2b6f603 commit 00f4e61

File tree

4 files changed

+52
-6
lines changed

4 files changed

+52
-6
lines changed

.hlint.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
within:
5656
- Development.IDE.Core.Shake
5757
- Development.IDE.GHC.Util
58+
- Development.IDE.Core.FileStore
5859
- Development.IDE.Plugin.CodeAction.Util
5960
- Development.IDE.Graph.Internal.Database
6061
- Development.IDE.Graph.Internal.Paths

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

+29-4
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import Data.Time (UTCTime (..))
7070
import Data.Tuple.Extra (dupe)
7171
import Data.Unique as Unique
7272
import Debug.Trace
73-
import Development.IDE.Core.FileStore (resetInterfaceStore)
73+
import Development.IDE.Core.FileStore (resetInterfaceStore, shareFilePath)
7474
import Development.IDE.Core.Preprocessor
7575
import Development.IDE.Core.RuleTypes
7676
import Development.IDE.Core.Shake
@@ -435,6 +435,30 @@ tcRnModule hsc_env tc_helpers pmod = do
435435
-- anywhere. So we zero it out.
436436
-- The field is not serialized or deserialised from disk, so we don't need to remove it
437437
-- while reading an iface from disk, only if we just generated an iface in memory
438+
--
439+
440+
441+
442+
-- | See https://github.com/haskell/haskell-language-server/issues/3450
443+
-- GHC's recompilation avoidance in the presense of TH is less precise than
444+
-- HLS. To avoid GHC from pessimising HLS, we filter out certain dependency information
445+
-- that we track ourselves. See also Note [Recompilation avoidance in the presence of TH]
446+
filterUsages :: [Usage] -> [Usage]
447+
#if MIN_VERSION_ghc(9,3,0)
448+
filterUsages = filter $ \case UsageHomeModuleInterface{} -> False
449+
_ -> True
450+
#else
451+
filterUsages = id
452+
#endif
453+
454+
-- | Mitigation for https://gitlab.haskell.org/ghc/ghc/-/issues/22744
455+
shareUsages :: ModIface -> ModIface
456+
shareUsages iface = iface {mi_usages = usages}
457+
where usages = map go (mi_usages iface)
458+
go usg@UsageFile{} = usg {usg_file_path = fp}
459+
where !fp = shareFilePath (usg_file_path usg)
460+
go usg = usg
461+
438462

439463
mkHiFileResultNoCompile :: HscEnv -> TcModuleResult -> IO HiFileResult
440464
mkHiFileResultNoCompile session tcm = do
@@ -444,7 +468,7 @@ mkHiFileResultNoCompile session tcm = do
444468
details <- makeSimpleDetails hsc_env_tmp tcGblEnv
445469
sf <- finalSafeMode (ms_hspp_opts ms) tcGblEnv
446470
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]
471+
let iface = iface' { mi_globals = Nothing, mi_usages = filterUsages (mi_usages iface') } -- See Note [Clearing mi_globals after generating an iface]
448472
pure $! mkHiFileResult ms iface details (tmrRuntimeModules tcm) Nothing
449473

450474
mkHiFileResultCompile
@@ -486,7 +510,7 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do
486510
let !partial_iface = force (mkPartialIface session details simplified_guts)
487511
final_iface' <- mkFullIface session partial_iface
488512
#endif
489-
let final_iface = final_iface' {mi_globals = Nothing} -- See Note [Clearing mi_globals after generating an iface]
513+
let final_iface = final_iface' {mi_globals = Nothing, mi_usages = filterUsages (mi_usages final_iface')} -- See Note [Clearing mi_globals after generating an iface]
490514

491515
-- Write the core file now
492516
core_file <- case mguts of
@@ -1462,7 +1486,8 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do
14621486
regenerate linkableNeeded
14631487

14641488
case (mb_checked_iface, recomp_iface_reqd) of
1465-
(Just iface, UpToDate) -> do
1489+
(Just iface', UpToDate) -> do
1490+
let iface = shareUsages iface'
14661491
details <- liftIO $ mkDetailsFromIface sessionWithMsDynFlags iface
14671492
-- parse the runtime dependencies from the annotations
14681493
let runtime_deps

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

+18
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Development.IDE.Core.FileStore(
1818
getModTime,
1919
isWatchSupported,
2020
registerFileWatches,
21+
shareFilePath,
2122
Log(..)
2223
) where
2324

@@ -28,6 +29,8 @@ import Control.Exception
2829
import Control.Monad.Extra
2930
import Control.Monad.IO.Class
3031
import qualified Data.ByteString as BS
32+
import qualified Data.HashMap.Strict as HashMap
33+
import Data.IORef
3134
import qualified Data.Text as T
3235
import qualified Data.Text.Utf16.Rope as Rope
3336
import Data.Time
@@ -76,6 +79,7 @@ import qualified Language.LSP.Types as LSP
7679
import qualified Language.LSP.Types.Capabilities as LSP
7780
import Language.LSP.VFS
7881
import System.FilePath
82+
import System.IO.Unsafe
7983

8084
data Log
8185
= LogCouldNotIdentifyReverseDeps !NormalizedFilePath
@@ -297,3 +301,17 @@ isWatchSupported = do
297301
, Just True <- _dynamicRegistration
298302
-> True
299303
| otherwise -> False
304+
305+
filePathMap :: IORef (HashMap.HashMap FilePath FilePath)
306+
filePathMap = unsafePerformIO $ newIORef HashMap.empty
307+
{-# NOINLINE filePathMap #-}
308+
309+
shareFilePath :: FilePath -> FilePath
310+
shareFilePath k = unsafePerformIO $ do
311+
atomicModifyIORef' filePathMap $ \km ->
312+
let new_key = HashMap.lookup k km
313+
in case new_key of
314+
Just v -> (km, v)
315+
Nothing -> (HashMap.insert k k km, k)
316+
{-# NOINLINE shareFilePath #-}
317+

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

+4-2
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,9 +169,9 @@ 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
172-
import GHC.Unit.Module.Deps (Dependencies(dep_mods))
174+
import GHC.Unit.Module.Deps (Dependencies(dep_mods), Usage(..))
173175
#endif
174176
#else
175177
import GHC.CoreToByteCode (coreExprToBCOs)

0 commit comments

Comments
 (0)