#![feature(rustc_private)] #[macro_use] mod common; use common::*; test_verify_one_file_with_options! { #[test] test1_expand_error ["--expand-errors"] => verus_code! { spec fn is_good_integer(z: int) -> bool { z >= 0 && z != 5 // EXPAND-ERRORS // ^^^^^^ } proof fn test_expansion_easy() { let x = 5; assert(is_good_integer(x)); // FAILS } } => Err(e) => assert_expand_fails(e, 1) } test_verify_one_file_with_options! { #[test] test2_expand_error ["--expand-errors"] => verus_code! { #[derive(PartialEq, Eq)] pub enum Message { Quit(bool), Move { x: i32, y: i32 }, Write(bool), } spec fn is_good_integer_2(x: int) -> bool { x >= 0 // EXPAND-ERRORS // ^^^^^^^ && x != 5 } spec fn is_good_message_2(msg:Message) -> bool { match msg { Message::Quit(b) => b, Message::Move{x, y} => is_good_integer_2( (x as int) - (y as int)), // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Message::Write(b) => b, } } spec fn is_good(msg: Message) -> bool { is_good_message_2(msg) //^^^^^^^^^^^^^^^^^^^^ } proof fn test_expansion_multiple_call() { let x = Message::Move{x: 5, y:6}; assert(is_good(x)); // FAILS //^^^^^^ ^^^^^^^^^^ } } => Err(e) => assert_expand_fails(e, 1) } test_verify_one_file_with_options! { #[test] test3_expand_requires ["--expand-errors"] => verus_code! { #[derive(PartialEq, Eq)] pub enum Message { Quit(bool), Move { x: i32, y: i32 }, Write(bool), } spec fn is_good_integer_3(x: int) -> bool { x >= 0 // EXPAND-ERRORS // ^^^^^^ && x != 5 } spec fn is_good_message_3(msg:Message) -> bool { match msg { Message::Quit(b) => b, // ^^^^^^^^^^^^^^^^ (TODO bad span due to match span) Message::Move{x, y} => is_good_integer_3( (x as int) - (y as int)), // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Message::Write(b) => b, } } proof fn test_require_failure(m:Message, b: bool) -> (good_int: int) requires b, is_good_message_3(m), // EXPAND-ERRORS // ^^^^^^^^^^^^^^^^^^^^ ensures is_good_integer_3(good_int), { return 0; } proof fn test_3(x:int) { let x = Message::Move{x: 0, y: 5}; test_require_failure(x, true); // FAILS //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } } => Err(e) => assert_expand_fails(e, 2) } test_verify_one_file_with_options! { #[test] test4_expand_ensures ["--expand-errors"] => verus_code! { #[derive(PartialEq, Eq)] pub enum Message { Quit(bool), Move { x: i32, y: i32 }, Write(bool), } spec fn is_good_integer_4(x: int) -> bool { x >= 0 && x != 5 // EXPAND-ERRORS // ^^^^^^ } spec fn is_good_message_4(msg:Message) -> bool { match msg { Message::Quit(b) => b, Message::Move{x, y} => is_good_integer_4( (x as int) - (y as int)), // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Message::Write(b) => b, } } proof fn test_ensures_failure(b: bool) -> (good_msg: Message) ensures is_good_message_4(good_msg), // FAILS //^^^^^^^^^^^^^^^^^^^^^^^^^^^ { let mut ret = Message::Write(true); if !b {ret = Message::Move{x: 10, y: 5};} ret } } => Err(e) => assert_expand_fails(e, 1) } test_verify_one_file_with_options! { #[ignore] #[test] test5_expand_forall ["--expand-errors"] => verus_code! { use vstd::seq::*; spec fn seq_bounded_by_length(s1: Seq) -> bool { (forall|i:int| (0 <= i && i < s1.len()) ==> // EXPAND-ERRORS (0 <= s1.index(i) && s1.index(i) < s1.len())) } proof fn test_expansion_forall() { let mut ss = Seq::empty(); ss = ss.push(0); ss = ss.push(10); assert(seq_bounded_by_length(ss)); // EXPAND-ERRORS } } => Err(e) => assert_expand_fails(e, 2) } test_verify_one_file_with_options! { // credit: (this example is copied from examples/rw2022_scripts.rs, example C) #[test] test6_expand_forall ["--expand-errors"] => verus_code! { spec fn divides(factor: nat, candidate: nat) -> bool { candidate % factor == 0 } spec fn is_prime(candidate: nat) -> bool { &&& 1 < candidate &&& forall|factor: nat| 1 < factor && factor < candidate ==> !divides(factor, candidate) // EXPAND-ERRORS } proof fn test_trigger() { assert(is_prime(6)); // FAILS } } => Err(e) => assert_expand_fails(e, 1) } test_verify_one_file_with_options! { // example: `reveal` does not flow #[test] test7_local_reveal ["--expand-errors"] => verus_code! { #[derive(PartialEq, Eq)] pub enum Message { Quit(bool), Move { x: i32, y: i32 }, Write(bool), } #[verifier(opaque)] spec fn is_good_integer(x: int) -> bool { x >= 0 && x != 5 } #[verifier(opaque)] spec fn is_good_message_10(msg:Message) -> bool { match msg { Message::Quit(b) => b, Message::Move{x, y} => is_good_integer( (x as int) - (y as int)), Message::Write(b) => b, } } proof fn test_reveal(b: bool) { let good_msg = Message::Move{x: 0, y: 0}; if b { reveal(is_good_message_10); } else { assert_by(true, {reveal(is_good_message_10);}); assert(is_good_message_10(good_msg)); // FAILS } } } => Err(e) => assert_expand_fails(e, 0) } test_verify_one_file_with_options! { // example: `reveal` at function exit point #[test] test8_ensures_reveal ["--expand-errors"] => verus_code! { #[derive(PartialEq, Eq)] pub enum Message { Quit(bool), Move { x: i32, y: i32 }, Write(bool), } #[verifier(opaque)] spec fn is_good_integer_13(x: int) -> bool { x >= 0 && x != 5 } #[verifier(opaque)] spec fn is_good_message_13(msg:Message) -> bool { match msg { Message::Quit(b) => b, Message::Move{x, y} => is_good_integer_13( (x as int) - (y as int)), // EXPAND-ERRORS Message::Write(b) => b, } } proof fn test_reveal_at_ensures(b: bool) -> (good_msg: Message) ensures is_good_message_13(good_msg), // FAILS { let good_msg = Message::Move{x: 0, y: 0}; reveal(is_good_message_13); good_msg } } => Err(e) => assert_expand_fails(e, 1) } test_verify_one_file_with_options! { #[test] test9_assume_conjunct ["--expand-errors"] => verus_code! { spec fn p1(a: int) -> bool { a == 1 } spec fn p2(a: int) -> bool { a == 2 } spec fn p(a: int) -> bool { &&& p1(a) &&& p2(a) // EXPAND-ERRORS } proof fn test(a: int) { assume(p1(a)); assert(p(a)); // FAILS } } => Err(e) => assert_expand_fails(e, 1) } test_verify_one_file_with_options! { #[ignore] #[test] test10_expand_forall_assume ["--expand-errors"] => verus_code! { use vstd::seq::*; spec fn no(i: int) -> bool { false } spec fn seq_bounded_by_length(s1: Seq) -> bool { (forall|i:int| (0 <= i && i < s1.len()) ==> no(i) && (0 <= #[trigger] s1[i] < s1.len())) } spec fn long_seq(s1: Seq) -> bool { s1.len() > 20 } spec fn seq_is_long_and_bounded_by_length(s1: Seq) -> bool { &&& seq_bounded_by_length(s1) &&& long_seq(s1) // EXPAND-ERRORS } proof fn test_expansion_forall() { let mut ss = Seq::empty(); ss = ss.push(0); ss = ss.push(10); assume(seq_bounded_by_length(ss)); assert(seq_is_long_and_bounded_by_length(ss)); // EXPAND-ERRORS } } => Err(e) => assert_expand_fails(e, 2) } test_verify_one_file_with_options! { #[test] test11_traits_ensures ["--expand-errors"] => verus_code! { trait T { spec fn f() -> bool; proof fn test() ensures Self::f(); // FAILS } impl T for u64 { spec fn f() -> bool { false } proof fn test() { } } } => Err(e) => assert_expand_fails(e, 0) } test_verify_one_file_with_options! { #[test] assoc_types_issue952 ["--expand-errors"] => verus_code! { struct C; trait D { spec fn l(&self, k: &E) -> u64; fn m(&self, k: &E) -> (p: u64) ensures p == self.l(k), // FAILS ; } trait N { type O; fn m() -> u64; } impl D for E where E: N { spec fn l(&self, k: &E::O) -> u64; fn m(&self, k: &E::O) -> u64 { E::m() } } } => Err(e) => assert_fails(e, 1) } test_verify_one_file_with_options! { #[test] unboxing_and_negation_issue788 ["--expand-errors"] => verus_code! { trait Tr { spec fn stuff(&self) -> bool; spec fn stuff2(&self) -> bool; } spec fn hi(t: T) -> bool { t.stuff() // EXPAND-ERRORS && !t.stuff2() // EXPAND-ERRORS } proof fn test(t: T) { assert(hi(t)); // FAILS } } => Err(e) => assert_expand_fails(e, 2) } test_verify_one_file_with_options! { #[test] test_open_spec_is_already_open_387_discussioncomment_5679297_1 ["--expand-errors"] => verus_code! { use vstd::set::*; spec fn maybe() -> bool; // spec fn yes() -> bool { true } // spec fn both(s: Set) -> bool { // &&& maybe() // &&& s.contains(0) // EXPAND-ERRORS // } proof fn test(s: Set) { assert(maybe()); // FAILS } } => Err(err) => { dbg!(&err.expand_errors_notes[0].rendered); assert!(err.expand_errors_notes[0].rendered.contains("function is uninterpreted")); } } test_verify_one_file_with_options! { #[test] test_open_spec_is_already_open_387_discussioncomment_5679297_2 ["--expand-errors"] => verus_code! { struct Z { _temp: (), } mod X { pub trait T { spec fn foo(&self) -> bool; } impl T for super::Z { open spec fn foo(&self) -> bool { false } } } use X::T; fn f() { let z = Z { _temp: () }; assert(z.foo()); // FAILS } } => Err(err) => { assert!(err.expand_errors_notes[0].rendered.contains("| false")); assert_expand_fails(err, 0); } } test_verify_one_file_with_options! { #[test] test_requires_resolved_trait_fn ["--expand-errors"] => verus_code! { struct Z { _temp: (), } mod X { pub trait T { fn foo(&self) requires false; } impl T for super::Z { fn foo(&self) { } } } use X::T; fn f() { let z = Z { _temp: () }; z.foo(); // FAILS } } => Err(err) => { assert!(err.expand_errors_notes[0].rendered.contains("false")); assert_expand_fails(err, 0); } } test_verify_one_file_with_options! { #[test] test_forall_requiring_type_invariants ["--expand-errors"] => verus_code! { spec fn stuff(i: nat) -> bool; spec fn bar() -> bool; spec fn foo() -> bool { (forall |i: nat| stuff(i)) && bar() // EXPAND-ERRORS } fn test() requires forall |i: nat| stuff(i), { assert(foo()); // FAILS } } => Err(e) => assert_expand_fails(e, 1) }