verus-lang / verus

Verified Rust for low-level systems code
MIT License
1.24k stars 72 forks source link

Failed precondition error message doesn't point to source code #1333

Open nwtnni opened 2 weeks ago

nwtnni commented 2 weeks ago

Found during SOSP workshop (cc @utaal)

error: precondition not satisfied
  --> exercise_01_memoize.rs:91:9
   |
91 |         handle.release_write(cache);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  --> /home/runner/work/verus/verus/source/vstd/rwlock.rs:404:13
   |
   = note: failed precondition

error: aborting due to 2 previous errors

The path /home/runner/work/verus/verus/source/vstd/rwlock.rs is incorrect for my system.

Source code:

// Verus tutorial - 'Advanced Topics' Exercise: computation memoization
//
// This file provides an implementation of a store to memoize the results of some computation.
// This exercise illustrates how Verus can handle interior mutability using lock invariants.
//
// Most of the implementation has been filled in for you. There are 2 blanks to fill in;
// search for 'EXERCISE' in this file.

use std::sync::Arc;
use vstd::hash_map::HashMapWithView;
use vstd::prelude::*;
use vstd::rwlock::{RwLock, RwLockPredicate};

mod memoization_exercise_internals;

verus! {
broadcast use vstd::std_specs::hash::group_hash_axioms;

//////// Import a function. Our objective is to memoize the results of this function.

use memoization_exercise_internals::{Args, Output};

/// Pure functional specification of the function to be memoized.
pub closed spec fn func(args: &Args) -> Output {
    memoization_exercise_internals::func(args)
}

/// Executable version of the function to be memoized.
/// The `ensures` clause guarantees that this computation is deterministic,
/// i.e., it returns the same value every time it is called for the same arguments.
pub fn expensive_function(args: &Args) -> (out: Output)
    ensures out == func(args),
{
    memoization_exercise_internals::expensive_function(args)
}

//////// Here we define the main data structure.

pub struct Memoizer {
    lock: RwLock<HashMapWithView<Args, Output>, Pred>,
}

// This is where we define our "lock invariant". The 'Pred' type is kind of a dummy type, here;
// in more complex cases, we might put actual data in here, but we don't need to for this
// exercise. Thus for the purposes of this file, 'Pred' only serves as a way to connect
// the lock invariant to the lock.
//
// See the RwLock docs for more information:
// https://verus-lang.github.io/verus/verusdoc/vstd/rwlock/struct.RwLock.html
struct Pred { }
impl RwLockPredicate<HashMapWithView<Args, Output>> for Pred {
    open spec fn inv(self, v: HashMapWithView<Args, Output>) -> bool {
        &&& v@.dom().finite()
        // &&& forall |k: Args| #[trigger] v@.dom().contains(k) ==> v@[k] == func(&k)

        &&& v@.dom().fold(true, |prev: bool, next: Args| prev && v@[next] == func(&next))
    }
}

impl Memoizer {
    /// Construct a new 'memoizer' object
    pub fn new() -> Self {
        Memoizer {
            lock: RwLock::new(HashMapWithView::<Args, Output>::new(), Ghost(Pred{})),
        }
    }

    /// Get the result of applying `expensive_function` to the given arguments.
    /// This should call `expensive_function` only if necessary, and it should store
    /// the output for future use.
    pub fn get(&self, args: &Args) -> (out: Output)
        ensures out == func(args)
    {
        // EXERCISE: fill me in
        //
        // Useful docs:
        //  - RwLock: https://verus-lang.github.io/verus/verusdoc/vstd/rwlock/struct.RwLock.html
        //  - HashMapWithView: https://verus-lang.github.io/verus/verusdoc/vstd/hash_map/struct.HashMapWithView.html

        let cache = self.lock.acquire_read();

        match cache.borrow().get(args) {
            Some(value) => return *value,
            None => cache.release_read(),
        }

        let (mut cache, handle) = self.lock.acquire_write();
        let value = expensive_function(args);
        cache.insert(*args, value);
        // assert(handle.rwlock().inv(cache));
        handle.release_write(cache);

        value
    }
}

/// Example usage. Note that this will compile only if `Memoizer: Sync`.
fn main() {
    let memoizer = Memoizer::new();
    let shared_memoizer = Arc::new(memoizer);

    let shared_memoizer1 = shared_memoizer.clone();
    vstd::thread::spawn(move || {
        let v = (*shared_memoizer1).get(&7);
        assert(v == func(&7));
    });

    let shared_memoizer2 = shared_memoizer.clone();
    vstd::thread::spawn(move || {
        let v = (*shared_memoizer2).get(&20);
        assert(v == func(&20));
    });
}

// Used as a placeholder for the exercises, where necessary
#[verifier::external_body]
fn todo<A>() -> A
    requires false
{
    todo!();
}
}
tjhance commented 2 weeks ago

This probably means the vstd source wasn't available to the error reporter. It might be an issue with the binary release.