-
First of all, this is an awesome tool. It's been a bit tricky to get started, especially since I'm also new to Rust as a whole, but I'm interested in the space and you've shown this has a lot of potential with the verified SAT solver, so I'm determined to muddle through it. I wanted to try to implement the abs function generically over signed integers as an exercise. I'm now realizing this was maybe not the most approachable exercise, since to do generics in Rust you have to deal with traits, and to use them with Creusot they all have to have an extern_spec. But I eventually came up with something that works below. I have some lingering questions though:
use creusot_contracts::*;
use creusot_contracts::std::ops::Neg;
use num_traits::{identities::Zero, sign::Signed, int::PrimInt};
pub trait NegCheckable: Neg<Output = Self> {
#[logic]
pub fn is_negatable(&self) -> bool;
}
macro_rules! impl_negcheckable_signed {
($($t:ty),*) => {
$(
impl NegCheckable for $t {
#[logic]
fn is_negatable(&self) -> bool { *self > <$t>::MIN }
}
)*
};
}
impl_negcheckable_signed!(i8, i16, i32, i64, i128, isize);
extern_spec! {
mod std {
mod ops {
trait Neg
where
Self: DeepModel<DeepModelTy = Int> + Signed + NegCheckable,
<Self as Neg>::Output: DeepModel<DeepModelTy = Int>
{
#[requires(self.is_negatable())]
#[ensures(result.deep_model() == -self.deep_model())]
fn neg(self) -> <Self as Neg>::Output;
}
}
}
mod num_traits {
mod identities {
trait Zero
where
Self: DeepModel<DeepModelTy = Int>
{
#[ensures(result.deep_model() == 0)]
fn zero() -> Self;
}
}
}
}
#[requires(x.is_negatable())]
#[ensures(result.deep_model() >= 0)]
#[ensures(result.deep_model() == x.deep_model() || result.deep_model() == -x.deep_model())]
fn abs<T>(x: T) -> T
where
T: NegCheckable + PrimInt + Signed + Zero + DeepModel<DeepModelTy = Int>
{
if x < T::zero() {
-x
} else {
x
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 18 replies
-
Hi @ethanbb ! I'm glad you like our work! Don"t hesitate to ask more questions, we will be happy to answer! Indeed, you did not start with the simplest exercise! But since you tried, I will try to answer your questions.
On the other hand, In many simple types, these two notions coincide. For example, the deep models and views of any integer type coincide. But it differs for complex types: the view of a Your case is a bit peculiar, because, since you are using integers, deep model and view coincide, but Creusot does not know that, because it only sees a generic type variable, for which they don't necessarily coincide. Since you are using
Indeed, we removed it recently (in #1594), because we kept forgetting to use it. Instead, a function returning
That is indeed a valid way of doing this, if you really want to have a generic version of
Yes and no. It is not possible to turn a program function into a logic function (yet). One issue is with termination: you need to make sure that the program function always terminates, for example. Something like this could be possible for That being said, you can always refer to the pre- and post-condition of any function. So, in your case, a possibility is to specify The advantage of this technique is that you don't need to add a generic (Note that it will not work out of the box, because, as you noticed, You may answer that it's not quite satisfactory, because the specification of |
Beta Was this translation helpful? Give feedback.
Hi @ethanbb !
I'm glad you like our work! Don"t hesitate to ask more questions, we will be happy to answer! Indeed, you did not start with the simplest exercise! But since you tried, I will try to answer your questions.
View
is just the trait used behind the@
notation. Nothing more, nothing less. It is useful, because we often want to speak about "the logical value of" a program type, and it is convenient to have a short notation for that. But it has no "generic" meaning whatsoever, it is…