Skip to content

Commit abbf309

Browse files
gbazmergify-bot
authored and
mergify-bot
committed
solver: swap catas with recursion, fuse travs (#7519)
* solver: swap catas with recursion, fuse travs * whoops * add traverseWithKey to cabal-install-solver (#7533) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * replace sequence . mapWithKey with traverseWithKey * more sequence -> traverse * Update Preference.hs fix bad merge * Update Solver.hs Co-authored-by: Gershom Bazerman <[email protected]> Co-authored-by: Emily Pillmore <[email protected]> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit 67c440a) # Conflicts: # cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs # cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs
1 parent 536f5ec commit abbf309

File tree

8 files changed

+143
-96
lines changed

8 files changed

+143
-96
lines changed

cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Cycles.hs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ import Distribution.Solver.Types.PackagePath
1818

1919
-- | Find and reject any nodes with cyclic dependencies
2020
detectCyclesPhase :: Tree d c -> Tree d c
21-
detectCyclesPhase = cata go
21+
detectCyclesPhase = go
2222
where
2323
-- Only check children of choice nodes.
24-
go :: TreeF d c (Tree d c) -> Tree d c
25-
go (PChoiceF qpn rdm gr cs) =
26-
PChoice qpn rdm gr $ fmap (checkChild qpn) cs
27-
go (FChoiceF qfn@(FN qpn _) rdm gr w m d cs) =
28-
FChoice qfn rdm gr w m d $ fmap (checkChild qpn) cs
29-
go (SChoiceF qsn@(SN qpn _) rdm gr w cs) =
30-
SChoice qsn rdm gr w $ fmap (checkChild qpn) cs
31-
go x = inn x
24+
go :: Tree d c -> Tree d c
25+
go (PChoice qpn rdm gr cs) =
26+
PChoice qpn rdm gr $ fmap (checkChild qpn) (fmap go cs)
27+
go (FChoice qfn@(FN qpn _) rdm gr w m d cs) =
28+
FChoice qfn rdm gr w m d $ fmap (checkChild qpn) (fmap go cs)
29+
go (SChoice qsn@(SN qpn _) rdm gr w cs) =
30+
SChoice qsn rdm gr w $ fmap (checkChild qpn) (fmap go cs)
31+
go (GoalChoice rdm cs) = GoalChoice rdm (fmap go cs)
32+
go x@(Fail _ _) = x
33+
go x@(Done _ _) = x
3234

3335
checkChild :: QPN -> Tree d c -> Tree d c
3436
checkChild qpn x@(PChoice _ rdm _ _) = failIfCycle qpn rdm x

cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Explore.hs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,19 +183,18 @@ updateCM cs cm =
183183

184184
-- | Record complete assignments on 'Done' nodes.
185185
assign :: Tree d c -> Tree Assignment c
186-
assign tree = cata go tree $ A M.empty M.empty M.empty
186+
assign tree = go tree (A M.empty M.empty M.empty)
187187
where
188-
go :: TreeF d c (Assignment -> Tree Assignment c)
189-
-> (Assignment -> Tree Assignment c)
190-
go (FailF c fr) _ = Fail c fr
191-
go (DoneF rdm _) a = Done rdm a
192-
go (PChoiceF qpn rdm y ts) (A pa fa sa) = PChoice qpn rdm y $ W.mapWithKey f ts
188+
go :: Tree d c -> Assignment -> Tree Assignment c
189+
go (Fail c fr) _ = Fail c fr
190+
go (Done rdm _) a = Done rdm a
191+
go (PChoice qpn rdm y ts) (A pa fa sa) = PChoice qpn rdm y $ W.mapWithKey f (fmap go ts)
193192
where f (POption k _) r = r (A (M.insert qpn k pa) fa sa)
194-
go (FChoiceF qfn rdm y t m d ts) (A pa fa sa) = FChoice qfn rdm y t m d $ W.mapWithKey f ts
193+
go (FChoice qfn rdm y t m d ts) (A pa fa sa) = FChoice qfn rdm y t m d $ W.mapWithKey f (fmap go ts)
195194
where f k r = r (A pa (M.insert qfn k fa) sa)
196-
go (SChoiceF qsn rdm y t ts) (A pa fa sa) = SChoice qsn rdm y t $ W.mapWithKey f ts
195+
go (SChoice qsn rdm y t ts) (A pa fa sa) = SChoice qsn rdm y t $ W.mapWithKey f (fmap go ts)
197196
where f k r = r (A pa fa (M.insert qsn k sa))
198-
go (GoalChoiceF rdm ts) a = GoalChoice rdm $ fmap ($ a) ts
197+
go (GoalChoice rdm ts) a = GoalChoice rdm $ fmap ($ a) (fmap go ts)
199198

200199
-- | A tree traversal that simultaneously propagates conflict sets up
201200
-- the tree from the leaves and creates a log.

cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Linking.hs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,21 +78,21 @@ type Validate = Reader ValidateState
7878
-- * Equal flag assignments
7979
-- * Equal stanza assignments
8080
validateLinking :: Index -> Tree d c -> Tree d c
81-
validateLinking index = (`runReader` initVS) . cata go
81+
validateLinking index = (`runReader` initVS) . go
8282
where
83-
go :: TreeF d c (Validate (Tree d c)) -> Validate (Tree d c)
83+
go :: Tree d c -> Validate (Tree d c)
8484

85-
go (PChoiceF qpn rdm gr cs) =
86-
PChoice qpn rdm gr <$> T.sequence (W.mapWithKey (goP qpn) cs)
87-
go (FChoiceF qfn rdm gr t m d cs) =
88-
FChoice qfn rdm gr t m d <$> T.sequence (W.mapWithKey (goF qfn) cs)
89-
go (SChoiceF qsn rdm gr t cs) =
90-
SChoice qsn rdm gr t <$> T.sequence (W.mapWithKey (goS qsn) cs)
85+
go (PChoice qpn rdm gr cs) =
86+
PChoice qpn rdm gr <$> (W.traverseWithKey (goP qpn) $ fmap go cs)
87+
go (FChoice qfn rdm gr t m d cs) =
88+
FChoice qfn rdm gr t m d <$> (W.traverseWithKey (goF qfn) $ fmap go cs)
89+
go (SChoice qsn rdm gr t cs) =
90+
SChoice qsn rdm gr t <$> (W.traverseWithKey (goS qsn) $ fmap go cs)
9191

9292
-- For the other nodes we just recurse
93-
go (GoalChoiceF rdm cs) = GoalChoice rdm <$> T.sequence cs
94-
go (DoneF revDepMap s) = return $ Done revDepMap s
95-
go (FailF conflictSet failReason) = return $ Fail conflictSet failReason
93+
go (GoalChoice rdm cs) = GoalChoice rdm <$> T.traverse go cs
94+
go (Done revDepMap s) = return $ Done revDepMap s
95+
go (Fail conflictSet failReason) = return $ Fail conflictSet failReason
9696

9797
-- Package choices
9898
goP :: QPN -> POption -> Validate (Tree d c) -> Validate (Tree d c)

cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ import qualified Distribution.Solver.Modular.WeightedPSQ as W
5151
-- children's versions, and package option. 'addWeights' prepends the new
5252
-- weights to the existing weights, which gives precedence to preferences that
5353
-- are applied later.
54-
addWeights :: [PN -> [Ver] -> POption -> Weight] -> Tree d c -> Tree d c
55-
addWeights fs = trav go
54+
addWeights :: [PN -> [Ver] -> POption -> Weight] -> EndoTreeTrav d c
55+
addWeights fs = go
5656
where
5757
go :: TreeF d c (Tree d c) -> TreeF d c (Tree d c)
5858
go (PChoiceF qpn@(Q _ pn) rdm x cs) =
@@ -69,21 +69,21 @@ addWeights fs = trav go
6969
W.mapWeightsWithKey (\k w -> weights k ++ w) cs)
7070
go x = x
7171

72-
addWeight :: (PN -> [Ver] -> POption -> Weight) -> Tree d c -> Tree d c
72+
addWeight :: (PN -> [Ver] -> POption -> Weight) -> EndoTreeTrav d c
7373
addWeight f = addWeights [f]
7474

7575
version :: POption -> Ver
7676
version (POption (I v _) _) = v
7777

7878
-- | Prefer to link packages whenever possible.
79-
preferLinked :: Tree d c -> Tree d c
79+
preferLinked :: EndoTreeTrav d c
8080
preferLinked = addWeight (const (const linked))
8181
where
8282
linked (POption _ Nothing) = 1
8383
linked (POption _ (Just _)) = 0
8484

8585
-- Works by setting weights on choice nodes. Also applies stanza preferences.
86-
preferPackagePreferences :: (PN -> PackagePreferences) -> Tree d c -> Tree d c
86+
preferPackagePreferences :: (PN -> PackagePreferences) -> EndoTreeTrav d c
8787
preferPackagePreferences pcs =
8888
preferPackageStanzaPreferences pcs .
8989
addWeights [
@@ -127,8 +127,20 @@ preferPackagePreferences pcs =
127127

128128
-- | Traversal that tries to establish package stanza enable\/disable
129129
-- preferences. Works by reordering the branches of stanza choices.
130+
<<<<<<< HEAD:cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs
130131
preferPackageStanzaPreferences :: (PN -> PackagePreferences) -> Tree d c -> Tree d c
131132
preferPackageStanzaPreferences pcs = trav go
133+
=======
134+
-- Note that this works on packages lower in the path as well as at the top level.
135+
-- This is because stanza preferences apply to local packages only
136+
-- and for local packages, a single version is fixed, which means
137+
-- (for now) that all stanza preferences must be uniform at all levels.
138+
-- Further, even when we can have multiple versions of the same package,
139+
-- the build plan will be more efficient if we can attempt to keep
140+
-- stanza preferences aligned at all levels.
141+
preferPackageStanzaPreferences :: (PN -> PackagePreferences) -> EndoTreeTrav d c
142+
preferPackageStanzaPreferences pcs = go
143+
>>>>>>> 67c440a40 (solver: swap catas with recursion, fuse travs (#7519)):cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs
132144
where
133145
go (SChoiceF qsn@(SN (Q pp pn) s) rdm gr _tr ts)
134146
| primaryPP pp && enableStanzaPref pn s =
@@ -221,9 +233,8 @@ processPackageConstraintS qpn s c b' (LabeledPackageConstraint (PackageConstrain
221233
-- by selectively disabling choices that have been ruled out by global user
222234
-- constraints.
223235
enforcePackageConstraints :: M.Map PN [LabeledPackageConstraint]
224-
-> Tree d c
225-
-> Tree d c
226-
enforcePackageConstraints pcs = trav go
236+
-> EndoTreeTrav d c
237+
enforcePackageConstraints pcs = go
227238
where
228239
go (PChoiceF qpn@(Q _ pn) rdm gr ts) =
229240
let c = varToConflictSet (P qpn)
@@ -268,8 +279,8 @@ enforcePackageConstraints pcs = trav go
268279
--
269280
-- This function does not enforce any of the constraints, since that is done by
270281
-- 'enforcePackageConstraints'.
271-
enforceManualFlags :: M.Map PN [LabeledPackageConstraint] -> Tree d c -> Tree d c
272-
enforceManualFlags pcs = trav go
282+
enforceManualFlags :: M.Map PN [LabeledPackageConstraint] -> EndoTreeTrav d c
283+
enforceManualFlags pcs = go
273284
where
274285
go (FChoiceF qfn@(FN (Q _ pn) fn) rdm gr tr Manual d ts) =
275286
FChoiceF qfn rdm gr tr Manual d $
@@ -295,8 +306,8 @@ enforceManualFlags pcs = trav go
295306
go x = x
296307

297308
-- | Require installed packages.
298-
requireInstalled :: (PN -> Bool) -> Tree d c -> Tree d c
299-
requireInstalled p = trav go
309+
requireInstalled :: (PN -> Bool) -> EndoTreeTrav d c
310+
requireInstalled p = go
300311
where
301312
go (PChoiceF v@(Q _ pn) rdm gr cs)
302313
| p pn = PChoiceF v rdm gr (W.mapWithKey installed cs)
@@ -319,8 +330,8 @@ requireInstalled p = trav go
319330
-- they are, perhaps this should just result in trying to reinstall those other
320331
-- packages as well. However, doing this all neatly in one pass would require to
321332
-- change the builder, or at least to change the goal set after building.
322-
avoidReinstalls :: (PN -> Bool) -> Tree d c -> Tree d c
323-
avoidReinstalls p = trav go
333+
avoidReinstalls :: (PN -> Bool) -> EndoTreeTrav d c
334+
avoidReinstalls p = go
324335
where
325336
go (PChoiceF qpn@(Q _ pn) rdm gr cs)
326337
| p pn = PChoiceF qpn rdm gr disableReinstalls
@@ -337,8 +348,8 @@ avoidReinstalls p = trav go
337348
go x = x
338349

339350
-- | Require all packages to be mentioned in a constraint or as a goal.
340-
onlyConstrained :: (PN -> Bool) -> Tree d QGoalReason -> Tree d QGoalReason
341-
onlyConstrained p = trav go
351+
onlyConstrained :: (PN -> Bool) -> EndoTreeTrav d QGoalReason
352+
onlyConstrained p = go
342353
where
343354
go (PChoiceF v@(Q _ pn) _ gr _) | not (p pn)
344355
= FailF
@@ -348,8 +359,8 @@ onlyConstrained p = trav go
348359
= x
349360

350361
-- | Sort all goals using the provided function.
351-
sortGoals :: (Variable QPN -> Variable QPN -> Ordering) -> Tree d c -> Tree d c
352-
sortGoals variableOrder = trav go
362+
sortGoals :: (Variable QPN -> Variable QPN -> Ordering) -> EndoTreeTrav d c
363+
sortGoals variableOrder = go
353364
where
354365
go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.sortByKeys goalOrder xs)
355366
go x = x
@@ -365,8 +376,8 @@ sortGoals variableOrder = trav go
365376
-- | Reduce the branching degree of the search tree by removing all choices
366377
-- after the first successful choice at each level. The returned tree is the
367378
-- minimal subtree containing the path to the first backjump.
368-
pruneAfterFirstSuccess :: Tree d c -> Tree d c
369-
pruneAfterFirstSuccess = trav go
379+
pruneAfterFirstSuccess :: EndoTreeTrav d c
380+
pruneAfterFirstSuccess = go
370381
where
371382
go (PChoiceF qpn rdm gr ts) = PChoiceF qpn rdm gr (W.takeUntil active ts)
372383
go (FChoiceF qfn rdm gr w m d ts) = FChoiceF qfn rdm gr w m d (W.takeUntil active ts)
@@ -379,8 +390,8 @@ pruneAfterFirstSuccess = trav go
379390
-- This is unnecessary for the default search strategy, because
380391
-- it descends only into the first goal choice anyway,
381392
-- but may still make sense to just reduce the tree size a bit.
382-
firstGoal :: Tree d c -> Tree d c
383-
firstGoal = trav go
393+
firstGoal :: EndoTreeTrav d c
394+
firstGoal = go
384395
where
385396
go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.firstOnly xs)
386397
go x = x
@@ -390,8 +401,8 @@ firstGoal = trav go
390401
-- possible by pruning all other goals when base is available. In nearly
391402
-- all cases, there's a single choice for the base package. Also, fixing
392403
-- base early should lead to better error messages.
393-
preferBaseGoalChoice :: Tree d c -> Tree d c
394-
preferBaseGoalChoice = trav go
404+
preferBaseGoalChoice :: EndoTreeTrav d c
405+
preferBaseGoalChoice = go
395406
where
396407
go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.filterIfAnyByKeys isBase xs)
397408
go x = x
@@ -400,10 +411,17 @@ preferBaseGoalChoice = trav go
400411
isBase (Goal (P (Q _pp pn)) _) = unPN pn == "base"
401412
isBase _ = False
402413

414+
<<<<<<< HEAD:cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs
403415
-- | Deal with setup dependencies after regular dependencies, so that we can
404416
-- will link setup dependencies against package dependencies when possible
405417
deferSetupChoices :: Tree d c -> Tree d c
406418
deferSetupChoices = trav go
419+
=======
420+
-- | Deal with setup and build-tool-depends dependencies after regular dependencies,
421+
-- so we will link setup/exe dependencies against package dependencies when possible
422+
deferSetupExeChoices :: EndoTreeTrav d c
423+
deferSetupExeChoices = go
424+
>>>>>>> 67c440a40 (solver: swap catas with recursion, fuse travs (#7519)):cabal-install-solver/src/Distribution/Solver/Modular/Preference.hs
407425
where
408426
go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.preferByKeys noSetup xs)
409427
go x = x
@@ -415,8 +433,8 @@ deferSetupChoices = trav go
415433
-- | Transformation that tries to avoid making weak flag choices early.
416434
-- Weak flags are trivial flags (not influencing dependencies) or such
417435
-- flags that are explicitly declared to be weak in the index.
418-
deferWeakFlagChoices :: Tree d c -> Tree d c
419-
deferWeakFlagChoices = trav go
436+
deferWeakFlagChoices :: EndoTreeTrav d c
437+
deferWeakFlagChoices = go
420438
where
421439
go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.prefer noWeakFlag (P.prefer noWeakStanza xs))
422440
go x = x
@@ -435,8 +453,8 @@ deferWeakFlagChoices = trav go
435453
-- function prunes all other goals. This transformation can help the solver find
436454
-- a solution in fewer steps by allowing it to backtrack sooner when it is
437455
-- exploring a subtree with no solutions. However, each step is more expensive.
438-
preferReallyEasyGoalChoices :: Tree d c -> Tree d c
439-
preferReallyEasyGoalChoices = trav go
456+
preferReallyEasyGoalChoices :: EndoTreeTrav d c
457+
preferReallyEasyGoalChoices = go
440458
where
441459
go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.filterIfAny zeroOrOneChoices xs)
442460
go x = x
@@ -454,15 +472,21 @@ type EnforceSIR = Reader (Map (PI PN) QPN)
454472
-- goal resolving to that instance (there may be other goals _linking_ to that
455473
-- instance however).
456474
enforceSingleInstanceRestriction :: Tree d c -> Tree d c
457-
enforceSingleInstanceRestriction = (`runReader` M.empty) . cata go
475+
enforceSingleInstanceRestriction = (`runReader` M.empty) . go
458476
where
459-
go :: TreeF d c (EnforceSIR (Tree d c)) -> EnforceSIR (Tree d c)
477+
go :: Tree d c -> EnforceSIR (Tree d c)
460478

461479
-- We just verify package choices.
462-
go (PChoiceF qpn rdm gr cs) =
463-
PChoice qpn rdm gr <$> sequenceA (W.mapWithKey (goP qpn) cs)
464-
go _otherwise =
465-
innM _otherwise
480+
go (PChoice qpn rdm gr cs) =
481+
PChoice qpn rdm gr <$> sequenceA (W.mapWithKey (goP qpn) (fmap go cs))
482+
go (FChoice qfn rdm y t m d ts) =
483+
FChoice qfn rdm y t m d <$> traverse go ts
484+
go (SChoice qsn rdm y t ts) =
485+
SChoice qsn rdm y t <$> traverse go ts
486+
go (GoalChoice rdm ts) =
487+
GoalChoice rdm <$> traverse go ts
488+
go x@(Fail _ _) = return x
489+
go x@(Done _ _) = return x
466490

467491
-- The check proper
468492
goP :: QPN -> POption -> EnforceSIR (Tree d c) -> EnforceSIR (Tree d c)

cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,40 +98,50 @@ solve :: SolverConfig -- ^ solver parameters
9898
-> S.Set PN -- ^ global goals
9999
-> RetryLog Message SolverFailure (Assignment, RevDepMap)
100100
solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
101-
explorePhase $
102-
detectCycles $
103-
heuristicsPhase $
104-
preferencesPhase $
105-
validationPhase $
106-
prunePhase $
101+
explorePhase .
102+
traceTree "cycles.json" id .
103+
detectCycles .
104+
traceTree "heuristics.json" id .
105+
trav (
106+
heuristicsPhase .
107+
preferencesPhase .
108+
validationPhase
109+
) .
110+
traceTree "semivalidated.json" id .
111+
validationCata .
112+
traceTree "pruned.json" id .
113+
trav prunePhase .
114+
traceTree "build.json" id $
107115
buildPhase
108116
where
109117
explorePhase = backjumpAndExplore (maxBackjumps sc)
110118
(enableBackjumping sc)
111119
(fineGrainedConflicts sc)
112120
(countConflicts sc)
113121
idx
114-
detectCycles = traceTree "cycles.json" id . detectCyclesPhase
122+
detectCycles = detectCyclesPhase
115123
heuristicsPhase =
116-
let heuristicsTree = traceTree "heuristics.json" id
124+
let
117125
sortGoals = case goalOrder sc of
118126
Nothing -> goalChoiceHeuristics .
127+
<<<<<<< HEAD:cabal-install/cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs
119128
heuristicsTree .
120129
P.deferSetupChoices .
130+
=======
131+
P.deferSetupExeChoices .
132+
>>>>>>> 67c440a40 (solver: swap catas with recursion, fuse travs (#7519)):cabal-install-solver/src/Distribution/Solver/Modular/Solver.hs
121133
P.deferWeakFlagChoices .
122134
P.preferBaseGoalChoice
123135
Just order -> P.firstGoal .
124-
heuristicsTree .
125-
P.sortGoals order
136+
P.sortGoals order
126137
PruneAfterFirstSuccess prune = pruneAfterFirstSuccess sc
127138
in sortGoals .
128139
(if prune then P.pruneAfterFirstSuccess else id)
129140
preferencesPhase = P.preferLinked .
130141
P.preferPackagePreferences userPrefs
131-
validationPhase = traceTree "validated.json" id .
132-
P.enforcePackageConstraints userConstraints .
133-
P.enforceManualFlags userConstraints .
134-
P.enforceSingleInstanceRestriction .
142+
validationPhase = P.enforcePackageConstraints userConstraints .
143+
P.enforceManualFlags userConstraints
144+
validationCata = P.enforceSingleInstanceRestriction .
135145
validateLinking idx .
136146
validateTree cinfo idx pkgConfigDB
137147
prunePhase = (if asBool (avoidReinstalls sc) then P.avoidReinstalls (const True) else id) .
@@ -143,8 +153,7 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
143153
P.onlyConstrained pkgIsExplicit
144154
OnlyConstrainedNone ->
145155
id)
146-
buildPhase = traceTree "build.json" id
147-
$ buildTree idx (independentGoals sc) (S.toList userGoals)
156+
buildPhase = buildTree idx (independentGoals sc) (S.toList userGoals)
148157

149158
allExplicit = M.keysSet userConstraints `S.union` userGoals
150159

0 commit comments

Comments
 (0)