Skip to content

Commit 01e08c9

Browse files
committed
compute deps fingerprint only
Refactor comments for clarity and update rule usage in needsCompilationRule Refactor imports in Handlers.hs for clarity and organization maximize sharing by creating a finger print rule and query result from GetModuleGraph avoid fingerprint nothing to fail in useWithSeparateFingerprintRule add fingerprints rules for module graph: GetModuleGraphTransDepsFingerprints,GetModuleGraphTransReverseDepsFingerprints,GetModuleGraphImmediateReverseDepsFingerprints. fix comment small refactor optimize buildImmediateDepsFingerprintMap use customize finger implement restriction to transitive deps for module graph
1 parent f162053 commit 01e08c9

File tree

6 files changed

+205
-42
lines changed

6 files changed

+205
-42
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,12 @@ typecheckParents :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePat
262262
typecheckParents recorder state nfp = void $ shakeEnqueue (shakeExtras state) parents
263263
where parents = mkDelayedAction "ParentTC" L.Debug (typecheckParentsAction recorder nfp)
264264

265+
266+
useReverseTransDeps :: NormalizedFilePath -> Action (Maybe [NormalizedFilePath])
267+
useReverseTransDeps file = transitiveReverseDependencies file <$> useWithSeparateFingerprintRule_ GetModuleGraphTransDepsFingerprints GetModuleGraph file
265268
typecheckParentsAction :: Recorder (WithPriority Log) -> NormalizedFilePath -> Action ()
266269
typecheckParentsAction recorder nfp = do
267-
revs <- transitiveReverseDependencies nfp <$> useNoFile_ GetModuleGraph
270+
revs <- useReverseTransDeps nfp
268271
case revs of
269272
Nothing -> logWith recorder Info $ LogCouldNotIdentifyReverseDeps nfp
270273
Just rs -> do

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ type instance RuleResult GetParsedModuleWithComments = ParsedModule
7474

7575
type instance RuleResult GetModuleGraph = DependencyInformation
7676

77+
-- | it only compute the fingerprint of the module graph for a file and its dependencies
78+
-- we need this to trigger recompilation when the sub module graph for a file changes
79+
type instance RuleResult GetModuleGraphTransDepsFingerprints = Fingerprint
80+
type instance RuleResult GetModuleGraphTransReverseDepsFingerprints = Fingerprint
81+
type instance RuleResult GetModuleGraphImmediateReverseDepsFingerprints = Fingerprint
82+
7783
data GetKnownTargets = GetKnownTargets
7884
deriving (Show, Generic, Eq, Ord)
7985
instance Hashable GetKnownTargets
@@ -417,6 +423,21 @@ data GetModuleGraph = GetModuleGraph
417423
instance Hashable GetModuleGraph
418424
instance NFData GetModuleGraph
419425

426+
data GetModuleGraphTransDepsFingerprints = GetModuleGraphTransDepsFingerprints
427+
deriving (Eq, Show, Generic)
428+
instance Hashable GetModuleGraphTransDepsFingerprints
429+
instance NFData GetModuleGraphTransDepsFingerprints
430+
431+
data GetModuleGraphTransReverseDepsFingerprints = GetModuleGraphTransReverseDepsFingerprints
432+
deriving (Eq, Show, Generic)
433+
instance Hashable GetModuleGraphTransReverseDepsFingerprints
434+
instance NFData GetModuleGraphTransReverseDepsFingerprints
435+
436+
data GetModuleGraphImmediateReverseDepsFingerprints = GetModuleGraphImmediateReverseDepsFingerprints
437+
deriving (Eq, Show, Generic)
438+
instance Hashable GetModuleGraphImmediateReverseDepsFingerprints
439+
instance NFData GetModuleGraphImmediateReverseDepsFingerprints
440+
420441
data ReportImportCycles = ReportImportCycles
421442
deriving (Eq, Show, Generic)
422443
instance Hashable ReportImportCycles

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

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Development.IDE.Core.Rules(
1212
-- * Types
1313
IdeState, GetParsedModule(..), TransitiveDependencies(..),
1414
GhcSessionIO(..), GetClientSettings(..),
15+
useTransDepModuleGraph,
1516
-- * Functions
1617
runAction,
1718
toIdeResult,
@@ -472,7 +473,7 @@ rawDependencyInformation fs = do
472473
reportImportCyclesRule :: Recorder (WithPriority Log) -> Rules ()
473474
reportImportCyclesRule recorder =
474475
defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \ReportImportCycles file -> fmap (\errs -> if null errs then (Just "1",([], Just ())) else (Nothing, (errs, Nothing))) $ do
475-
DependencyInformation{..} <- useNoFile_ GetModuleGraph
476+
DependencyInformation {depErrorNodes, depPathIdMap} <- useTransDepModuleGraph file
476477
case pathToId depPathIdMap file of
477478
-- The header of the file does not parse, so it can't be part of any import cycles.
478479
Nothing -> pure []
@@ -608,7 +609,7 @@ typeCheckRule recorder = define (cmapWithPrio LogShake recorder) $ \TypeCheck fi
608609
-- very expensive.
609610
when (foi == NotFOI) $
610611
logWith recorder Logger.Warning $ LogTypecheckedFOI file
611-
typeCheckRuleDefinition hsc pm
612+
typeCheckRuleDefinition hsc pm file
612613

613614
knownFilesRule :: Recorder (WithPriority Log) -> Rules ()
614615
knownFilesRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake recorder) $ \GetKnownTargets -> do
@@ -633,17 +634,20 @@ dependencyInfoForFiles fs = do
633634
(rawDepInfo, bm) <- rawDependencyInformation fs
634635
let (all_fs, _all_ids) = unzip $ HM.toList $ pathToIdMap $ rawPathIdMap rawDepInfo
635636
msrs <- uses GetModSummaryWithoutTimestamps all_fs
636-
let mss = map (fmap msrModSummary) msrs
637+
let mss = zip _all_ids $ map (fmap msrModSummary) msrs
637638
let deps = map (\i -> IM.lookup (getFilePathId i) (rawImports rawDepInfo)) _all_ids
638-
nodeKeys = IM.fromList $ catMaybes $ zipWith (\fi mms -> (getFilePathId fi,) . NodeKey_Module . msKey <$> mms) _all_ids mss
639+
nodeKeys = IM.fromList $ catMaybes $ zipWith (\fi (_, mms) -> (getFilePathId fi,) . NodeKey_Module . msKey <$> mms) _all_ids mss
639640
mns = catMaybes $ zipWith go mss deps
640-
go (Just ms) (Just (Right (ModuleImports xs))) = Just $ ModuleNode this_dep_keys ms
641+
go (pid,Just ms) (Just (Right (ModuleImports xs))) = Just $ (pid, ModuleNode this_dep_keys ms)
641642
where this_dep_ids = mapMaybe snd xs
642643
this_dep_keys = mapMaybe (\fi -> IM.lookup (getFilePathId fi) nodeKeys) this_dep_ids
643-
go (Just ms) _ = Just $ ModuleNode [] ms
644+
go (pid, Just ms) _ = Just $ (pid, ModuleNode [] ms)
644645
go _ _ = Nothing
645-
mg = mkModuleGraph mns
646-
pure (fingerprintToBS $ Util.fingerprintFingerprints $ map (maybe fingerprint0 msrFingerprint) msrs, processDependencyInformation rawDepInfo bm mg)
646+
mg = IntMap.fromList $ map (first getFilePathId) mns
647+
let shallowFingers = IntMap.fromList $! foldr' (\(i, m) acc -> case m of
648+
Just x -> (getFilePathId i,msrFingerprint x):acc
649+
Nothing -> acc) [] $ zip _all_ids msrs
650+
pure (fingerprintToBS $ Util.fingerprintFingerprints $ map (maybe fingerprint0 msrFingerprint) msrs, processDependencyInformation rawDepInfo bm mg shallowFingers)
647651

648652
-- This is factored out so it can be directly called from the GetModIface
649653
-- rule. Directly calling this rule means that on the initial load we can
@@ -652,14 +656,15 @@ dependencyInfoForFiles fs = do
652656
typeCheckRuleDefinition
653657
:: HscEnv
654658
-> ParsedModule
659+
-> NormalizedFilePath
655660
-> Action (IdeResult TcModuleResult)
656-
typeCheckRuleDefinition hsc pm = do
661+
typeCheckRuleDefinition hsc pm fp = do
657662
IdeOptions { optDefer = defer } <- getIdeOptions
658663

659664
unlift <- askUnliftIO
660665
let dets = TypecheckHelpers
661666
{ getLinkables = unliftIO unlift . uses_ GetLinkable
662-
, getModuleGraph = unliftIO unlift $ useNoFile_ GetModuleGraph
667+
, getModuleGraph = unliftIO unlift $ useTransDepModuleGraph fp
663668
}
664669
addUsageDependencies $ liftIO $
665670
typecheckModule defer hsc dets pm
@@ -731,6 +736,11 @@ instance Default GhcSessionDepsConfig where
731736
{ fullModuleGraph = True
732737
}
733738

739+
useTransDepModuleGraph :: NormalizedFilePath -> Action DependencyInformation
740+
useTransDepModuleGraph file = filterDependencyInformationReachable file <$> useWithSeparateFingerprintRule_ GetModuleGraphTransDepsFingerprints GetModuleGraph file
741+
useImmediateDepsModuleGraph :: NormalizedFilePath -> Action (Maybe DependencyInformation)
742+
useImmediateDepsModuleGraph file = useWithSeparateFingerprintRule GetModuleGraphTransDepsFingerprints GetModuleGraph file
743+
734744
-- | Note [GhcSessionDeps]
735745
-- ~~~~~~~~~~~~~~~~~~~~~
736746
-- For a file 'Foo', GhcSessionDeps "Foo.hs" results in an HscEnv which includes
@@ -756,9 +766,10 @@ ghcSessionDepsDefinition fullModSummary GhcSessionDepsConfig{..} env file = do
756766
depSessions <- map hscEnv <$> uses_ (GhcSessionDeps_ fullModSummary) deps
757767
ifaces <- uses_ GetModIface deps
758768
let inLoadOrder = map (\HiFileResult{..} -> HomeModInfo hirModIface hirModDetails emptyHomeModInfoLinkable) ifaces
759-
mg <- do
769+
de <- useTransDepModuleGraph file
770+
mg <- mkModuleGraph <$> do
760771
if fullModuleGraph
761-
then depModuleGraph <$> useNoFile_ GetModuleGraph
772+
then return $ IntMap.elems $ depModuleGraph de
762773
else do
763774
let mgs = map hsc_mod_graph depSessions
764775
-- On GHC 9.4+, the module graph contains not only ModSummary's but each `ModuleNode` in the graph
@@ -770,8 +781,7 @@ ghcSessionDepsDefinition fullModSummary GhcSessionDepsConfig{..} env file = do
770781
let module_graph_nodes =
771782
nubOrdOn mkNodeKey (ModuleNode final_deps ms : concatMap mgModSummaries' mgs)
772783
liftIO $ evaluate $ liftRnf rwhnf module_graph_nodes
773-
return $ mkModuleGraph module_graph_nodes
774-
de <- useNoFile_ GetModuleGraph
784+
return module_graph_nodes
775785
session' <- liftIO $ mergeEnvs hsc mg de ms inLoadOrder depSessions
776786

777787
-- Here we avoid a call to to `newHscEnvEqWithImportPaths`, which creates a new
@@ -801,7 +811,7 @@ getModIfaceFromDiskRule recorder = defineEarlyCutoff (cmapWithPrio LogShake reco
801811
, old_value = m_old
802812
, get_file_version = use GetModificationTime_{missingFileDiagnostics = False}
803813
, get_linkable_hashes = \fs -> map (snd . fromJust . hirCoreFp) <$> uses_ GetModIface fs
804-
, get_module_graph = useNoFile_ GetModuleGraph
814+
, get_module_graph = useTransDepModuleGraph f
805815
, regenerate = regenerateHiFile session f ms
806816
}
807817
hsc_env' <- setFileCacheHook (hscEnv session)
@@ -977,7 +987,7 @@ regenerateHiFile sess f ms compNeeded = do
977987
Just pm -> do
978988
-- Invoke typechecking directly to update it without incurring a dependency
979989
-- on the parsed module and the typecheck rules
980-
(diags', mtmr) <- typeCheckRuleDefinition hsc pm
990+
(diags', mtmr) <- typeCheckRuleDefinition hsc pm f
981991
case mtmr of
982992
Nothing -> pure (diags', Nothing)
983993
Just tmr -> do
@@ -1135,7 +1145,7 @@ needsCompilationRule file
11351145
| "boot" `isSuffixOf` fromNormalizedFilePath file =
11361146
pure (Just $ encodeLinkableType Nothing, Just Nothing)
11371147
needsCompilationRule file = do
1138-
graph <- useNoFile GetModuleGraph
1148+
graph <- useImmediateDepsModuleGraph file
11391149
res <- case graph of
11401150
-- Treat as False if some reverse dependency header fails to parse
11411151
Nothing -> pure Nothing
@@ -1247,6 +1257,19 @@ mainRule recorder RulesConfig{..} = do
12471257
persistentDocMapRule
12481258
persistentImportMapRule
12491259
getLinkableRule recorder
1260+
defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \GetModuleGraphTransDepsFingerprints file -> do
1261+
di <- useNoFile_ GetModuleGraph
1262+
let finger = lookupFingerprint file di (depTransDepsFingerprints di)
1263+
return (fingerprintToBS <$> finger, ([], finger))
1264+
defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \GetModuleGraphTransReverseDepsFingerprints file -> do
1265+
di <- useNoFile_ GetModuleGraph
1266+
let finger = lookupFingerprint file di (depTransReverseDepsFingerprints di)
1267+
return (fingerprintToBS <$> finger, ([], finger))
1268+
defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \GetModuleGraphImmediateReverseDepsFingerprints file -> do
1269+
di <- useNoFile_ GetModuleGraph
1270+
let finger = lookupFingerprint file di (depImmediateReverseDepsFingerprints di)
1271+
return (fingerprintToBS <$> finger, ([], finger))
1272+
12501273

12511274
-- | Get HieFile for haskell file on NormalizedFilePath
12521275
getHieFile :: NormalizedFilePath -> Action (Maybe HieFile)

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ module Development.IDE.Core.Shake(
3131
shakeEnqueue,
3232
newSession,
3333
use, useNoFile, uses, useWithStaleFast, useWithStaleFast', delayedAction,
34+
useWithSeparateFingerprintRule,
35+
useWithSeparateFingerprintRule_,
3436
FastResult(..),
3537
use_, useNoFile_, uses_,
3638
useWithStale, usesWithStale,
@@ -1148,6 +1150,23 @@ usesWithStale key files = do
11481150
-- whether the rule succeeded or not.
11491151
traverse (lastValue key) files
11501152

1153+
-- we use separate fingerprint rules to trigger the rebuild of the rule
1154+
useWithSeparateFingerprintRule
1155+
:: (IdeRule k v, IdeRule k1 Fingerprint)
1156+
=> k1 -> k -> NormalizedFilePath -> Action (Maybe v)
1157+
useWithSeparateFingerprintRule fingerKey key file = do
1158+
_ <- use fingerKey file
1159+
useWithoutDependency key emptyFilePath
1160+
1161+
-- we use separate fingerprint rules to trigger the rebuild of the rule
1162+
useWithSeparateFingerprintRule_
1163+
:: (IdeRule k v, IdeRule k1 Fingerprint)
1164+
=> k1 -> k -> NormalizedFilePath -> Action v
1165+
useWithSeparateFingerprintRule_ fingerKey key file = do
1166+
useWithSeparateFingerprintRule fingerKey key file >>= \case
1167+
Just v -> return v
1168+
Nothing -> liftIO $ throwIO $ BadDependency (show key)
1169+
11511170
useWithoutDependency :: IdeRule k v
11521171
=> k -> NormalizedFilePath -> Action (Maybe v)
11531172
useWithoutDependency key file =

0 commit comments

Comments
 (0)