@@ -357,8 +357,25 @@ def _cache(self, data: Union[ConjectureData, ConjectureResult]) -> None:
357
357
# write to the buffer cache here as we move more things to the ir cache.
358
358
if data .invalid_at is None :
359
359
self .__data_cache [data .buffer ] = result
360
- key = self ._cache_key_ir (data = data )
361
- self .__data_cache_ir [key ] = result
360
+
361
+ # interesting buffer-based data can mislead the shrinker if we cache them.
362
+ #
363
+ # @given(st.integers())
364
+ # def f(n):
365
+ # assert n < 100
366
+ #
367
+ # may generate two counterexamples, n=101 and n=m > 101, in that order,
368
+ # where the buffer corresponding to n is large due to eg failed probes.
369
+ # We shrink m and eventually try n=101, but it is cached to a large buffer
370
+ # and so the best we can do is n=102, a non-ideal shrink.
371
+ #
372
+ # We can cache ir-based buffers fine, which always correspond to the
373
+ # smallest buffer via forced=. The overhead here is small because almost
374
+ # all interesting data are ir-based via the shrinker (and that overhead
375
+ # will tend towards zero as we move generation to the ir).
376
+ if data .ir_tree_nodes is not None or data .status < Status .INTERESTING :
377
+ key = self ._cache_key_ir (data = data )
378
+ self .__data_cache_ir [key ] = result
362
379
363
380
def cached_test_function_ir (
364
381
self , nodes : List [IRNode ]
@@ -1218,7 +1235,7 @@ def shrink_interesting_examples(self) -> None:
1218
1235
self .interesting_examples .values (), key = lambda d : sort_key (d .buffer )
1219
1236
):
1220
1237
assert prev_data .status == Status .INTERESTING
1221
- data = self .new_conjecture_data_for_buffer (prev_data .buffer )
1238
+ data = self .new_conjecture_data_ir (prev_data .examples . ir_tree_nodes )
1222
1239
self .test_function (data )
1223
1240
if data .status != Status .INTERESTING :
1224
1241
self .exit_with (ExitReason .flaky )
0 commit comments