From 68387e4cf462541028892772d2fc12da8abd6a6c Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 2 Oct 2024 14:01:39 -0400 Subject: [PATCH] Enable doctests to avoid coding mistakes in our API documentation (#3557) In order to enable the tests, I had to fix a few tests, enhance tests with hidden code, and mark a few as `no_run` instead of `rust`. The tests marked as `no_run` will at least compile, but we cannot execute them otherwise execution would be stuck in an infinite loop, since we provide dummy implementation to a lot of our intrinsics with `loop {}`. Finally, as I was cleaning up the documentation, I noticed that we accidently published an API that was meant to be internal to Kani. I marked this API as deprecated for now. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- library/kani/src/contracts.rs | 43 ++++++-- library/kani/src/invariant.rs | 33 ++++++- library/kani/src/lib.rs | 2 + library/kani/src/shadow.rs | 2 +- library/kani/src/slice.rs | 3 +- library/kani_core/src/lib.rs | 98 +++++++++++++------ scripts/kani-regression.sh | 4 +- .../deprecated_warning.expected | 1 + .../ui/check_deprecated/deprecated_warning.rs | 10 ++ 9 files changed, 152 insertions(+), 44 deletions(-) create mode 100644 tests/ui/check_deprecated/deprecated_warning.expected create mode 100644 tests/ui/check_deprecated/deprecated_warning.rs diff --git a/library/kani/src/contracts.rs b/library/kani/src/contracts.rs index beddd7cc580e..2dc2bd2a4957 100644 --- a/library/kani/src/contracts.rs +++ b/library/kani/src/contracts.rs @@ -25,7 +25,7 @@ //! simple division function `my_div`: //! //! ``` -//! fn my_div(dividend: u32, divisor: u32) -> u32 { +//! fn my_div(dividend: usize, divisor: usize) -> usize { //! dividend / divisor //! } //! ``` @@ -35,8 +35,12 @@ //! allows us to declare constraints on what constitutes valid inputs to our //! function. In this case we would want to disallow a divisor that is `0`. //! -//! ```ignore +//! ``` +//! # use kani::requires; //! #[requires(divisor != 0)] +//! # fn my_div(dividend: usize, divisor: usize) -> usize { +//! # dividend / divisor +//! # } //! ``` //! //! This is called a precondition, because it is enforced before (pre-) the @@ -51,7 +55,11 @@ //! approximation of the result of division for instance could be this: //! //! ``` -//! #[ensures(|result : &u32| *result <= dividend)] +//! # use kani::ensures; +//! #[ensures(|result : &usize| *result <= dividend)] +//! # fn my_div(dividend: usize, divisor: usize) -> usize { +//! # dividend / divisor +//! # } //! ``` //! //! This is called a postcondition and it also has access to the arguments and @@ -66,9 +74,11 @@ //! order does not matter. In our example putting them together looks like this: //! //! ``` -//! #[kani::requires(divisor != 0)] -//! #[kani::ensures(|result : &u32| *result <= dividend)] -//! fn my_div(dividend: u32, divisor: u32) -> u32 { +//! use kani::{requires, ensures}; +//! +//! #[requires(divisor != 0)] +//! #[ensures(|result : &usize| *result <= dividend)] +//! fn my_div(dividend: usize, divisor: usize) -> usize { //! dividend / divisor //! } //! ``` @@ -84,9 +94,18 @@ //! function we want to check. //! //! ``` +//! # use kani::{requires, ensures}; +//! # +//! # #[requires(divisor != 0)] +//! # #[ensures(|result : &usize| *result <= dividend)] +//! # fn my_div(dividend: usize, divisor: usize) -> usize { +//! # dividend / divisor +//! # } +//! # //! #[kani::proof_for_contract(my_div)] //! fn my_div_harness() { -//! my_div(kani::any(), kani::any()) } +//! my_div(kani::any(), kani::any()); +//! } //! ``` //! //! The harness is checked like any other by running `cargo kani` and can be @@ -104,10 +123,18 @@ //! the contract will be used automatically. //! //! ``` +//! # use kani::{requires, ensures}; +//! # +//! # #[requires(divisor != 0)] +//! # #[ensures(|result : &usize| *result <= dividend)] +//! # fn my_div(dividend: usize, divisor: usize) -> usize { +//! # dividend / divisor +//! # } +//! # //! #[kani::proof] //! #[kani::stub_verified(my_div)] //! fn use_div() { -//! let v = vec![...]; +//! let v = kani::vec::any_vec::(); //! let some_idx = my_div(v.len() - 1, 3); //! v[some_idx]; //! } diff --git a/library/kani/src/invariant.rs b/library/kani/src/invariant.rs index 068cdedc277e..b1bac031b677 100644 --- a/library/kani/src/invariant.rs +++ b/library/kani/src/invariant.rs @@ -39,6 +39,14 @@ /// ``` /// You can specify its safety invariant as: /// ```rust +/// # #[derive(kani::Arbitrary)] +/// # pub struct MyDate { +/// # day: u8, +/// # month: u8, +/// # year: i64, +/// # } +/// # fn days_in_month(_: i64, _: u8) -> u8 { 31 } +/// /// impl kani::Invariant for MyDate { /// fn is_safe(&self) -> bool { /// self.month > 0 @@ -49,12 +57,33 @@ /// } /// ``` /// And use it to check that your APIs are safe: -/// ```rust +/// ```no_run +/// # use kani::Invariant; +/// # +/// # #[derive(kani::Arbitrary)] +/// # pub struct MyDate { +/// # day: u8, +/// # month: u8, +/// # year: i64, +/// # } +/// # +/// # fn days_in_month(_: i64, _: u8) -> u8 { todo!() } +/// # fn increase_date(_: &mut MyDate, _: u8) { todo!() } +/// # +/// # impl Invariant for MyDate { +/// # fn is_safe(&self) -> bool { +/// # self.month > 0 +/// # && self.month <= 12 +/// # && self.day > 0 +/// # && self.day <= days_in_month(self.year, self.month) +/// # } +/// # } +/// # /// #[kani::proof] /// fn check_increase_date() { /// let mut date: MyDate = kani::any(); /// // Increase date by one day -/// increase_date(date, 1); +/// increase_date(&mut date, 1); /// assert!(date.is_safe()); /// } /// ``` diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 053af760067b..ce1eeb992121 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -19,6 +19,8 @@ #![feature(ptr_metadata)] #![feature(f16)] #![feature(f128)] +// Need to add this since we are deprecating `kani::check`. Remove this when we remove that API. +#![allow(deprecated)] // Allow us to use `kani::` to access crate features. extern crate self as kani; diff --git a/library/kani/src/shadow.rs b/library/kani/src/shadow.rs index a7ea57c6fd40..48ce8f0f9211 100644 --- a/library/kani/src/shadow.rs +++ b/library/kani/src/shadow.rs @@ -10,7 +10,7 @@ //! //! # Example //! -//! ``` +//! ```no_run //! use kani::shadow::ShadowMem; //! use std::alloc::{alloc, Layout}; //! diff --git a/library/kani/src/slice.rs b/library/kani/src/slice.rs index 1b13de29d589..c419a881fa55 100644 --- a/library/kani/src/slice.rs +++ b/library/kani/src/slice.rs @@ -9,7 +9,8 @@ use crate::{any, assume}; /// /// # Example: /// -/// ```rust +/// ```no_run +/// # fn foo(_: &[i32]) {} /// let arr = [1, 2, 3]; /// let slice = kani::slice::any_slice_of_array(&arr); /// foo(slice); // where foo is a function that takes a slice and verifies a property about it diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 5df8f2228c62..839313f084c2 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -42,6 +42,7 @@ macro_rules! kani_lib { pub use kani_core::*; kani_core::kani_intrinsics!(core); kani_core::generate_arbitrary!(core); + kani_core::check_intrinsic!(); pub mod mem { kani_core::kani_mem!(core); @@ -58,6 +59,10 @@ macro_rules! kani_lib { kani_core::kani_intrinsics!(std); kani_core::generate_arbitrary!(std); + kani_core::check_intrinsic! { + msg="This API will be made private in future releases.", vis=pub + } + pub mod mem { kani_core::kani_mem!(std); } @@ -85,7 +90,7 @@ macro_rules! kani_intrinsics { /// /// The code snippet below should never panic. /// - /// ```rust + /// ```no_run /// let i : i32 = kani::any(); /// kani::assume(i > 10); /// if i < 0 { @@ -95,7 +100,7 @@ macro_rules! kani_intrinsics { /// /// The following code may panic though: /// - /// ```rust + /// ```no_run /// let i : i32 = kani::any(); /// assert!(i < 0, "This may panic and verification should fail."); /// kani::assume(i > 10); @@ -118,7 +123,7 @@ macro_rules! kani_intrinsics { /// /// # Example: /// - /// ```rust + /// ```no_run /// let x: bool = kani::any(); /// let y = !x; /// kani::assert(x || y, "ORing a boolean variable with its negation must be true") @@ -138,35 +143,15 @@ macro_rules! kani_intrinsics { assert!(cond, "{}", msg); } - /// Creates an assertion of the specified condition and message, but does not assume it afterwards. - /// - /// # Example: - /// - /// ```rust - /// let x: bool = kani::any(); - /// let y = !x; - /// kani::check(x || y, "ORing a boolean variable with its negation must be true") - /// ``` - #[cfg(not(feature = "concrete_playback"))] - #[inline(never)] - #[rustc_diagnostic_item = "KaniCheck"] - pub const fn check(cond: bool, msg: &'static str) { - let _ = cond; - let _ = msg; - } - - #[cfg(feature = "concrete_playback")] - #[inline(never)] - #[rustc_diagnostic_item = "KaniCheck"] - pub const fn check(cond: bool, msg: &'static str) { - assert!(cond, "{}", msg); - } - /// Creates a cover property with the specified condition and message. /// /// # Example: /// - /// ```rust + /// ```no_run + /// # use crate::kani; + /// # + /// # let array: [u8; 10] = kani::any(); + /// # let slice = kani::slice::any_slice_of_array(&array); /// kani::cover(slice.len() == 0, "The slice may have a length of 0"); /// ``` /// @@ -193,7 +178,11 @@ macro_rules! kani_intrinsics { /// In the snippet below, we are verifying the behavior of the function `fn_under_verification` /// under all possible `NonZeroU8` input values, i.e., all possible `u8` values except zero. /// - /// ```rust + /// ```no_run + /// # use std::num::NonZeroU8; + /// # use crate::kani; + /// # + /// # fn fn_under_verification(_: NonZeroU8) {} /// let inputA = kani::any::(); /// fn_under_verification(inputA); /// ``` @@ -231,7 +220,11 @@ macro_rules! kani_intrinsics { /// In the snippet below, we are verifying the behavior of the function `fn_under_verification` /// under all possible `u8` input values between 0 and 12. /// - /// ```rust + /// ```no_run + /// # use std::num::NonZeroU8; + /// # use crate::kani; + /// # + /// # fn fn_under_verification(_: u8) {} /// let inputA: u8 = kani::any_where(|x| *x < 12); /// fn_under_verification(inputA); /// ``` @@ -460,3 +453,48 @@ macro_rules! kani_intrinsics { } }; } + +#[macro_export] +macro_rules! check_intrinsic { + ($(msg=$msg:literal, vis=$vis:vis)?) => { + /// Creates a non-fatal property with the specified condition and message. + /// + /// This check will not impact the program control flow even when it fails. + /// + /// # Example: + /// + /// ```no_run + /// let x: bool = kani::any(); + /// let y = !x; + /// kani::check(x || y, "ORing a boolean variable with its negation must be true"); + /// kani::check(x == y, "A boolean variable is always different than its negation"); + /// kani::cover!(true, "This should still be reachable"); + /// ``` + /// + /// # Deprecated + /// + /// This function was meant to be internal only, and it was added to Kani's public interface + /// by mistake. Thus, it will be made private in future releases. + /// Instead, we recommend users to either use `assert` or `cover` to create properties they + /// would like to verify. + /// + /// See for more details. + #[cfg(not(feature = "concrete_playback"))] + #[inline(never)] + #[rustc_diagnostic_item = "KaniCheck"] + // TODO: Remove the `#![allow(deprecated)]` inside kani's crate once this is made private. + $(#[deprecated(since="0.55.0", note=$msg)])? + $($vis)? const fn check(cond: bool, msg: &'static str) { + let _ = cond; + let _ = msg; + } + + #[cfg(feature = "concrete_playback")] + #[inline(never)] + #[rustc_diagnostic_item = "KaniCheck"] + $(#[deprecated(since="0.55.0", note=$msg)])? + $($vis)? const fn check(cond: bool, msg: &'static str) { + assert!(cond, "{}", msg); + } + }; +} diff --git a/scripts/kani-regression.sh b/scripts/kani-regression.sh index bd6b04d7386e..be2548235ce6 100755 --- a/scripts/kani-regression.sh +++ b/scripts/kani-regression.sh @@ -41,8 +41,8 @@ cargo test -p cprover_bindings cargo test -p kani-compiler cargo test -p kani-driver cargo test -p kani_metadata -# skip doc tests and enable assertions to fail -cargo test -p kani --lib --features concrete_playback +# Use concrete playback to enable assertions failure +cargo test -p kani --features concrete_playback # Test the actual macros, skipping doc tests and enabling extra traits for "syn" # so we can debug print AST RUSTFLAGS=--cfg=kani_sysroot cargo test -p kani_macros --features syn/extra-traits --lib diff --git a/tests/ui/check_deprecated/deprecated_warning.expected b/tests/ui/check_deprecated/deprecated_warning.expected new file mode 100644 index 000000000000..3a0760035f8b --- /dev/null +++ b/tests/ui/check_deprecated/deprecated_warning.expected @@ -0,0 +1 @@ +warning: use of deprecated function `kani::check`: This API will be made private in future releases. diff --git a/tests/ui/check_deprecated/deprecated_warning.rs b/tests/ui/check_deprecated/deprecated_warning.rs new file mode 100644 index 000000000000..f3d791256081 --- /dev/null +++ b/tests/ui/check_deprecated/deprecated_warning.rs @@ -0,0 +1,10 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// kani-flags: --only-codegen + +/// Ensure that Kani prints a deprecation warning if users invoke `kani::check`. +#[kani::proof] +fn internal_api() { + kani::check(kani::any(), "oops"); +}