Print SMT array counterexamples more intelligibly
RyanGlScott opened this issue · comments
Consider this erroneous SAW proof, which makes use of a predicate with an SMT array argument:
// test.saw
import "Array.cry";
prove_print
(w4_unint_z3 [])
{{ \(arr : Array Integer Integer) ->
arrayLookup (arrayUpdate arr 0 27) 0 == 42 }};
SAW is able to realize that this proof is invalid. However, the way it prints the counterexample leaves a lot to be desired:
$ ~/Software/saw-1.1/bin/saw test.saw
[11:58:13.968] Loading file "/home/ryanscott/Documents/Hacking/SAW/test.saw"
[11:58:14.064] Stack trace:
"prove_print" (/home/ryanscott/Documents/Hacking/SAW/test.saw:4:1-4:12)
prove: 1 unsolved subgoal(s)
Invalid: [arr = FOTArray FOTInt FOTInt]
arr = FOTArray FOTInt FOTInt
doesn't really say anything useful, as it just repeats the type of arr
(Array Integer Integer
) in terms of SAWCore's internals. Ideally, the counterexample would say something like "an array where every element is 0". In fact, if you retrieve a counter-example using SMT-LIB:
; test.smt2
(declare-const arr (Array Int Int))
(assert (not (= (select (store arr 0 27) 0) 42)))
(check-sat)
(get-model)
Then Z3 is able to produce this in a neat, compact way:
$ z3 test.smt2
sat
(
(define-fun arr () (Array Int Int)
((as const (Array Int Int)) 0))
)
We should strive to make SAW's counterexample output as neat as this. This issue tracks that task.
To do this, we will need to refactor some of saw-core
's internals a bit. The immediate reason why SAW is unable to produce a meaningful counterexample for SMT array values is because saw-core
simply doesn't store them:
saw-script/saw-core/src/Verifier/SAW/FiniteValue.hs
Lines 75 to 83 in 48e0e7d
FOVArray
is the odd one out in the definition of FirstOrderValue
, as it is the only constructor that doesn't store any meaningful value information. We would need to change the definition of FOVArray
to do so. But where do we get this value information from? FirstOrderValue
s are created here:
saw-script/saw-core-what4/src/Verifier/SAW/Simulator/What4/FirstOrder.hs
Lines 106 to 119 in 48e0e7d
This means that FirstOrderValue
s are ultimately derived from what4
's notion of GroundValue
s. In particular, a ground SMT array is a GroundArray
. We would need to figure out how to sensibly convert a GroundArray
to a FirstOrderValue
representation.
In the case of ArrayConcrete
, this is fairly straightforward to print, as each ArrayConcrete
value is a constant array value plus a finite number of array updates. ArrayMapping
is harder, as an ArrayMapping
contains an arbitrary function. We needn't let perfect be the enemy of good, however, as I suspect that the ArrayConcrete
case will be far more common in practice. Perhaps we can just print ArrayMapping
as <array-fun>
, and if users want to figure out what the array contains at various indices, they can retrieve the array using caseProofResult
or caseSatResult
.