Skip to content

Commit 51958f0

Browse files
authored
Enable contracts for const generic functions (#3726)
This PR adds dummy assignments of the input variables to local variables in contracts-replace-closure to avoid may-drop checks in const generic functions. Resolves #3667 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent 41a67c8 commit 51958f0

File tree

4 files changed

+55
-12
lines changed

4 files changed

+55
-12
lines changed

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,12 @@ impl<'a> ContractConditionsHandler<'a> {
8484
let wrapper_arg_ident = Ident::new(WRAPPER_ARG, Span::call_site());
8585
let return_type = return_type_to_type(&self.annotated_fn.sig.output);
8686
let mut_recv = self.has_mutable_receiver().then(|| quote!(core::ptr::addr_of!(self),));
87-
let redefs = self.arg_redefinitions();
88-
let modifies_closure =
89-
self.modifies_closure(&self.annotated_fn.sig.output, &self.annotated_fn.block, redefs);
87+
let redefs_mut_only = self.arg_redefinitions(true);
88+
let modifies_closure = self.modifies_closure(
89+
&self.annotated_fn.sig.output,
90+
&self.annotated_fn.block,
91+
redefs_mut_only,
92+
);
9093
let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site());
9194
parse_quote!(
9295
let #wrapper_arg_ident = (#mut_recv);
@@ -172,17 +175,17 @@ impl<'a> ContractConditionsHandler<'a> {
172175
.unwrap_or(false)
173176
}
174177

175-
/// Generate argument re-definitions for mutable arguments.
178+
/// Generate argument re-definitions for arguments.
176179
///
177-
/// This is used so Kani doesn't think that modifying a local argument value is a side effect.
178-
fn arg_redefinitions(&self) -> TokenStream2 {
180+
/// This is used so Kani doesn't think that modifying a local argument value is a side effect,
181+
/// and avoid may-drop checks in const generic functions.
182+
pub fn arg_redefinitions(&self, redefine_only_mut: bool) -> TokenStream2 {
179183
let mut result = TokenStream2::new();
180184
for (mutability, ident) in self.arg_bindings() {
181185
if mutability == MutBinding::Mut {
182-
result.extend(quote!(let mut #ident = #ident;))
183-
} else {
184-
// This would make some replace some temporary variables from error messages.
185-
//result.extend(quote!(let #ident = #ident; ))
186+
result.extend(quote!(let mut #ident = #ident;));
187+
} else if !redefine_only_mut {
188+
result.extend(quote!(let #ident = #ident;));
186189
}
187190
}
188191
result

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use proc_macro2::{Ident, Span, TokenStream};
77
use quote::quote;
88
use std::mem;
9-
use syn::Stmt;
9+
use syn::{Block, Stmt};
1010

1111
use super::{
1212
ClosureType, ContractConditionsData, ContractConditionsHandler, INTERNAL_RESULT_IDENT,
@@ -19,7 +19,18 @@ impl<'a> ContractConditionsHandler<'a> {
1919
fn initial_replace_stmts(&self) -> Vec<syn::Stmt> {
2020
let return_type = return_type_to_type(&self.annotated_fn.sig.output);
2121
let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site());
22-
vec![syn::parse_quote!(let #result : #return_type = kani::any_modifies();)]
22+
// Add dummy assignments of the input variables to local variables
23+
// to avoid may drop checks in const generic functions.
24+
// https://github.com/model-checking/kani/issues/3667
25+
let redefs = self.arg_redefinitions(false);
26+
let redefs_block: Block = syn::parse_quote!({#redefs});
27+
vec![
28+
vec![syn::parse_quote!(
29+
let #result : #return_type = kani::any_modifies();
30+
)],
31+
redefs_block.stmts,
32+
]
33+
.concat()
2334
}
2435

2536
/// Split an existing replace body of the form
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
** 1 of
2+
Failed Checks: Check that *dst is assignable
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: -Zfunction-contracts
4+
5+
//! Check that Kani contract can be applied to a constant generic function.
6+
//! <https://github.com/model-checking/kani/issues/3667>
7+
8+
struct Foo<T> {
9+
ptr: *const T,
10+
}
11+
12+
impl<T: Sized> Foo<T> {
13+
#[kani::requires(true)]
14+
pub const unsafe fn bar(self, v: T)
15+
where
16+
T: Sized,
17+
{
18+
unsafe { core::ptr::write(self.ptr as *mut T, v) };
19+
}
20+
}
21+
22+
#[kani::proof_for_contract(Foo::bar)]
23+
fn check_const_generic_function() {
24+
let x: u8 = kani::any();
25+
let foo: Foo<u8> = Foo { ptr: &x };
26+
unsafe { foo.bar(kani::any::<u8>()) };
27+
}

0 commit comments

Comments
 (0)