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

"intro" combinator for universal quantifiers #1343

Open
Lysxia opened this issue Feb 5, 2025 · 3 comments
Open

"intro" combinator for universal quantifiers #1343

Lysxia opened this issue Feb 5, 2025 · 3 comments
Labels
enhancement New feature or request

Comments

@Lysxia
Copy link
Collaborator

Lysxia commented Feb 5, 2025

To prove a postcondition with a universal quantifier #[ensures(forall<x: _> _)], it would be nice to be able to have a way to put x in scope (like the intro tactic in Coq, or forall in Dafny).


I know of a currently working hacky solution which is to create an auxiliary lemma where the quantified variable becomes a function parameter:

#[ensures(forall<x: _> P(x))]
fn theorem() {
   proof_assert! { forall<x: _> lemma(x) == () }; // Hack to make theorem depend on lemma, so lemma is in the context of theorem in the Coma output
}

#[ensures(P(x))]
fn lemma(x: _) {
   ... // proof with x in scope
}

It's not pretty, but it seems to work, so this issue is more about a "nice to have" feature IMO.


What is missing is a new binder, call it intro:

#[ensures(forall<x: _> P(x))]
fn theorem() {
  pearlite! {
    intro<x: _> ... // Proof of P(x)
  }
}

The challenge is to figure out what the VCgen is supposed to do with this binder.

@jhjourdan proposes this WP (assuming that the expressions don't return a result so Q is just a proposition):

wp (intro<x> F(x)) (Q) = exists<x> wp (F(x)) (Q)

Which is logically correct because all types are inhabited. The question is whether SMT will be able to prove propositions of that form in practice.

We want that WP to be provable if Q is a quantified proposition Q = forall<y> P(y), and F(x) proves P(x).
Indeed, in that case wp (F(x)) (Q) is supposed to look like P(x) -> forall<y> P(y), and the WP wraps it in exists:

wp (intro<x> F(x)) (forall<y> P(y)) = exists<x> (P(x) -> forall<y> P(y))

That's a tautology also known as the drinker's paradox.

SMT solvers will be asked to refute it, i.e., they will be given the negation:

forall<x> (P(x) && exists<y> ~P(y))

and the desired outcome is that they find that to be unsatisfiable, by deriving a contradiction from that statement as a hypothesis, call it H.

That is possible by applying H twice: first we apply H to an arbitrary x0 to get access to the existential y such that P(y), and use that y to apply H a second time. The hope is that SMT solvers will be able to do that even if P has a complex shape.

@Lysxia Lysxia added the enhancement New feature or request label Feb 5, 2025
@jhjourdan
Copy link
Collaborator

Thanks, @Lysxia, for writting all this down!

@xldenis
Copy link
Collaborator

xldenis commented Feb 5, 2025

This is really good. how would this combine with proof assertions? I think like all: in dafny it would be good if we can place assertions under the binder to guide the proof.

@jhjourdan
Copy link
Collaborator

I don't see why this could not work out-of-the-box with assertions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants