Skip to content

Commit c0e4e1b

Browse files
authored
Remove kani::Arbitrary from the modifies contract instrumentation (rust-lang#3169)
This is an additional fix for model-checking/kani#3098. With this fix, Kani should be able to check for contracts using modifies clauses that contain references to types that doesn't implement `kani::Arbitrary`. The verification will still fail if the same contract is used as a verified stub. --------- Signed-off-by: Felipe R. Monteiro <[email protected]>
1 parent 36f715c commit c0e4e1b

File tree

3 files changed

+73
-10
lines changed

3 files changed

+73
-10
lines changed

library/kani_macros/src/sysroot/contracts/check.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use super::{
1313
ContractConditionsData, ContractConditionsHandler,
1414
};
1515

16+
const WRAPPER_ARG_PREFIX: &str = "_wrapper_arg_";
17+
1618
impl<'a> ContractConditionsHandler<'a> {
1719
/// Create the body of a check function.
1820
///
@@ -60,7 +62,11 @@ impl<'a> ContractConditionsHandler<'a> {
6062
let wrapper_args = if let Some(wrapper_call_args) =
6163
inner.iter_mut().find_map(|stmt| try_as_wrapper_call_args(stmt, &wrapper_name))
6264
{
63-
let wrapper_args = make_wrapper_args(wrapper_call_args.len(), attr.len());
65+
let wrapper_args = make_wrapper_idents(
66+
wrapper_call_args.len(),
67+
attr.len(),
68+
WRAPPER_ARG_PREFIX,
69+
);
6470
wrapper_call_args
6571
.extend(wrapper_args.clone().map(|a| Expr::Verbatim(quote!(#a))));
6672
wrapper_args
@@ -124,20 +130,43 @@ impl<'a> ContractConditionsHandler<'a> {
124130

125131
/// Emit a modifies wrapper, possibly augmenting a prior, existing one.
126132
///
127-
/// We only augment if this clause is a `modifies` clause. In that case we
128-
/// expand its signature with one new argument of type `&impl Arbitrary` for
129-
/// each expression in the clause.
133+
/// We only augment if this clause is a `modifies` clause. Before,
134+
/// we annotated the wrapper arguments with `impl kani::Arbitrary`,
135+
/// so Rust would infer the proper types for each argument.
136+
/// We want to remove the restriction that these arguments must
137+
/// implement `kani::Arbitrary` for checking. Now, we annotate each
138+
/// argument with a generic type parameter, so the compiler can
139+
/// continue inferring the correct types.
130140
pub fn emit_augmented_modifies_wrapper(&mut self) {
131141
if let ContractConditionsData::Modifies { attr } = &self.condition_type {
132-
let wrapper_args = make_wrapper_args(self.annotated_fn.sig.inputs.len(), attr.len());
142+
let wrapper_args = make_wrapper_idents(
143+
self.annotated_fn.sig.inputs.len(),
144+
attr.len(),
145+
WRAPPER_ARG_PREFIX,
146+
);
147+
// Generate a unique type parameter identifier
148+
let type_params = make_wrapper_idents(
149+
self.annotated_fn.sig.inputs.len(),
150+
attr.len(),
151+
"WrapperArgType",
152+
);
133153
let sig = &mut self.annotated_fn.sig;
134-
for arg in wrapper_args.clone() {
154+
for (arg, arg_type) in wrapper_args.clone().zip(type_params) {
155+
// Add the type parameter to the function signature's generic parameters list
156+
sig.generics.params.push(syn::GenericParam::Type(syn::TypeParam {
157+
attrs: vec![],
158+
ident: arg_type.clone(),
159+
colon_token: None,
160+
bounds: Default::default(),
161+
eq_token: None,
162+
default: None,
163+
}));
135164
let lifetime = syn::Lifetime { apostrophe: Span::call_site(), ident: arg.clone() };
136165
sig.inputs.push(FnArg::Typed(syn::PatType {
137166
attrs: vec![],
138167
colon_token: Token![:](Span::call_site()),
139168
pat: Box::new(syn::Pat::Verbatim(quote!(#arg))),
140-
ty: Box::new(syn::Type::Verbatim(quote!(&#lifetime impl kani::Arbitrary))),
169+
ty: Box::new(syn::parse_quote! { &#arg_type }),
141170
}));
142171
sig.generics.params.push(syn::GenericParam::Lifetime(syn::LifetimeParam {
143172
lifetime,
@@ -146,6 +175,7 @@ impl<'a> ContractConditionsHandler<'a> {
146175
attrs: vec![],
147176
}));
148177
}
178+
149179
self.output.extend(quote!(#[kanitool::modifies(#(#wrapper_args),*)]))
150180
}
151181
self.emit_common_header();
@@ -191,10 +221,14 @@ fn try_as_wrapper_call_args<'a>(
191221
}
192222
}
193223

194-
/// Make `num` [`Ident`]s with the names `_wrapper_arg_{i}` with `i` starting at `low` and
224+
/// Make `num` [`Ident`]s with the names `prefix{i}` with `i` starting at `low` and
195225
/// increasing by one each time.
196-
fn make_wrapper_args(low: usize, num: usize) -> impl Iterator<Item = syn::Ident> + Clone {
197-
(low..).map(|i| Ident::new(&format!("_wrapper_arg_{i}"), Span::mixed_site())).take(num)
226+
fn make_wrapper_idents(
227+
low: usize,
228+
num: usize,
229+
prefix: &'static str,
230+
) -> impl Iterator<Item = syn::Ident> + Clone + 'static {
231+
(low..).map(move |i| Ident::new(&format!("{prefix}{i}"), Span::mixed_site())).take(num)
198232
}
199233

200234
#[cfg(test)]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VERIFICATION:- SUCCESSFUL
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: -Zfunction-contracts
4+
5+
//! Check that is possible to use `modifies` clause for verification, but not stubbing.
6+
//! Here, we cover the case when the modifies clause contains references to function
7+
//! parameters of generic types. Noticed that here the type T is not annotated with
8+
//! `kani::Arbitrary` since this is no longer a requirement if the contract is only
9+
//! use for verification.
10+
11+
pub mod contracts {
12+
#[kani::modifies(x)]
13+
#[kani::modifies(y)]
14+
pub fn swap<T>(x: &mut T, y: &mut T) {
15+
core::mem::swap(x, y)
16+
}
17+
}
18+
19+
mod verify {
20+
use super::*;
21+
22+
#[kani::proof_for_contract(contracts::swap)]
23+
pub fn check_swap_primitive() {
24+
let mut x: u8 = kani::any();
25+
let mut y: u8 = kani::any();
26+
contracts::swap(&mut x, &mut y)
27+
}
28+
}

0 commit comments

Comments
 (0)