Skip to content

Add proof_with() and declare_with() builtin functions for ghost/track…#2436

Open
ziqiaozhou wants to merge 4 commits into
mainfrom
proof-with-builtin
Open

Add proof_with() and declare_with() builtin functions for ghost/track…#2436
ziqiaozhou wants to merge 4 commits into
mainfrom
proof-with-builtin

Conversation

@ziqiaozhou
Copy link
Copy Markdown
Collaborator

@ziqiaozhou ziqiaozhou commented May 12, 2026

…ed args

Why: to allow with spec in verus_spec for traits and external functions.

  • Add builtin API: proof_with(), declare_with() (plus aliases declare_with_tracked/declare_with_ghost for backward compatibility)

  • Infer Tracked vs Ghost mode from the type annotation via ADT DefId

  • Add VIR lowering in fn_call_to_vir.rs with type and mode checking

  • Add pre-scan for declare_with params in rust_to_vir_func.rs

  • Add proof_with test suite

  • Add first-pass lifetime checking to ensure that tracked/ghost arguments passed via proof_with() satisfy the lifetime constraints declared by declare_with() in the callee.

  • Add proof_with_lifetime.rs module with region-aware lifetime checking

  • Use rustc_hir_analysis::lower_ty() to preserve real regions instead of erased regions from typeck writeback

  • Check that argument regions outlive expected parameter regions using the function's where-clause predicates

  • Add test cases for lifetime mismatch detection (both Tracked and Ghost)

For trait methods, #[verus_spec(with...)] and proof_with! pair should rewrite code to following style

use vstd::prelude::*;
struct A {
    a: u64
}

trait AOp {
    fn test(&self) {
        let b: Tracked<u64> = declare_with();
        let c: Ghost<u32> = declare_with();
        requires(b@ == 1 && c@ == 2);
    }
}
impl AOp for A {
    fn test(&self)
    {
        let b: Tracked<u64> = declare_with();
        let c: Ghost<u32> = declare_with();
        assert(b@ == 1);
        assert(c@ == 0); // FAILS
    }
}
fn call_test() {
    let a = A { a: 0 };
    proof_with((Tracked(1u64), Ghost(2u32)), a.test());
}

#[verifier(external)]
fn unverified_call_test() {
    let a = A { a: 0 };
    a.test();
}


For external functions, we need to expand assume_specification and proof_with! to rewrite code in following style

use vstd::prelude::*;
#[verifier(external)]
fn negate_bool(b: bool, x: u8) -> bool {
    !b
}

#[verifier(external_fn_specification)]
fn negate_bool_requires_ensures(b: bool, x: u8) -> bool
{
    let extra: Tracked<u8> = declare_with();
    requires(x == extra@);
    ensures(|ret_b: bool| ret_b == !b);
    negate_bool(b, x)
}

fn call_test() {
    proof_with(Tracked(1u8), negate_bool(true, 1));
}

#[verifier(external)]
fn unverified_call_test() {
    negate_bool(true, 1);
}

By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

@ziqiaozhou ziqiaozhou force-pushed the proof-with-builtin branch from 68b907c to de1defd Compare May 12, 2026 00:28
…ed args

Why: to allow with spec in verus_spec for traits and external functions.

- Add builtin API: proof_with(), declare_with() (plus aliases
  declare_with_tracked/declare_with_ghost for backward compatibility)
- Infer Tracked vs Ghost mode from the type annotation via ADT DefId
- Add VIR lowering in fn_call_to_vir.rs with type and mode checking
- Add pre-scan for declare_with params in rust_to_vir_func.rs
- Add proof_with test suite

- Add first-pass lifetime checking to ensure that tracked/ghost arguments
passed via proof_with() satisfy the lifetime constraints declared by
declare_with() in the callee.
- Add proof_with_lifetime.rs module with region-aware lifetime checking
- Use rustc_hir_analysis::lower_ty() to preserve real regions instead
  of erased regions from typeck writeback
- Check that argument regions outlive expected parameter regions using
  the function's where-clause predicates
- Add test cases for lifetime mismatch detection (both Tracked and Ghost)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Ziqiao Zhou <ziqiaozhou@microsoft.com>
@ziqiaozhou ziqiaozhou force-pushed the proof-with-builtin branch from de1defd to 188e57c Compare May 12, 2026 16:20
@ziqiaozhou ziqiaozhou force-pushed the proof-with-builtin branch from ca79384 to 7953d42 Compare May 13, 2026 07:27
- Rewrite proof_with_lifetime.rs using InferCtxt region solver
- Rename external_fn_extra_tracked_params → declare_with_params
- Clean up check_proof_with_lifetime signature (10 → 7 args)
- Simplify is_tracked match in pre_scan_declare_with_params
- Refactor pending_tracked_args to Option<Vec> with take() semantics
- Remove in_args_depth field from BodyCtxt

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ziqiaozhou ziqiaozhou force-pushed the proof-with-builtin branch from 7953d42 to 8fb7da4 Compare May 13, 2026 07:40
@ziqiaozhou ziqiaozhou marked this pull request as ready for review May 14, 2026 05:55
@ziqiaozhou ziqiaozhou marked this pull request as draft May 14, 2026 05:56
@ziqiaozhou ziqiaozhou marked this pull request as ready for review May 15, 2026 23:36
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.

1 participant