Closed RyanGlScott closed 1 day ago
Some investigation reveals that the culprit is likely the fact that you cannot check Const_RefRoots
for equality, nor can you check if they overlap. In another, more complicated test case (see https://github.com/GaloisInc/formal-verso/issues/4), we've managed to observe the Cannot compare Const_RefRoots
error message that's used in these pieces of code. Note that we are using const string slices, which is one of the ways that a Const_RefRoot
-based reference can arise in crucible-mir
.
This limitation is unfortunate, and I'm unclear how to fix this issue without at least partially addressing this limitation. I decided to ask @spernsteiner what he thought:
Patches.md
has some relevant notes:... we don't have a good way to decide whether two
Const_RefRoot
s are "the same object" or not. (We could assign them aNonce
for identity, but since the point ofConst_RefRoot
is to support muxing, we'd have to mux the nonces, which makes things much more complicated. And we can't simply declare that allConst_RefRoot
s containing the same value are the same object, because we don't have a generic equality test that covers all crucible types.)It looks like
Const_RefRoot
is mostly produced bytransConstVal
. I wonder if we could get rid ofConst_RefRoot
entirely by bringing our const/static handling more in line with rustc. Specifically, now that rustc tracks explicit static allocations even for things like string literals, we could consider creating a Crucible global variable for each allocation and havingtransConstVal
produceGlobalVar_RefRoot
pointing to those allocations.There's already a
ConstStaticRef
case intransConstVal
, which would probably cover most cases. (It doesn't handle pointers into the interior of an allocation, but it looks like the othertransConstVal
cases don't currently handle that either.) The main change would probably be ensuring that mir-json emits all the right static allocations and usesConstStaticRef
instead ofConstSlice
etc
Here is a rough outline for how we might go about fixing this:
The first step would be to change mir-json
to emit code for constant slices in a way that is closer to how we emit code for ConstStaticRef
s. But before we do that: how does one know what the relevant mir-json
code path for ConstStaticRef
is in the first place?
The way that I do this is by matching up the code in crucible-mir:Mir.JSON
with the correspond JSON that mir-json
emits. For instance, this is the crucible-mir
code that parses ConstStaticRef
s:
Just (String "static_ref") -> ConstStaticRef <$> v .: "def_id"
Note that "static_ref"
part. Here is the corresponding code in mir-json
:
return Some(json!({
"kind": "static_ref",
"def_id": aid,
}));
That is, you have to find the JSON with the corresponding "kind"
. A similar process can be used to find the mir-json
code paths for ConstStr
(for constant string slices) and ConstSlice
(for other forms of constant slices).
mir-json
code paths for constant slices. You can find them here and here. I suspect that we will want to change these so that we create top-level allocations for each constant slice, just like we do in the "static_ref"
case here. Once we do that, we should be able to delete the "val"
part of ConstStr
's JSON and delete the "element_ty"
/"element"
parts of ConstSlice
's JSON and instead replace them with a "def_id"
, just like is done here.ConstStr
and ConstSlice
in crucible-mir:Mir.JSON
. Take inspiration from the way that ConstStaticRef
is parsed.ConstStr
and ConstSlice
to make them work more like how ConstStaticRef
is handled. This includes pretty-printing code and, more importantly, the code which translates these constructs into Crucible.Ideally those str/bstr/slice special cases could all be deleted and all const allocs would be treated uniformly. Two things that might be blocking this:
mir-json
might not support fat pointer constants. From looking briefly at try_render_ref_opty
, my guess is if you delete the special cases, it will output the pointer correctly but omit the metadata (slice/str length). The bstr case should just work, though (the length is statically known).ConstVal
s.I think ConstStr
, ConstSlice
, and ConstByteStr
should ideally all be deleted, with the first two combined into a ConstPtrWithLen
that takes a const pointer (usually ConstStaticRef
, but it might sometimes be ConstRawPtr
for empty/zst slices) and a usize
length and combines them into the appropriate slice repr. Then mir-json
can have a single case in try_render_ref_opty
for "has length metadata" instead of separate slice and str cases (the bstr case I suspect can just be removed). This could potentially be expanded to cover custom DSTs (e.g. &RefCell<[T]>
) and/or vtable metadata (&dyn Trait
) in the future.
What I've done is:
I have kept ConstStrRef and ConstSliceRef separate because conflating string slices and ordinary array slices didn't seem like a good idea. They can definitely be folded together if you think that's safe/advisable/adequately future-proof.
There are still four other references to constMirRef so we can't get rid of Const_RefRoots entirely yet.
Consider this program:
I would expect SAW to be able to verify
f
andg
againstf_spec
andg_spec
below:Surprisingly, however, SAW fails when using the
f_ov
override in a compositional proof ofg_spec
:This is very strange. What's more,
crux-mir
is able to simulate the same program (usingg
as a#[crux::test]
entrypoint), which suggests that the fault lies with SAW. Moreover, SAW is able to handle the following, simpler program without issues:This suggests that the problem only arises when an override has multiple references as arguments. This led me to discover that if you comment out this line of code:
https://github.com/GaloisInc/saw-script/blob/45d64dd7e56c2879cd0a684be321d579899e3404/src/SAWScript/Crucible/MIR/Override.hs#L947
Then SAW will accept the specs in
test.saw
that it previously rejected. I'm not exactly sure what is going on, but this suggests that something is rather broken in the way that disjointness checking is currently implemented.