-
Notifications
You must be signed in to change notification settings - Fork 121
Add support for function contracts in cprover_bindings. #1302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5cea135
899b300
f424956
0f0212e
d3ebe3b
1eeba2b
7a4a96d
6d04ada
60eb6b8
363ada5
ecbf2cb
e61041a
94cc0d5
d00853a
12f381a
d732dd4
aeb5550
42e9f8e
a705a20
326c825
0ea305f
ba1e7d3
b5e956f
2daa5d7
659df61
8ba7653
1759834
1e484d9
c25fbd5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright Kani Contributors | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
||
use super::{Expr, Location}; | ||
use std::fmt::Debug; | ||
|
||
/// A `Contract` represents a code contract type. | ||
/// A contract describes specifications (in the form of preconditions, postconditions, and invariants) of certain expressions. | ||
/// Further details about the CBMC implementation can be found here - | ||
/// https://github.com/diffblue/cbmc/blob/develop/doc/cprover-manual/contracts.md | ||
|
||
/// Represents a contract on a function, loop, etc. | ||
#[derive(Clone, Debug)] | ||
pub enum Contract { | ||
FunctionContract { ensures: Vec<Spec>, requires: Vec<Spec> }, | ||
} | ||
|
||
/// A `Spec` is a struct for representing the `requires`, `ensures`, and `assigns` clauses in a function contract. | ||
/// Every expression inside a function contract clause is wrapped into a lambda expression on the CBMC side. | ||
/// This is because CBMC generates a new symbol for each contract and the symbol needs to be self-contained. | ||
/// That is, variables that may have only existed in the scope of a function declaration are | ||
/// treated as binding variables in the lambda expression and hence made available to the contract symbol. | ||
/// A list of fresh symbols (one for each binding variable in the lambda expression) is stored in `temporary_symbols`. | ||
/// The binding variables include the return value of the function (may be empty) and the list of function arguments. | ||
#[derive(Clone, Debug)] | ||
pub struct Spec { | ||
temporary_symbols: Vec<Expr>, | ||
clause: Expr, | ||
location: Location, | ||
} | ||
|
||
/// Getters | ||
impl Spec { | ||
pub fn temporary_symbols(&self) -> &Vec<Expr> { | ||
&self.temporary_symbols | ||
} | ||
|
||
pub fn clause(&self) -> &Expr { | ||
&self.clause | ||
} | ||
|
||
pub fn location(&self) -> &Location { | ||
&self.location | ||
} | ||
} | ||
|
||
/// Setters | ||
impl Spec { | ||
pub fn with_location(mut self, loc: Location) -> Self { | ||
self.location = loc; | ||
self | ||
} | ||
} | ||
|
||
/// Constructor | ||
impl Spec { | ||
pub fn new(temporary_symbols: Vec<Expr>, clause: Expr, location: Location) -> Self { | ||
assert!(temporary_symbols.iter().all(|x| x.is_symbol()), "Variables must be symbols"); | ||
Spec { temporary_symbols, clause, location } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
// Copyright Kani Contributors | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
use super::super::utils::aggr_tag; | ||
use super::{DatatypeComponent, Expr, Location, Parameter, Stmt, Type}; | ||
use super::{Contract, DatatypeComponent, Expr, Location, Parameter, Stmt, Type}; | ||
use crate::{InternStringOption, InternedString}; | ||
|
||
/// Based off the CBMC symbol implementation here: | ||
|
@@ -56,6 +56,7 @@ pub enum SymbolModes { | |
pub enum SymbolValues { | ||
Expr(Expr), | ||
Stmt(Stmt), | ||
Contract(Contract), | ||
None, | ||
} | ||
/// Constructors | ||
|
@@ -128,6 +129,29 @@ impl Symbol { | |
) | ||
} | ||
|
||
pub fn contract<T: Into<InternedString>, U: Into<InternedString>>( | ||
name: T, | ||
base_name: U, | ||
typ: Type, | ||
contract: Contract, | ||
loc: Location, | ||
) -> Symbol { | ||
let name = name.into(); | ||
// Both base name and pretty name contain the name of the function that the contract is written for. | ||
let base_name: InternedString = base_name.into(); | ||
let pretty_name = base_name; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be the pretty name of the function? (We might have discussed this already) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the CBMC side, the |
||
Symbol::new( | ||
name, | ||
loc, | ||
typ, | ||
SymbolValues::Contract(contract), | ||
Some(base_name), | ||
Some(pretty_name), | ||
) | ||
// CBMC distinguishes between contract symbols and object or function symbols using this flag. | ||
.with_is_property(true) | ||
} | ||
|
||
pub fn constant( | ||
name: &str, | ||
pretty_name: &str, | ||
|
@@ -283,6 +307,11 @@ impl Symbol { | |
self | ||
} | ||
|
||
pub fn with_is_property(mut self, v: bool) -> Symbol { | ||
self.is_property = v; | ||
self | ||
} | ||
|
||
pub fn with_is_thread_local(mut self, v: bool) -> Symbol { | ||
self.is_thread_local = v; | ||
self | ||
|
@@ -341,24 +370,31 @@ impl Symbol { | |
} | ||
|
||
impl SymbolValues { | ||
pub fn is_contract(&self) -> bool { | ||
match self { | ||
SymbolValues::Contract(_) => true, | ||
SymbolValues::None | SymbolValues::Expr(_) | SymbolValues::Stmt(_) => false, | ||
} | ||
} | ||
|
||
pub fn is_expr(&self) -> bool { | ||
match self { | ||
SymbolValues::Expr(_) => true, | ||
SymbolValues::None | SymbolValues::Stmt(_) => false, | ||
SymbolValues::None | SymbolValues::Contract(_) | SymbolValues::Stmt(_) => false, | ||
} | ||
} | ||
|
||
pub fn is_none(&self) -> bool { | ||
match self { | ||
SymbolValues::None => true, | ||
SymbolValues::Expr(_) | SymbolValues::Stmt(_) => false, | ||
SymbolValues::Contract(_) | SymbolValues::Expr(_) | SymbolValues::Stmt(_) => false, | ||
} | ||
} | ||
|
||
pub fn is_stmt(&self) -> bool { | ||
match self { | ||
SymbolValues::Stmt(_) => true, | ||
SymbolValues::Expr(_) | SymbolValues::None => false, | ||
SymbolValues::Contract(_) | SymbolValues::Expr(_) | SymbolValues::None => false, | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File comment to explain what contracts are and link to documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comment + CBMC documentation link.