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

History Expressions #2993

Closed

Conversation

JustusAdam
Copy link
Contributor

Add support for History Expressions. This allows an ensures clause to evaluate expressions before the function runs, bing them to anonymous variables and use them after calculating the result to be able to compare to the state before the function evaluation.

TODO

  • Update external documentation with information about how history expressions are used
  • Update internal documentation with expanded example of how old is implemented
  • Test case. I could use some help here both with implementing test cases as well as with suggestions for other things to test. Here is the list so far
    • Basic pass/fail
    • pass/fail on using clone and not using clone respectively for a reference
    • Using a function call in old
    • Using a block expression (e.g. sequence of statements) as argument to old
    • Denying nesting of old, e.g. old(... old( ) ... )

Resolves #2803

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.

@JustusAdam JustusAdam added this to the Function Contracts MVP milestone Feb 5, 2024
@github-actions github-actions bot added the Z-BenchCI Tag a PR to run benchmark CI label Feb 5, 2024
/// This doesn't do anything fancy, it's just to make sure the way that history
/// expressions are treated in contract checks and replaces is uniform.
pub fn create_history_expr_bindings(history_expressions: &[(Ident, Expr)]) -> TokenStream2 {
let history_variables = history_expressions.iter().map(|(v, _)| v);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be

let (history_variables, history_exprs) = history_expressions.iter().unzip();

instead of creating two iters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unzip requires creation of two intermediate vectors though because it doesn't split into an iterator but has to be collected.

@pi314mm
Copy link
Contributor

pi314mm commented May 30, 2024

This seems like it would cause problems with the result ident since all idents are lifted, or in the event of someone making a locally bound variable within a closure, so something like ensures((|a| old(a)) (argument)). Wouldn't it be easier to implement if we require that the argument for old be an ident instead of an expression?

Here is an example of something reasonable that breaks

#[kani::ensures((|a| old(*ptr+a))(1) == *ptr)]
fn modify(ptr: &mut u32) {
    *ptr += 1;
}

Here is another example of something reasonable that breaks

#[kani::ensures(old(*ptr+result) == *ptr)]
fn modify(ptr: &mut u32) -> u32 {
    *ptr += 1;
    1
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Z-BenchCI Tag a PR to run benchmark CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

History Expression Support for Function Contracts
3 participants