Skip to content
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

modifies Clauses for Function Contracts #2800

Merged
merged 80 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
2e6bed3
WIP
JustusAdam Sep 30, 2023
c772a10
Sketch for new modifies clause
JustusAdam Sep 30, 2023
ed2e79d
Add explicit lifetimes to arguments and reorder code
JustusAdam Oct 1, 2023
5a8f746
Sketch for the internal mechanisms
JustusAdam Oct 1, 2023
2759469
Debugging proc-macro code
JustusAdam Oct 2, 2023
9dd55a4
Fixed the assigns bug
JustusAdam Nov 2, 2023
ba27ab6
Using lifetime decoupling for `modifies`
JustusAdam Nov 2, 2023
bbc4e99
Fix lifetime decoupling trait
JustusAdam Nov 3, 2023
00157cb
Make assigns existence optional again
JustusAdam Nov 3, 2023
3db104d
Merge remote-tracking branch 'fork/main' into new-assigns-check
JustusAdam Nov 3, 2023
559cf81
Reviving havoc protection for reentry tracker
JustusAdam Nov 3, 2023
f6d939c
Fixed recursion tracker havocking
JustusAdam Nov 7, 2023
187c56f
Fmt
JustusAdam Nov 11, 2023
54727e2
Cargo fix
JustusAdam Nov 12, 2023
b7c8e63
Fix two test cases + support impl call
JustusAdam Nov 14, 2023
27ebf4f
Fix another test case
JustusAdam Nov 14, 2023
d9e921c
Simple havoc
JustusAdam Nov 14, 2023
ffcdc76
Fixing Formatting and clippy
JustusAdam Nov 19, 2023
b833438
Kani fmt
JustusAdam Nov 19, 2023
64f7e5f
Fix simlpe havoc
JustusAdam Nov 21, 2023
0c3cea7
Fix static exclude for recursion tracker
JustusAdam Nov 22, 2023
4b451cf
Some changes had accidentally been reverted
JustusAdam Nov 22, 2023
0b6ba1e
Formatting
JustusAdam Nov 22, 2023
9362624
Allow pointer transmutes
JustusAdam Nov 22, 2023
7148bac
turns out these need to be refs
JustusAdam Nov 22, 2023
ba4a73e
Gracefully handle if the contract artifact is absent
JustusAdam Nov 22, 2023
9610a9e
Clippy ...
JustusAdam Nov 22, 2023
03ef3ce
Ignoring this error for now
JustusAdam Nov 23, 2023
7b788c7
Changing how contracts communicate
JustusAdam Nov 29, 2023
904ef0e
Merge remote-tracking branch 'fork/main' into new-assigns-check
JustusAdam Nov 29, 2023
981a465
Formatting
JustusAdam Nov 29, 2023
f9b4ef3
Fix Warnings
JustusAdam Nov 29, 2023
12a993d
Consistent use of file paths for recursion tracker
JustusAdam Nov 30, 2023
1843442
Simplify check function editingˆ
JustusAdam Nov 30, 2023
00518b0
Fix ordering for havoc and postconditions
JustusAdam Nov 30, 2023
862a5d7
Extra test cases
JustusAdam Nov 30, 2023
e229ad0
Formatting
JustusAdam Nov 30, 2023
9d95def
Grouping test cases and adding more
JustusAdam Dec 1, 2023
15a596b
Documentation, dead code removal and unique arguments in `modifies`
JustusAdam Dec 1, 2023
af780f1
Test case for unique generated argument names
JustusAdam Dec 1, 2023
e76d0b3
Added missing `expected` files
JustusAdam Dec 1, 2023
907a7f9
Another missing `expected` file
JustusAdam Dec 1, 2023
1c71508
Revert some clippy changes
JustusAdam Dec 1, 2023
e4f3cfd
Remove not strictly necessary dep
JustusAdam Dec 1, 2023
9f3d102
Some basic write-sets documentation
JustusAdam Dec 1, 2023
0573d09
Missing formatting
JustusAdam Dec 1, 2023
384c2f6
Forgot to commit this one
JustusAdam Dec 1, 2023
f7578a8
Typo
JustusAdam Dec 1, 2023
1fffd53
Tinkering with the vector test case.
JustusAdam Dec 4, 2023
34e3a1b
Addressing code review
JustusAdam Dec 14, 2023
f01b619
Apply suggestions from code review
JustusAdam Dec 14, 2023
f283759
Addressing code review leftovers
JustusAdam Dec 14, 2023
6192f3e
Merge remote-tracking branch 'origin/new-assigns-check' into new-assi…
JustusAdam Dec 14, 2023
00bfe6e
Added missing expected file
JustusAdam Dec 14, 2023
9c6b876
Fixed the expected file
JustusAdam Dec 15, 2023
a5dfccd
Added test cases for global variable modifications
JustusAdam Dec 15, 2023
c5b6a2b
Apply suggestions from code review
JustusAdam Dec 23, 2023
6892afc
Suggestions from @feliperodri
JustusAdam Dec 23, 2023
0819432
Merge remote-tracking branch 'origin/new-assigns-check' into new-assi…
JustusAdam Dec 23, 2023
55c5583
Merge remote-tracking branch 'fork/main' into new-assigns-check
JustusAdam Dec 25, 2023
455373c
Update contract code to stable MIR
JustusAdam Dec 26, 2023
68a7f6e
Merge remote-tracking branch 'origin/new-assigns-check' into new-assi…
JustusAdam Dec 26, 2023
0980cbc
Addressing code review comments
JustusAdam Jan 29, 2024
fac45b6
Copyright
JustusAdam Jan 29, 2024
96d4b12
Clippy suggestions
JustusAdam Jan 29, 2024
63d2d7a
Merge remote-tracking branch 'fork/main' into new-assigns-check
JustusAdam Jan 29, 2024
d105b87
Update error reporting
JustusAdam Jan 29, 2024
5eab701
Fixing manes used by contracts metadata
JustusAdam Jan 30, 2024
4bd7583
Fix test case
JustusAdam Jan 30, 2024
0a4503a
Formatting
JustusAdam Jan 31, 2024
6414e22
Clippy complained
JustusAdam Jan 31, 2024
6bbbb24
Whoops
JustusAdam Jan 31, 2024
9b17d94
Name the other Rc test case 'fixme'
JustusAdam Jan 31, 2024
52e703a
Forgot that this now needs formatting
JustusAdam Feb 1, 2024
40e3abe
Used the wrong name
JustusAdam Feb 1, 2024
8db7d20
Merge branch 'main' into new-assigns-check
feliperodri Feb 1, 2024
75a554d
Suggestions from code review
JustusAdam Feb 2, 2024
edd7bf8
Forgot to stage after renaming
JustusAdam Feb 2, 2024
7d08810
Moving all the contract communication logic into the compiler
JustusAdam Feb 2, 2024
1807020
Wrong import
JustusAdam Feb 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cprover_bindings/src/goto_program/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ pub use expr::{
};
pub use location::Location;
pub use stmt::{Stmt, StmtBody, SwitchCase};
pub use symbol::{Symbol, SymbolValues};
pub use symbol::{FunctionContract, Lambda, Symbol, SymbolValues};
pub use symbol_table::SymbolTable;
pub use typ::{CIntType, DatatypeComponent, Parameter, Type};
72 changes: 72 additions & 0 deletions cprover_bindings/src/goto_program/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub struct Symbol {
pub location: Location,
pub typ: Type,
pub value: SymbolValues,
/// Contracts to be enforced (only supported for functions)
pub contract: Option<Box<FunctionContract>>,

/// Optional debugging information

Expand Down Expand Up @@ -44,6 +46,57 @@ pub struct Symbol {
pub is_weak: bool,
}

/// The equivalent of a "mathematical function" in CBMC. Semantically this is an
/// anonymous function object, similar to a closure, but without closing over an
/// environment.
///
/// This is only valid for use as a function contract. It may not perform side
/// effects, a property that is enforced on the CBMC side.
///
/// The precise nomenclature is that in CBMC a contract value has *type*
/// `mathematical_function` and values of that type are `lambda`s. Since this
/// struct represents such values it is named `Lambda`.
#[derive(Debug, Clone)]
pub struct Lambda {
pub arguments: Vec<Parameter>,
pub body: Expr,
}

impl Lambda {
pub fn as_contract_for(
fn_ty: &Type,
return_var_name: Option<InternedString>,
body: Expr,
) -> Self {
let arguments = match fn_ty {
Type::Code { parameters, return_type } => {
[Parameter::new(None, return_var_name, (**return_type).clone())]
.into_iter()
.chain(parameters.iter().cloned())
.collect()
}
_ => panic!(
"Contract lambdas can only be generated for `Code` types, received {fn_ty:?}"
),
};
Self { arguments, body }
}
}

/// The CBMC representation of a function contract. Represents
/// https://diffblue.github.io/cbmc/contracts-user.html but currently only assigns clauses are
/// supported.
#[derive(Clone, Debug)]
pub struct FunctionContract {
pub(crate) assigns: Vec<Lambda>,
}

impl FunctionContract {
pub fn new(assigns: Vec<Lambda>) -> Self {
Self { assigns }
}
}

/// Currently, only C is understood by CBMC.
// TODO: <https://github.com/model-checking/kani/issues/1>
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -84,6 +137,7 @@ impl Symbol {
base_name,
pretty_name,

contract: None,
module: None,
mode: SymbolModes::C,
// global properties
Expand All @@ -107,6 +161,18 @@ impl Symbol {
}
}

/// Add this contract to the symbol (symbol must be a function) or fold the
/// conditions into an existing contract.
pub fn attach_contract(&mut self, contract: FunctionContract) {
assert!(self.typ.is_code());
match self.contract {
Some(ref mut prior) => {
prior.assigns.extend(contract.assigns);
}
None => self.contract = Some(Box::new(contract)),
}
}

/// The symbol that defines the type of the struct or union.
/// For a struct foo this is the symbol "tag-foo" that maps to the type struct foo.
pub fn aggr_ty<T: Into<InternedString>>(t: Type, pretty_name: T) -> Symbol {
Expand Down Expand Up @@ -319,6 +385,12 @@ impl Symbol {
self.is_auxiliary = hidden;
self
}

/// Set `is_property`.
pub fn with_is_property(mut self, v: bool) -> Self {
self.is_property = v;
self
}
}

/// Predicates
Expand Down
13 changes: 12 additions & 1 deletion cprover_bindings/src/goto_program/symbol_table.rs
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::{env, MachineModel};
use super::{BuiltinFn, Stmt, Symbol};
use super::{BuiltinFn, FunctionContract, Stmt, Symbol};
use crate::InternedString;
use std::collections::BTreeMap;
/// This is a typesafe implementation of the CBMC symbol table, based on the CBMC code at:
Expand Down Expand Up @@ -79,6 +79,17 @@ impl SymbolTable {
let name = name.into();
self.symbol_table.get_mut(&name).unwrap().update_fn_declaration_with_definition(body);
}

/// Attach a contract to the symbol identified by `name`. If a prior
/// contract exists it is extended with additional clauses.
pub fn attach_contract<T: Into<InternedString>>(
&mut self,
name: T,
contract: FunctionContract,
) {
let sym = self.symbol_table.get_mut(&name.into()).unwrap();
sym.attach_contract(contract);
}
JustusAdam marked this conversation as resolved.
Show resolved Hide resolved
}

/// Getters
Expand Down
11 changes: 11 additions & 0 deletions cprover_bindings/src/goto_program/typ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,17 @@ impl Parameter {
}
}

/// Constructor
impl Parameter {
pub fn new<S: Into<InternedString>>(
base_name: Option<S>,
identifier: Option<S>,
typ: Type,
) -> Self {
Self { base_name: base_name.map(Into::into), identifier: identifier.map(Into::into), typ }
}
}

impl CIntType {
pub fn sizeof_in_bits(&self, st: &SymbolTable) -> u64 {
match self {
Expand Down
9 changes: 9 additions & 0 deletions cprover_bindings/src/irep/irep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use super::super::goto_program::{Location, Type};
use super::super::MachineModel;
use super::{IrepId, ToIrep};
use crate::cbmc_string::InternedString;
use crate::linear_map;
use linear_map::LinearMap;
use num::BigInt;
use std::fmt::Debug;
Expand Down Expand Up @@ -141,4 +142,12 @@ impl Irep {
pub fn zero() -> Irep {
Irep::just_id(IrepId::Id0)
}

pub fn tuple(sub: Vec<Irep>) -> Self {
Irep {
id: IrepId::Tuple,
sub,
named_sub: linear_map![(IrepId::Type, Irep::just_id(IrepId::Tuple))],
}
}
}
2 changes: 2 additions & 0 deletions cprover_bindings/src/irep/irep_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ pub enum IrepId {
CSpecLoopInvariant,
CSpecRequires,
CSpecEnsures,
CSpecAssigns,
VirtualFunction,
ElementType,
WorkingDirectory,
Expand Down Expand Up @@ -1462,6 +1463,7 @@ impl ToString for IrepId {
IrepId::CSpecLoopInvariant => "#spec_loop_invariant",
IrepId::CSpecRequires => "#spec_requires",
IrepId::CSpecEnsures => "#spec_ensures",
IrepId::CSpecAssigns => "#spec_assigns",
IrepId::VirtualFunction => "virtual_function",
IrepId::ElementType => "element_type",
IrepId::WorkingDirectory => "working_directory",
Expand Down
72 changes: 58 additions & 14 deletions cprover_bindings/src/irep/to_irep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use super::super::goto_program;
use super::super::MachineModel;
use super::{Irep, IrepId};
use crate::linear_map;
use crate::InternedString;
use goto_program::{
BinaryOperator, CIntType, DatatypeComponent, Expr, ExprValue, Location, Parameter,
BinaryOperator, CIntType, DatatypeComponent, Expr, ExprValue, Lambda, Location, Parameter,
SelfOperator, Stmt, StmtBody, SwitchCase, SymbolValues, Type, UnaryOperator,
};

Expand All @@ -16,10 +17,10 @@ pub trait ToIrep {
}

/// Utility functions
fn arguments_irep(arguments: &[Expr], mm: &MachineModel) -> Irep {
fn arguments_irep<'a>(arguments: impl Iterator<Item = &'a Expr>, mm: &MachineModel) -> Irep {
Irep {
id: IrepId::Arguments,
sub: arguments.iter().map(|x| x.to_irep(mm)).collect(),
sub: arguments.map(|x| x.to_irep(mm)).collect(),
named_sub: linear_map![],
}
}
Expand Down Expand Up @@ -169,6 +170,16 @@ impl ToIrep for Expr {
}
}

impl Irep {
pub fn symbol(identifier: InternedString) -> Self {
Irep {
id: IrepId::Symbol,
sub: vec![],
named_sub: linear_map![(IrepId::Identifier, Irep::just_string_id(identifier))],
}
}
}

impl ToIrep for ExprValue {
fn to_irep(&self, mm: &MachineModel) -> Irep {
match self {
Expand Down Expand Up @@ -245,7 +256,7 @@ impl ToIrep for ExprValue {
}
ExprValue::FunctionCall { function, arguments } => side_effect_irep(
IrepId::FunctionCall,
vec![function.to_irep(mm), arguments_irep(arguments, mm)],
vec![function.to_irep(mm), arguments_irep(arguments.iter(), mm)],
),
ExprValue::If { c, t, e } => Irep {
id: IrepId::If,
Expand Down Expand Up @@ -297,14 +308,7 @@ impl ToIrep for ExprValue {
sub: values.iter().map(|x| x.to_irep(mm)).collect(),
named_sub: linear_map![],
},
ExprValue::Symbol { identifier } => Irep {
id: IrepId::Symbol,
sub: vec![],
named_sub: linear_map![(
IrepId::Identifier,
Irep::just_string_id(identifier.to_string()),
)],
},
ExprValue::Symbol { identifier } => Irep::symbol(*identifier),
ExprValue::Typecast(e) => {
Irep { id: IrepId::Typecast, sub: vec![e.to_irep(mm)], named_sub: linear_map![] }
}
Expand Down Expand Up @@ -456,7 +460,7 @@ impl ToIrep for StmtBody {
vec![
lhs.as_ref().map_or(Irep::nil(), |x| x.to_irep(mm)),
function.to_irep(mm),
arguments_irep(arguments, mm),
arguments_irep(arguments.iter(), mm),
],
),
StmtBody::Goto(dest) => code_irep(IrepId::Goto, vec![])
Expand Down Expand Up @@ -499,10 +503,50 @@ impl ToIrep for SwitchCase {
}
}

impl ToIrep for Lambda {
/// At the moment this function assumes that this lambda is used for a
/// `modifies` contract. It should work for any other lambda body, but
/// the parameter names use "modifies" in their generated names.
fn to_irep(&self, mm: &MachineModel) -> Irep {
let (ops_ireps, types) = self
.arguments
.iter()
.enumerate()
.map(|(index, param)| {
let ty_rep = param.typ().to_irep(mm);
(
Irep::symbol(
param.identifier().unwrap_or_else(|| format!("_modifies_{index}").into()),
)
.with_named_sub(IrepId::Type, ty_rep.clone()),
ty_rep,
)
})
.unzip();
let typ = Irep {
id: IrepId::MathematicalFunction,
sub: vec![Irep::just_sub(types), self.body.typ().to_irep(mm)],
named_sub: Default::default(),
};
Irep {
id: IrepId::Lambda,
sub: vec![Irep::tuple(ops_ireps), self.body.to_irep(mm)],
named_sub: linear_map!((IrepId::Type, typ)),
}
}
}

impl goto_program::Symbol {
pub fn to_irep(&self, mm: &MachineModel) -> super::Symbol {
let mut typ = self.typ.to_irep(mm);
if let Some(contract) = &self.contract {
typ = typ.with_named_sub(
IrepId::CSpecAssigns,
Irep::just_sub(contract.assigns.iter().map(|req| req.to_irep(mm)).collect()),
);
}
super::Symbol {
typ: self.typ.to_irep(mm),
typ,
value: match &self.value {
SymbolValues::Expr(e) => e.to_irep(mm),
SymbolValues::Stmt(s) => s.to_irep(mm),
Expand Down
Loading
Loading