Closed jhjourdan closed 2 months ago
Now that #488 has been merged this seems to have become a soundness issue. However, I'm not sure it would be okay to make logic functions forbidden in ghost code (eg. The desugaring of a for loop currently includes produced = ghost!(produced.push(e))
which calls Ghost::deref
and Seq::push
both of which are logic functions). Currently, there doesn't seem to be much difference between predicates and logic functions (that return bool), so I wonder if it would work to forbid calling predicates in logic functions and ghost code, and restrict ^
/creusot_contracts::stubs::fin
to being used in predicates.
Now that https://github.com/xldenis/creusot/pull/488 has been merged this seems to have become a soundness issue.
Yea, the example given above demonstrates that. Allowing access to prophetic information in ghost code is problematic.
I'm leaving on vacation next week, but when I get back I think the most crucial concern will be overhauling ghost code and placing it on a firm theoretical foundation. There are several other questions that I want to answer simultaneously:
ghost!
macro and Ghost
type (currently you have to annotate all bindings with type information). If you would like to help with this redesign, your input is welcome!
-- Re: predicates vs functions
Currently, there doesn't seem to be much difference between predicates and logic functions (that return bool)
The reason for the existence of predicate
is to target why3's predicate
construct, but I've never gotten a clear answer from Why3 devs about the reason for the need to distinguish the two.
Currently, there doesn't seem to be much difference between predicates and logic functions (that return bool), so I wonder if it would work to forbid calling predicates in logic functions and ghost code, and restrict ^/creusot_contracts::stubs::fin to being used in predicates.
I rather like this solution, I don't think i've ever needed prophetic information in a logical function, though more generally, I think there's a real need to sit down and understand ghost code, it's rules and impact on soundness, even if we ban this specific hole.
From @dewert99
enum Bad<'a> {
None,
Some(Ghost<&'a mut Bad<'a>>)
}
fn test_bad() {
let mut x = Bad::None;
let m = &mut x;
let g = ghost!(m);
*m = Bad::Some(ghost!(*g));
proof_assert!(*m == Bad::Some(g));
proof_assert!(^(*g) == ^m);
let _ = m;
proof_assert!(^(*g) == Bad::Some(g));
}
This should be fixed now, right?
Indeed.
Consider this piece of code:
There are three issues here: 1- I need to put parentheses in
ghost!{ (prophecy(b) + 1) };
, otherwise I get weird type errors. This sounds like an easy to fix bug in theghost
macro.2- The
Ghost
instruct S
is not translated into a ghost field. Similarly, a borrow of a ghost is not translated into anything Ghost, which is blocking since then we cannot make use of it.In my view, the way we should solve this issue is by not using the
ghost
keyword in WhyML, and make sure thatGhost
is indeed a monad that cannot be escaped, and make this check in Creusot.3- Once this code compile correctly, it exhibits a causality loop. Using logic functions such as
prophecy
should be forbidden in ghost code.