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

Replace height intrinsic with is_smaller_than and make height a partial order #570

Merged
merged 11 commits into from
Jun 14, 2023

Conversation

Chris-Hawblitzel
Copy link
Collaborator

This pull request allows decreases that go through Seq, Map, FnSpec, and user-defined abstract datatypes. For example:

        struct S {
            x: Seq<Box<S>>,
        }

        spec fn f(s: S) -> int
            decreases s
        {
            if s.x.len() > 0 {
                f(*s.x[0])
            } else {
                0
            }
        }

The new is_smaller_than(a, b) intrinsic expresses that b can decrease to a in a decreases clause:

#[verifier(external_body)]
#[verifier(broadcast_forall)]
pub proof fn axiom_seq_index_height<A>(s: Seq<A>, i: int)
    requires
        0 <= i < s.len(),
    ensures
        #[trigger] is_smaller_than(s[i], s),
{
}

is_smaller_than also works on lexicographic tuples, which can be useful for debugging termination failures:

assert(is_smaller_than((i, j - 1), (i, j)));

is_smaller_than replaces the previous height intrinsic. height is still used in the SMT encoding, but heights are no longer integers, and are instead Z3 partial orders. This allows the SMT encoding to express the heights of infinite maps and FnSpec functions.

Partial orders are a new feature in Z3, so this pull request requires upgrading to the latest Z3 release (4.12.2).

and in the SMT encoding, encode height as a Z3 partial order rather than an int
to allow decreases on infinite maps.
Copy link
Collaborator

@tjhance tjhance left a comment

Choose a reason for hiding this comment

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

I'm a little confused about how tuples work. Is the lexicographic ordering of tuples consistent with the height-ordering of tuples?

If I write is_smaller_than((a, b), (c, d)) then it looks like rust_to_vir_expr lexicographic ordering, but we can also talk about the partial-order on tuple objects themselves:

let x = (a, b);
let y = (c, d);
is_smaller_than(x, y)

Is this equivalent to is_smaller_than((a, b), (c, d))?

LinearOrder,
TreeOrder,
PiecewiseLinearOrder,
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

this could use some documentation

@@ -59,7 +67,7 @@ pub enum BinaryOp {
Gt,
EuclideanDiv,
EuclideanMod,

Relation(Relation, u64),
Copy link
Collaborator

Choose a reason for hiding this comment

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

same - what's the u64 for?

@Chris-Hawblitzel
Copy link
Collaborator Author

I'm a little confused about how tuples work. Is the lexicographic ordering of tuples consistent with the height-ordering of tuples?

If I write is_smaller_than((a, b), (c, d)) then it looks like rust_to_vir_expr lexicographic ordering, but we can also talk about the partial-order on tuple objects themselves:

let x = (a, b);
let y = (c, d);
is_smaller_than(x, y)

Is this equivalent to is_smaller_than((a, b), (c, d))?

It's not equivalent. Maybe the larger question is what the syntax for is_smaller_than should be. Right now, the tuples are just a way to transport the various expressions through Rust's syntax and type checker. But we could have some dedicated syntax that doesn't overload tuples like this. I'm not convinced that is_smaller_than is important enough to deserve its own operator in the Verus syntax macro (along the lines of F*'s << operator). But would it be better to have an ordinary ! macro for it? Like is_smaller_than!(a, b; c, d) or is_smaller_than!(a, b => c, d) or decreases_to!(c, d => a, b)?

@tjhance
Copy link
Collaborator

tjhance commented May 30, 2023

Perhaps we should just have is_smaller_than and is_smaller_than_lexicographic (and the latter can just expect syntactic tuples)

@utaal
Copy link
Collaborator

utaal commented Jun 6, 2023

FYI, I made this change to help smoothen the transition to z3 4.12.2 (and for future version updates): 6f5342c (adds a message that points the user to tools/get-z3.(ps1|sh)).

} else {
(vec![args[0]], vec![args[1]])
};
// convert is_smaller_than((x0, y0, z0), (x1, y1, z1)) into
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you please extract this conversion into a separate function?

Copy link
Collaborator

@utaal utaal left a comment

Choose a reason for hiding this comment

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

Thank you! Having is_smaller_than(_lexicographic) will hopefully make debugging failing termination proof easier.

Looks good to me, modulo @tjhance's and my review comments.

@Chris-Hawblitzel
Copy link
Collaborator Author

I added a restriction similar to FStarLang/FStar#2954 . I think this should be ready to merge into main now.

@Chris-Hawblitzel Chris-Hawblitzel merged commit cb4237b into main Jun 14, 2023
@Chris-Hawblitzel Chris-Hawblitzel deleted the is_smaller_than branch June 14, 2023 22:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants