Skip to content

Commit 9cd1d2f

Browse files
Find and save some shrinking code that got lost
1 parent b24b667 commit 9cd1d2f

File tree

1 file changed

+62
-3
lines changed

1 file changed

+62
-3
lines changed

src/Constrained/Generation.hs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,70 @@ shrinkWithSpec (simplifySpec -> spec) a = filter (`conformsToSpec` spec) $ case
155155
ExplainSpec _ s -> shrinkWithSpec s a
156156
-- TODO: filter on can't if we have a known to be sound shrinker
157157
TypeSpec s _ -> shrinkWithTypeSpec s a
158-
-- TODO: The better way of doing this is to compute the dependency graph,
159-
-- shrink one variable at a time, and fixup the rest of the variables
160-
SuspendedSpec {} -> shr a
158+
SuspendedSpec x p -> shrinkFromPreds p x a
161159
MemberSpec {} -> shr a
162160
TrueSpec -> shr a
163161
ErrorSpec {} -> []
164162
where
165163
shr = shrinkWithTypeSpec (emptySpec @a)
166164

165+
shrinkFromPreds :: HasSpec a => Pred -> Var a -> a -> [a]
166+
shrinkFromPreds p
167+
| Result plan <- prepareLinearization p = \x a -> listFromGE $ do
168+
-- NOTE: we do this to e.g. guard against bad construction functions in Exists
169+
xaGood <- checkPred (Env.singleton x a) p
170+
unless xaGood $
171+
fatalError "Trying to shrink a bad value, don't do that!"
172+
-- Get an `env` for the original value
173+
initialEnv <- envFromPred (Env.singleton x a) p
174+
return
175+
[ a'
176+
| -- Shrink the initialEnv
177+
env' <- shrinkEnvFromPlan initialEnv plan
178+
, -- Get the value of the constrained variable `x` in the shrunk env
179+
Just a' <- [Env.lookup env' x]
180+
, -- NOTE: this is necessary because it's possible that changing
181+
-- a particular value in the env during shrinking might not result
182+
-- in the value of `x` changing and there is no better way to know than
183+
-- to do this.
184+
a' /= a
185+
]
186+
| otherwise = error "Bad pred"
187+
188+
-- Start with a valid Env for the plan and try to shrink it
189+
shrinkEnvFromPlan :: Env -> SolverPlan -> [Env]
190+
shrinkEnvFromPlan initialEnv SolverPlan {..} = go mempty solverPlan
191+
where
192+
go :: Env -> [SolverStage] -> [Env]
193+
go _ [] = [] -- In this case we decided to keep every variable the same so nothing to return
194+
go env ((unsafeSubstStage env -> SolverStage {..}) : plan) = do
195+
Just a <- [Env.lookup initialEnv stageVar]
196+
-- Two cases:
197+
-- - either we shrink this value and try to fixup every value later on in the plan or
198+
[ fixedEnv
199+
| a' <- shrinkWithSpec stageSpec a
200+
, let env' = Env.extend stageVar a' env
201+
, Just fixedEnv <- [fixupPlan env' plan]
202+
]
203+
-- - we keep this value the way it is and try to shrink some later value
204+
++ go (Env.extend stageVar a env) plan
205+
206+
-- Fix the rest of the plan given an environment `env` for the plan so far
207+
fixupPlan :: Env -> [SolverStage] -> Maybe Env
208+
fixupPlan env [] = pure env
209+
fixupPlan env ((unsafeSubstStage env -> SolverStage {..}) : plan) =
210+
case Env.lookup initialEnv stageVar >>= fixupWithSpec stageSpec of
211+
Nothing -> Nothing
212+
Just a -> fixupPlan (Env.extend stageVar a env) plan
213+
214+
-- Try to fix a value w.r.t a specification
215+
fixupWithSpec :: forall a. HasSpec a => Specification a -> a -> Maybe a
216+
fixupWithSpec spec a
217+
| a `conformsToSpec` spec = Just a
218+
| otherwise = case spec of
219+
MemberSpec (a' :| _) -> Just a'
220+
_ -> listToMaybe $ filter (`conformsToSpec` spec) (shrinkWithSpec TrueSpec a)
221+
167222
-- Debugging --------------------------------------------------------------
168223

169224
-- | A version of `genFromSpecT` that runs in the IO monad. Good for debugging.
@@ -197,6 +252,10 @@ prettyPlan (simplifySpec -> spec)
197252

198253
-- ---------------------- Building a plan -----------------------------------
199254

255+
unsafeSubstStage :: Env -> SolverStage -> SolverStage
256+
unsafeSubstStage env (SolverStage y ps spec relevant) =
257+
normalizeSolverStage $ SolverStage y (substPred env <$> ps) spec relevant
258+
200259
substStage :: HasSpec a => Set Name -> Var a -> a -> SolverStage -> SolverStage
201260
substStage rel' x val (SolverStage y ps spec relevant) =
202261
normalizeSolverStage $ SolverStage y (substPred env <$> ps) spec relevant'

0 commit comments

Comments
 (0)