Skip to content

Commit 1ebba9e

Browse files
committed
Addressing comments
Added `requires` test case Made tests `expected` tests Changes `requires_ensures` macro to function
1 parent cc0e7d7 commit 1ebba9e

19 files changed

Lines changed: 157 additions & 71 deletions

cprover_bindings/src/goto_program/expr.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,16 @@ pub enum ExprValue {
170170
},
171171
}
172172

173-
/// The equivalent of a "mathematical function" in CBMC but spiritually it is more like a function object.
173+
/// The equivalent of a "mathematical function" in CBMC. Semantically this is an
174+
/// anonymous function object, similar to a closure, but without closing over an
175+
/// environment.
174176
///
175-
/// This is only used to implement function contracts and values of this sort are otherwise not constructible.
177+
/// This is only valid for use as a function contract. It may not perform side
178+
/// effects, a property that is enforced on the CBMC side.
179+
///
180+
/// The precise nomenclature is that in CBMC a contract value has *type*
181+
/// `mathematical_function` and values of that type are `lambda`s. Since this
182+
/// struct represents such values it is named `Lambda`.
176183
#[derive(Debug, Clone)]
177184
pub struct Lambda {
178185
pub arguments: Vec<(InternedString, Type)>,

cprover_bindings/src/goto_program/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ pub use expr::{
2323
};
2424
pub use location::Location;
2525
pub use stmt::{Stmt, StmtBody, SwitchCase};
26-
pub use symbol::{Contract, Symbol, SymbolValues};
26+
pub use symbol::{FunctionContract, Symbol, SymbolValues};
2727
pub use symbol_table::SymbolTable;
2828
pub use typ::{CIntType, DatatypeComponent, Parameter, Type};

cprover_bindings/src/goto_program/symbol.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub struct Symbol {
1414
pub typ: Type,
1515
pub value: SymbolValues,
1616
/// Contracts to be enforced (only supported for functions)
17-
pub contract: Option<Box<Contract>>,
17+
pub contract: Option<Box<FunctionContract>>,
1818

1919
/// Optional debugging information
2020
@@ -50,13 +50,13 @@ pub struct Symbol {
5050
/// See https://diffblue.github.io/cbmc/contracts-user.html for the meaning of
5151
/// each type of clause.
5252
#[derive(Clone, Debug)]
53-
pub struct Contract {
53+
pub struct FunctionContract {
5454
pub(crate) requires: Vec<Lambda>,
5555
pub(crate) ensures: Vec<Lambda>,
5656
pub(crate) assigns: Vec<Lambda>,
5757
}
5858

59-
impl Contract {
59+
impl FunctionContract {
6060
pub fn new(requires: Vec<Lambda>, ensures: Vec<Lambda>, assigns: Vec<Lambda>) -> Self {
6161
Self { requires, ensures, assigns }
6262
}
@@ -128,7 +128,7 @@ impl Symbol {
128128

129129
/// Add this contract to the symbol (symbol must be a function) or fold the
130130
/// conditions into an existing contract.
131-
pub fn attach_contract(&mut self, contract: Contract) {
131+
pub fn attach_contract(&mut self, contract: FunctionContract) {
132132
assert!(self.typ.is_code());
133133
match self.contract {
134134
Some(ref mut prior) => {

cprover_bindings/src/goto_program/symbol_table.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
use super::super::{env, MachineModel};
4-
use super::{BuiltinFn, Contract, Stmt, Symbol};
4+
use super::{BuiltinFn, FunctionContract, Stmt, Symbol};
55
use crate::InternedString;
66
use std::collections::BTreeMap;
77
/// This is a typesafe implementation of the CBMC symbol table, based on the CBMC code at:
@@ -80,7 +80,11 @@ impl SymbolTable {
8080
self.symbol_table.get_mut(&name).unwrap().update_fn_declaration_with_definition(body);
8181
}
8282

83-
pub fn attach_contract<T: Into<InternedString>>(&mut self, name: T, contract: Contract) {
83+
pub fn attach_contract<T: Into<InternedString>>(
84+
&mut self,
85+
name: T,
86+
contract: FunctionContract,
87+
) {
8488
let sym = self.symbol_table.get_mut(&name.into()).unwrap();
8589
sym.attach_contract(contract);
8690
}

kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use crate::codegen_cprover_gotoc::GotocCtx;
77
use crate::kani_middle::contracts::GFnContract;
8-
use cbmc::goto_program::{Contract, Expr, Lambda, Stmt, Symbol, Type};
8+
use cbmc::goto_program::{Expr, FunctionContract, Lambda, Stmt, Symbol, Type};
99
use cbmc::InternString;
1010
use rustc_middle::mir::traversal::reverse_postorder;
1111
use rustc_middle::mir::{Body, HasLocalDecls, Local};
@@ -247,7 +247,7 @@ impl<'tcx> GotocCtx<'tcx> {
247247
/// lambda takes the return value as first argument and then immediately
248248
/// calls the generated spec function, but passing the return value as the
249249
/// last argument.
250-
fn as_goto_contract(&mut self, fn_contract: &GFnContract<Instance<'tcx>>) -> Contract {
250+
fn as_goto_contract(&mut self, fn_contract: &GFnContract<Instance<'tcx>>) -> FunctionContract {
251251
use rustc_middle::mir;
252252
let mut handle_contract_expr = |instance| {
253253
let mir = self.current_fn().mir();
@@ -286,7 +286,7 @@ impl<'tcx> GotocCtx<'tcx> {
286286
fn_contract.requires().iter().copied().map(&mut handle_contract_expr).collect();
287287
let ensures =
288288
fn_contract.ensures().iter().copied().map(&mut handle_contract_expr).collect();
289-
Contract::new(requires, ensures, vec![])
289+
FunctionContract::new(requires, ensures, vec![])
290290
}
291291

292292
/// Convert the contract to a CBMC contract, then attach it to `instance`.

kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ impl GotocCodegenBackend {
123123
}
124124
}
125125

126-
// Gets its own loop, because the functions used in the contract
127-
// expressions must have been declared before
126+
// Attaching the contrcts gets its own loop, because the
127+
// functions used in the contract expressions must have been
128+
// declared before
128129
for (item, contract) in &items_with_contracts {
129130
if let Some(contract) = contract {
130131
let instance = match item {

library/kani_macros/src/lib.rs

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -237,14 +237,14 @@ mod sysroot {
237237
/// Rewrites function contract annotations (`requires`, `ensures`) by lifing
238238
/// the condition into a separate function. As an example: (using `ensures`)
239239
///
240-
/// ```rs
240+
/// ```ignore
241241
/// #[kani::ensures(x < result)]
242242
/// fn foo(x: u32) -> u32 { .. }
243243
/// ```
244244
///
245245
/// Is rewritten to
246246
///
247-
/// ```rs
247+
/// ```ignore
248248
/// fn foo_ensures_<hash of foo>(x: u32, result: u32) {
249249
/// x < result
250250
/// }
@@ -258,66 +258,68 @@ mod sysroot {
258258
///
259259
/// This macro is supposed to be called with the name of the procedural
260260
/// macro it should generate, e.g. `requires_ensures(requires)`
261-
macro_rules! requires_ensures {
262-
($name: ident) => {
263-
pub fn $name(attr: TokenStream, item: TokenStream) -> TokenStream {
264-
use syn::{FnArg, PatType, PatIdent, Pat, Signature, Token, ReturnType, TypeTuple, punctuated::Punctuated, Type};
265-
use proc_macro2::Span;
266-
let attr = proc_macro2::TokenStream::from(attr);
261+
fn handle_requires_ensures(name: &str, attr: TokenStream, item: TokenStream) -> TokenStream {
262+
use proc_macro2::Span;
263+
use syn::{
264+
punctuated::Punctuated, FnArg, Pat, PatIdent, PatType, ReturnType, Signature, Token,
265+
Type, TypeTuple,
266+
};
267+
let attr = proc_macro2::TokenStream::from(attr);
267268

268-
let a_short_hash = short_hash_of_token_stream(&item);
269+
let a_short_hash = short_hash_of_token_stream(&item);
269270

270-
let item_fn @ ItemFn { sig, .. } = &parse_macro_input!(item as ItemFn);
271-
let Signature { inputs, output , .. } = sig;
271+
let item_fn @ ItemFn { sig, .. } = &parse_macro_input!(item as ItemFn);
272+
let Signature { inputs, output, .. } = sig;
272273

273-
let gen_fn_name = identifier_for_generated_function(item_fn, stringify!($name), a_short_hash);
274-
let attribute = format_ident!("{}", stringify!($name));
275-
let kani_attributes = quote!(
276-
#[allow(dead_code)]
277-
#[kanitool::#attribute = stringify!(#gen_fn_name)]
278-
);
279-
280-
let typ = match output {
281-
ReturnType::Type(_, t) => t.clone(),
282-
_ => Box::new(TypeTuple { paren_token: Default::default(), elems: Punctuated::new() }.into()),
283-
};
284-
285-
let mut gen_fn_inputs = inputs.clone();
286-
gen_fn_inputs.push(
287-
FnArg::Typed(PatType {
288-
attrs: vec![],
289-
pat: Box::new(Pat::Ident(PatIdent{
290-
attrs: vec![],
291-
by_ref: None,
292-
mutability: None,
293-
ident: Ident::new("result", Span::call_site()),
294-
subpat: None,
295-
})),
296-
colon_token: Token![:](Span::call_site()),
297-
ty: typ,
298-
})
299-
);
300-
301-
assert!(sig.variadic.is_none(), "Varaidic signatures are not supported");
302-
303-
let mut gen_sig = sig.clone();
304-
gen_sig.inputs = gen_fn_inputs;
305-
gen_sig.output = ReturnType::Type(Default::default(), Box::new(Type::Verbatim(quote!(bool))));
306-
gen_sig.ident = gen_fn_name;
274+
let gen_fn_name = identifier_for_generated_function(item_fn, name, a_short_hash);
275+
let attribute = format_ident!("{name}");
276+
let kani_attributes = quote!(
277+
#[allow(dead_code)]
278+
#[kanitool::#attribute = stringify!(#gen_fn_name)]
279+
);
307280

308-
quote!(
309-
#gen_sig {
310-
#attr
311-
}
312-
313-
#kani_attributes
314-
#item_fn
315-
)
316-
.into()
281+
let typ = match output {
282+
ReturnType::Type(_, t) => t.clone(),
283+
_ => Box::new(
284+
TypeTuple { paren_token: Default::default(), elems: Punctuated::new() }.into(),
285+
),
286+
};
287+
288+
let mut gen_fn_inputs = inputs.clone();
289+
gen_fn_inputs.push(FnArg::Typed(PatType {
290+
attrs: vec![],
291+
pat: Box::new(Pat::Ident(PatIdent {
292+
attrs: vec![],
293+
by_ref: None,
294+
mutability: None,
295+
ident: Ident::new("result", Span::call_site()),
296+
subpat: None,
297+
})),
298+
colon_token: Token![:](Span::call_site()),
299+
ty: typ,
300+
}));
301+
302+
assert!(sig.variadic.is_none(), "Variadic signatures are not supported");
303+
304+
let mut gen_sig = sig.clone();
305+
gen_sig.inputs = gen_fn_inputs;
306+
gen_sig.output =
307+
ReturnType::Type(Default::default(), Box::new(Type::Verbatim(quote!(bool))));
308+
gen_sig.ident = gen_fn_name;
309+
310+
quote!(
311+
#gen_sig {
312+
#attr
317313
}
318-
}
314+
315+
#kani_attributes
316+
#item_fn
317+
)
318+
.into()
319319
}
320320

321+
/// Hash this `TokenStream` and return an integer that is at most digits
322+
/// long when hex formatted.
321323
fn short_hash_of_token_stream(stream: &proc_macro::TokenStream) -> u64 {
322324
use std::hash::Hasher;
323325
let mut hasher = std::collections::hash_map::DefaultHasher::default();
@@ -326,6 +328,9 @@ mod sysroot {
326328
long_hash % 0x1_000_000 // six hex digits
327329
}
328330

331+
/// Makes consistent names for a generated function which was created for
332+
/// `purpose`, from an attribute that decorates `related_function` with the
333+
/// hash `hash`.
329334
fn identifier_for_generated_function(
330335
related_function: &ItemFn,
331336
purpose: &str,
@@ -335,8 +340,13 @@ mod sysroot {
335340
Ident::new(&identifier, proc_macro2::Span::mixed_site())
336341
}
337342

338-
requires_ensures!(requires);
339-
requires_ensures!(ensures);
343+
pub fn requires(attr: TokenStream, item: TokenStream) -> TokenStream {
344+
handle_requires_ensures("requires", attr, item)
345+
}
346+
347+
pub fn ensures(attr: TokenStream, item: TokenStream) -> TokenStream {
348+
handle_requires_ensures("ensures", attr, item)
349+
}
340350

341351
kani_attribute!(should_panic, no_args);
342352
kani_attribute!(solver);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
RESULTS:
2+
Check 1: arbitrary_ensures_fail::max.missing_definition.1
3+
- Status: FAILURE
4+
- Description: "Function `arbitrary_ensures_fail::max` with missing definition is unreachable"
5+
- Location: Unknown file in function arbitrary_ensures_fail::max

tests/kani/FunctionContract/arbitrary_ensures_fail.rs renamed to tests/expected/function-contract/arbitrary_ensures_fail.rs

File renamed without changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
RESULTS:
2+
Check 1: arbitrary_ensures_pass::max.missing_definition.1
3+
- Status: SUCCESS
4+
- Description: "Function `arbitrary_ensures_pass::max` with missing definition is unreachable"
5+
- Location: Unknown file in function arbitrary_ensures_pass::max

0 commit comments

Comments
 (0)