From 0e49d50f8e9d9e5e311dff9929b85f1d1a89773e Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Fri, 19 Apr 2024 01:19:30 -0500 Subject: [PATCH 01/52] Loop Contracts Annotation for While-Loop --- library/kani/src/lib.rs | 19 +++++ library/kani_macros/src/lib.rs | 84 +++++++++++++++++++ .../kani_macros/src/sysroot/contracts/mod.rs | 2 +- 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 25b1b389a2b1..8161ad7f052b 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -140,6 +140,25 @@ pub const fn assert(cond: bool, msg: &'static str) { #[rustc_diagnostic_item = "KaniCover"] pub const fn cover(_cond: bool, _msg: &'static str) {} +/// This function is only used for loop contract annotation. +/// It behaves as a placeholder to telling us where is the loop head and loop end +/// that we want to annotate to. +#[inline(never)] +#[rustc_diagnostic_item = "KaniLoopInvariant"] +pub const fn kani_loop_invariant(_cond: bool) {} + +/// This function is only used for loop contract annotation. +/// It behaves as a placeholder to telling us where the loop invariants stmts begin. +#[inline(never)] +#[rustc_diagnostic_item = "KaniLoopInvariantBegin"] +pub const fn kani_loop_invariant_begin() {} + +/// This function is only used for loop contract annotation. +/// It behaves as a placeholder to telling us where the loop invariants stmts end. +#[inline(never)] +#[rustc_diagnostic_item = "KaniLoopInvariantEnd"] +pub const fn kani_loop_invariant_end() {} + /// This creates an symbolic *valid* value of type `T`. You can assign the return value of this /// function to a variable that you want to make symbolic. /// diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 0991730f8802..7b51a94fa824 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -15,11 +15,15 @@ mod derive; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; +use proc_macro2::TokenStream as TokenStream2; + #[cfg(kani_sysroot)] use sysroot as attr_impl; +use proc_macro2::{Ident, Span}; #[cfg(not(kani_sysroot))] use regular as attr_impl; +use syn::{Expr, Stmt}; /// Marks a Kani proof harness /// @@ -198,6 +202,13 @@ pub fn modifies(attr: TokenStream, item: TokenStream) -> TokenStream { attr_impl::modifies(attr, item) } +#[proc_macro_attribute] +pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { + attr_impl::loop_invariant(attr, item) +} + +static mut LOOP_INVARIANT_COUNT: u32 = 0; + /// This module implements Kani attributes in a way that only Kani's compiler can understand. /// This code should only be activated when pre-building Kani's sysroot. #[cfg(kani_sysroot)] @@ -206,6 +217,7 @@ mod sysroot { mod contracts; + use contracts::helpers::*; pub use contracts::{ensures, modifies, proof_for_contract, requires, stub_verified}; use super::*; @@ -346,6 +358,77 @@ mod sysroot { kani_attribute!(stub); kani_attribute!(unstable); kani_attribute!(unwind); + + pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { + // Update the loop counter to distinguish loop invariants for different loops. + unsafe { + LOOP_INVARIANT_COUNT += 1; + } + + let mut loop_stmt: Stmt = syn::parse(item.clone()).unwrap(); + // Parse the loop invariant expression. + let inv_expr: Expr = chunks_by(TokenStream2::from(attr.clone()), is_token_stream_2_comma) + .map(syn::parse2::) + .filter_map(|expr| match expr { + Err(_) => None, + Ok(expr) => Some(expr), + }) + .collect::>()[0] + .clone(); + + // Annotate a place holder function call + // kani::kani_loop_invariant() + // at the end of the loop. + match loop_stmt { + Stmt::Expr(ref mut e, _) => match e { + Expr::While(ref mut ew) => { + // A while loop of the form + // ``` rust + // while guard { + // body + // } + // ``` + // is annotated as + // ``` rust + // kani::kani_loop_invariant_begin(); + // let __kani_loop_invariant_1 = ||(inv); + // kani::kani_loop_invariant_end(); + // while guard{ + // body + // kani::kani_loop_invariant(true); + // } + // ``` + let end_stmt: Stmt = syn::parse( + quote!( + kani::kani_loop_invariant(true);) + .into(), + ) + .unwrap(); + ew.body.stmts.push(end_stmt); + } + _ => (), + }, + _ => todo!("implement support for loops other than while loops."), + } + + // instrument the invariants as a closure, + // and the call to the closure between two placeholders + // TODO: all variables in inv_expr should be reference. + unsafe { + let closre_name = Ident::new( + &format!("__kani_loop_invariant_{LOOP_INVARIANT_COUNT}"), + Span::call_site(), + ); + quote!( + let #closre_name = ||(#inv_expr); + kani::kani_loop_invariant_begin(); + #closre_name(); + kani::kani_loop_invariant_end(); + #loop_stmt + ) + .into() + } + } } /// This module provides dummy implementations of Kani attributes which cannot be interpreted by @@ -384,4 +467,5 @@ mod regular { no_op!(modifies); no_op!(proof_for_contract); no_op!(stub_verified); + no_op!(loop_invariant); } diff --git a/library/kani_macros/src/sysroot/contracts/mod.rs b/library/kani_macros/src/sysroot/contracts/mod.rs index 02d2b98eb8db..a9fad0afda2d 100644 --- a/library/kani_macros/src/sysroot/contracts/mod.rs +++ b/library/kani_macros/src/sysroot/contracts/mod.rs @@ -238,7 +238,7 @@ use syn::{parse_macro_input, Expr, ItemFn}; mod bootstrap; mod check; -mod helpers; +pub mod helpers; mod initialize; mod replace; mod shared; From a5cc42c53a9240aba1ca0c2ef15b9cba43ee4b0f Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 17 Apr 2024 14:01:05 -0700 Subject: [PATCH 02/52] Bump dependencies and Kani's version to 0.50.0 (#3148) Release notes are the following: ### Major Changes * Fix compilation issue with proc_macro2 (v1.0.80+) and Kani v0.49.0 (https://github.com/model-checking/kani/issues/3138). ### What's Changed * Implement valid value check for `write_bytes` by @celinval in https://github.com/model-checking/kani/pull/3108 * Rust toolchain upgraded to 2024-04-15 by @tautschnig @celinval **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.49.0...kani-0.50.0 --- CHANGELOG.md | 14 ++++ Cargo.lock | 131 +++++++++++++++++---------------- Cargo.toml | 2 +- cprover_bindings/Cargo.toml | 2 +- kani-compiler/Cargo.toml | 2 +- kani-driver/Cargo.toml | 2 +- kani_metadata/Cargo.toml | 2 +- library/kani/Cargo.toml | 2 +- library/kani_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 92 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd592b2f27a7..61e06601dfc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ This file contains notable changes (e.g. breaking changes, major changes, etc.) This file was introduced starting Kani 0.23.0, so it only contains changes from version 0.23.0 onwards. +## [0.50.0] + +### Major Changes +* Fix compilation issue with proc_macro2 (v1.0.80+) and Kani v0.49.0 +(https://github.com/model-checking/kani/issues/3138). + +### What's Changed +* Implement valid value check for `write_bytes` by @celinval in +https://github.com/model-checking/kani/pull/3108 +* Rust toolchain upgraded to 2024-04-15 by @tautschnig @celinval + +**Full Changelog**: +https://github.com/model-checking/kani/compare/kani-0.49.0...kani-0.50.0 + ## [0.49.0] ### What's Changed diff --git a/Cargo.lock b/Cargo.lock index ae711d863430..821dbd9c346c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "autocfg" @@ -97,7 +97,7 @@ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "build-kani" -version = "0.49.0" +version = "0.50.0" dependencies = [ "anyhow", "cargo_metadata", @@ -174,7 +174,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.49.0" +version = "0.50.0" dependencies = [ "lazy_static", "linear-map", @@ -295,9 +295,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encode_unicode" @@ -338,9 +338,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -410,14 +410,14 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kani" -version = "0.49.0" +version = "0.50.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.49.0" +version = "0.50.0" dependencies = [ "clap", "cprover_bindings", @@ -438,7 +438,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.49.0" +version = "0.50.0" dependencies = [ "anyhow", "cargo_metadata", @@ -466,7 +466,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.49.0" +version = "0.50.0" dependencies = [ "anyhow", "home", @@ -475,17 +475,17 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.49.0" +version = "0.50.0" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] name = "kani_metadata" -version = "0.49.0" +version = "0.50.0" dependencies = [ "clap", "cprover_bindings", @@ -571,9 +571,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" dependencies = [ "num-bigint", "num-complex", @@ -733,18 +733,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -909,29 +909,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -992,7 +992,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "std" -version = "0.49.0" +version = "0.50.0" dependencies = [ "kani", ] @@ -1030,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1045,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" dependencies = [ "proc-macro2", "quote", @@ -1083,7 +1083,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1149,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1315,7 +1315,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1335,17 +1335,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1356,9 +1357,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -1368,9 +1369,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -1380,9 +1381,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -1392,9 +1399,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -1404,9 +1411,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -1416,9 +1423,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -1428,15 +1435,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] @@ -1464,5 +1471,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] diff --git a/Cargo.toml b/Cargo.toml index e271a3650c03..93affb02856f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.49.0" +version = "0.50.0" edition = "2021" description = "A bit-precise model checker for Rust." readme = "README.md" diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index ed0e57847e71..d199558ff16a 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index ffc508e90866..244172715056 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 3a476922a838..ab83c2202bc9 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.49.0" +version = "0.50.0" edition = "2021" description = "Build a project with Kani and run all proof harnesses" license = "MIT OR Apache-2.0" diff --git a/kani_metadata/Cargo.toml b/kani_metadata/Cargo.toml index 582c20c7bd9f..7936d943556b 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index b87536740fcd..97952dd7ab9e 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index fe4279fc4366..a54fb44d8b6c 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 05f5b4de5635..6f9a380fc584 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -5,7 +5,7 @@ # Note: this package is intentionally named std to make sure the names of # standard library symbols are preserved name = "std" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/tools/build-kani/Cargo.toml b/tools/build-kani/Cargo.toml index 525c060232a7..eabb396e0923 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.49.0" +version = "0.50.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From 537eefc45a2512991fef1e00e6b92402596c55c6 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 18 Apr 2024 16:11:21 -0700 Subject: [PATCH 03/52] Upgrade toolchain to 2024-04-18 and improve toolchain workflow (#3149) The toolchain upgrade itself didn't require any modification, but it looks like the rust toolchain script includes any untracked files to the PR, which is the root cause of the #3146 CI failure. Thus, I made the following changes (each one of them in its own commit): 1. Moved the upgrade step to its own script. 2. Added a bit of debugging and doc to the script. 3. Added a new step that cleans the workspace before the PR creation. 4. Actually update the toolchain. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/workflows/toolchain-upgrade.yml | 41 ++-------------- .gitignore | 4 ++ rust-toolchain.toml | 2 +- scripts/toolchain_update.sh | 65 +++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 37 deletions(-) create mode 100755 scripts/toolchain_update.sh diff --git a/.github/workflows/toolchain-upgrade.yml b/.github/workflows/toolchain-upgrade.yml index 8252bfb826e6..a4b95ea195f0 100644 --- a/.github/workflows/toolchain-upgrade.yml +++ b/.github/workflows/toolchain-upgrade.yml @@ -30,42 +30,11 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - current_toolchain_date=$(grep ^channel rust-toolchain.toml | sed 's/.*nightly-\(.*\)"/\1/') - echo "current_toolchain_date=$current_toolchain_date" >> $GITHUB_ENV - current_toolchain_epoch=$(date --date $current_toolchain_date +%s) - next_toolchain_date=$(date --date "@$(($current_toolchain_epoch + 86400))" +%Y-%m-%d) - echo "next_toolchain_date=$next_toolchain_date" >> $GITHUB_ENV - if gh issue list -S \ - "Toolchain upgrade to nightly-$next_toolchain_date failed" \ - --json number,title | grep title ; then - echo "next_step=none" >> $GITHUB_ENV - elif ! git ls-remote --exit-code origin toolchain-$next_toolchain_date ; then - echo "next_step=create_pr" >> $GITHUB_ENV - sed -i "/^channel/ s/$current_toolchain_date/$next_toolchain_date/" rust-toolchain.toml - git diff - git clone --filter=tree:0 https://github.com/rust-lang/rust rust.git - cd rust.git - current_toolchain_hash=$(curl https://static.rust-lang.org/dist/$current_toolchain_date/channel-rust-nightly-git-commit-hash.txt) - echo "current_toolchain_hash=$current_toolchain_hash" >> $GITHUB_ENV - next_toolchain_hash=$(curl https://static.rust-lang.org/dist/$next_toolchain_date/channel-rust-nightly-git-commit-hash.txt) - echo "next_toolchain_hash=$next_toolchain_hash" >> $GITHUB_ENV - EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) - echo "git_log<<$EOF" >> $GITHUB_ENV - git log --oneline $current_toolchain_hash..$next_toolchain_hash | \ - sed 's#^#https://github.com/rust-lang/rust/commit/#' >> $GITHUB_ENV - echo "$EOF" >> $GITHUB_ENV - cd .. - rm -rf rust.git - if ! cargo build-dev ; then - echo "next_step=create_issue" >> $GITHUB_ENV - else - if ! ./scripts/kani-regression.sh ; then - echo "next_step=create_issue" >> $GITHUB_ENV - fi - fi - else - echo "next_step=none" >> $GITHUB_ENV - fi + source scripts/toolchain_update.sh + + - name: Clean untracked files + run: git clean -f + - name: Create Pull Request if: ${{ env.next_step == 'create_pr' }} uses: peter-evans/create-pull-request@v6 diff --git a/.gitignore b/.gitignore index aae8f479aac9..a2defc0df119 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,10 @@ no_llvm_build /tmp/ # Created by default with `src/ci/docker/run.sh` /obj/ +# Created by kani-compiler +*.rlib +*.rmeta +*.mir ## Temporary files *~ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index be42da187e10..2d65b1576d5f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-15" +channel = "nightly-2024-04-18" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/scripts/toolchain_update.sh b/scripts/toolchain_update.sh new file mode 100755 index 000000000000..856c346e542c --- /dev/null +++ b/scripts/toolchain_update.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# This script is part of our CI nightly job to bump the toolchain version. +# It will potentially update the rust-toolchain.toml file, and run the +# regression. +# +# In order to manually run this script, you will need to do the following: +# +# 1. Set $GITHUB_ENV to point to an output file. +# 2. Install and configure GitHub CLI + +set -eu + +current_toolchain_date=$(grep ^channel rust-toolchain.toml | sed 's/.*nightly-\(.*\)"/\1/') +echo "current_toolchain_date=$current_toolchain_date" >> $GITHUB_ENV + +current_toolchain_epoch=$(date --date $current_toolchain_date +%s) +next_toolchain_date=$(date --date "@$(($current_toolchain_epoch + 86400))" +%Y-%m-%d) +echo "next_toolchain_date=$next_toolchain_date" >> $GITHUB_ENV + +echo "------ Start upgrade ------" +echo "- current: ${current_toolchain_date}" +echo "- next: ${next_toolchain_date}" +echo "---------------------------" + +if gh issue list -S \ + "Toolchain upgrade to nightly-$next_toolchain_date failed" \ + --json number,title | grep title +then + echo "Skip update: Found existing issue" + echo "next_step=none" >> $GITHUB_ENV +elif ! git ls-remote --exit-code origin toolchain-$next_toolchain_date +then + echo "next_step=create_pr" >> $GITHUB_ENV + + # Modify rust-toolchain file + sed -i "/^channel/ s/$current_toolchain_date/$next_toolchain_date/" rust-toolchain.toml + + git diff + git clone --filter=tree:0 https://github.com/rust-lang/rust rust.git + cd rust.git + current_toolchain_hash=$(curl https://static.rust-lang.org/dist/$current_toolchain_date/channel-rust-nightly-git-commit-hash.txt) + echo "current_toolchain_hash=$current_toolchain_hash" >> $GITHUB_ENV + + next_toolchain_hash=$(curl https://static.rust-lang.org/dist/$next_toolchain_date/channel-rust-nightly-git-commit-hash.txt) + echo "next_toolchain_hash=$next_toolchain_hash" >> $GITHUB_ENV + + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "git_log<<$EOF" >> $GITHUB_ENV + + git log --oneline $current_toolchain_hash..$next_toolchain_hash | \ + sed 's#^#https://github.com/rust-lang/rust/commit/#' >> $GITHUB_ENV + echo "$EOF" >> $GITHUB_ENV + + cd .. + rm -rf rust.git + if ! ./scripts/kani-regression.sh ; then + echo "next_step=create_issue" >> $GITHUB_ENV + fi +else + echo "Skip update: Found existing branch" + echo "next_step=none" >> $GITHUB_ENV +fi From 4806dac9dbb22639042a85dde9b057aef7578d5f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 09:47:32 +0200 Subject: [PATCH 04/52] Automatic toolchain upgrade to nightly-2024-04-19 (#3150) Update Rust toolchain from nightly-2024-04-18 to nightly-2024-04-19 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/becebb3158149a115cad8a402612e25436a7e37b up to https://github.com/rust-lang/rust/commit/e3181b091e88321f5ea149afed6db0edf0a4f37b. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2d65b1576d5f..b1cbf7169fea 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-18" +channel = "nightly-2024-04-19" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 55e5f0da0d43477369b97baaa837b8a616a880a3 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 19 Apr 2024 13:41:28 -0700 Subject: [PATCH 05/52] Stabilize cover statement and update contracts RFC (#3091) I would like to propose that we stabilize the cover statement as is. Any further improvements or changes can be done separately, with or without an RFC. I am also updating the contracts RFC status since parts of it have been integrated to Kani, but it is still unstable. ### Call-out This PR requires at least 2 approvals. --- library/kani/src/mem.rs | 2 +- rfc/src/rfcs/0003-cover-statement.md | 12 ++++++++---- rfc/src/rfcs/0009-function-contracts.md | 7 ++++--- scripts/build-docs.sh | 4 +++- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index 203eae2229c4..43062ebed6a1 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -44,7 +44,7 @@ use std::ptr::{DynMetadata, NonNull, Pointee}; /// Note that an unaligned pointer is still considered valid. /// /// TODO: Kani should automatically add those checks when a de-reference happens. -/// https://github.com/model-checking/kani/issues/2975 +/// /// /// This function will either panic or return `true`. This is to make it easier to use it in /// contracts. diff --git a/rfc/src/rfcs/0003-cover-statement.md b/rfc/src/rfcs/0003-cover-statement.md index 6839af22f929..de452654392b 100644 --- a/rfc/src/rfcs/0003-cover-statement.md +++ b/rfc/src/rfcs/0003-cover-statement.md @@ -1,8 +1,8 @@ - **Feature Name:** Cover statement (`cover-statement`) - **Feature Request Issue:** - **RFC PR:** -- **Status:** Unstable -- **Version:** 1 +- **Status:** Stable +- **Version:** 2 ------------------- @@ -138,8 +138,12 @@ However, if the condition can indeed be covered, verification would fail due to ## Open questions -Should we allow format arguments in the macro, e.g. `kani::cover!(x > y, "{} can be greater than {}", x, y)`? -Users may expect this to be supported since the macro looks similar to the `assert` macro, but Kani doesn't include the formatted string in the message description, since it's not available at compile time. +- ~Should we allow format arguments in the macro, e.g. `kani::cover!(x > y, "{} can be greater than {}", x, y)`? +Users may expect this to be supported since the macro looks similar to the `assert` macro, but Kani doesn't include the formatted string in the message description, since it's not available at compile time.~ + - For now, this macro will not accept format arguments, since this + is not well handled by Kani. + This is an extesion to this API that can be easily added later on if Kani + ever supports runtime formatting. ## Other Considerations diff --git a/rfc/src/rfcs/0009-function-contracts.md b/rfc/src/rfcs/0009-function-contracts.md index e26592080822..dae805a7db72 100644 --- a/rfc/src/rfcs/0009-function-contracts.md +++ b/rfc/src/rfcs/0009-function-contracts.md @@ -1,8 +1,8 @@ - **Feature Name:** Function Contracts - **Feature Request Issue:** [#2652](https://github.com/model-checking/kani/issues/2652) and [Milestone](https://github.com/model-checking/kani/milestone/31) - **RFC PR:** [#2620](https://github.com/model-checking/kani/pull/2620) -- **Status:** Under Review -- **Version:** 0 +- **Status:** Unstable +- **Version:** 1 - **Proof-of-concept:** [features/contracts](https://github.com/model-checking/kani/tree/features/contracts) - **Feature Gate:** `-Zfunction-contracts`, enforced by compile time error[^gate] @@ -893,4 +893,5 @@ times larger than what they expect the function will touch). [^stubcheck]: Kani cannot report the occurrence of a contract function to check in abstracted functions as errors, because the mechanism is needed to verify - mutually recursive functions. \ No newline at end of file + mutually recursive functions. + diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh index cd40a2edabad..2e2c10b052f6 100755 --- a/scripts/build-docs.sh +++ b/scripts/build-docs.sh @@ -28,8 +28,10 @@ else curl -sSL -o "$FILE" "$URL" echo "$EXPECTED_HASH $FILE" | sha256sum -c - tar zxf $FILE + MDBOOK=${SCRIPT_DIR}/mdbook + else + MDBOOK=mdbook fi - MDBOOK=${SCRIPT_DIR}/mdbook fi KANI_DIR=$SCRIPT_DIR/.. From 0e6c192fea90a79464c4addecaa3a9a2f7e72cf6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:51:45 +0200 Subject: [PATCH 06/52] Automatic toolchain upgrade to nightly-2024-04-20 (#3154) Update Rust toolchain from nightly-2024-04-19 to nightly-2024-04-20 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/e3181b091e88321f5ea149afed6db0edf0a4f37b up to https://github.com/rust-lang/rust/commit/f9b16149208c8a8a349c32813312716f6603eb6f. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b1cbf7169fea..f4634f5b7b43 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-19" +channel = "nightly-2024-04-20" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From dd6e60f73f7f67ea6dfcceb3ba6c3fd8df1808b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:24:51 +0000 Subject: [PATCH 07/52] Bump tests/perf/s2n-quic from `2d5e891` to `5f88e54` (#3140) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `2d5e891` to `5f88e54`. --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 2d5e891f3fdc..5f88e5498215 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 2d5e891f3fdc8a88b2d457baceedea5751efaa0d +Subproject commit 5f88e549821518e71b550faf353a8b9970a29deb From a542edef4c710b183fb65d5dfb5f80205eeef1e9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:07:41 +0200 Subject: [PATCH 08/52] Automatic cargo update to 2024-04-22 (#3157) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 821dbd9c346c..6f45be0e419e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,7 +174,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -480,7 +480,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -860,9 +860,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.5.0", "errno", @@ -924,7 +924,7 @@ checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1030,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1045,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.59" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -1068,22 +1068,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1119,9 +1119,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", "serde", @@ -1149,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1471,5 +1471,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] From 3cf110c98343b463f078e4543bedd846d7b9abbd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:39:21 +0000 Subject: [PATCH 09/52] Automatic toolchain upgrade to nightly-2024-04-21 (#3158) Update Rust toolchain from nightly-2024-04-20 to nightly-2024-04-21 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/f9b16149208c8a8a349c32813312716f6603eb6f up to https://github.com/rust-lang/rust/commit/dbce3b43b6cb34dd3ba12c3ec6f708fe68e9c3df. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f4634f5b7b43..70848743b686 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-20" +channel = "nightly-2024-04-21" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 4be4214fc63a9e5974cb7818252cdb284b3eafc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:28:02 -0700 Subject: [PATCH 10/52] Bump tests/perf/s2n-quic from `5f88e54` to `9730578` (#3159) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `5f88e54` to `9730578`.
Commits
  • 9730578 chore: release 1.37.0 (#2187)
  • b862ad9 s2n-quic-dc: initial commit (#2185)
  • e0f224b feat(s2n-quic-core): allow forced PTO transmissions (#2130)
  • bfb921d feat(s2n-quic-core): Add ability to create an incremental reader initialized ...
  • 23b07e4 feat(s2n-quic): allow disabling active connection migration support (#2182)
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 5f88e5498215..9730578c0d56 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 5f88e549821518e71b550faf353a8b9970a29deb +Subproject commit 9730578c0d562d80bbbe663161b3a5408ed3116c From e5b0a2a4635cfabaf7d56e899bdddc5b6f3fabcd Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Mon, 22 Apr 2024 19:45:21 -0400 Subject: [PATCH 11/52] Fix cargo audit error (#3160) Fixes cargo audit CI job by updating `rustix`. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f45be0e419e..627755fd91b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -860,9 +860,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", From b244770c4043f768ac2e9e9f2d955b8d96441187 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 23 Apr 2024 02:31:55 +0200 Subject: [PATCH 12/52] Fix cbmc-update CI job (#3156) We had a spurious update attempt logged in #3155 for the job prior to this fix would empty out the version strings. This was caused by use of undefined variables. Resolves: #3155 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. Co-authored-by: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- .github/workflows/cbmc-update.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cbmc-update.yml b/.github/workflows/cbmc-update.yml index 6f9a6ae3a74b..5fe8a866c0e4 100644 --- a/.github/workflows/cbmc-update.yml +++ b/.github/workflows/cbmc-update.yml @@ -30,7 +30,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - grep ^CBMC_VERSION kani-dependencies >> $GITHUB_ENV + grep ^CBMC_VERSION kani-dependencies | sed 's/"//g' >> $GITHUB_ENV CBMC_LATEST=$(gh -R diffblue/cbmc release list | grep Latest | awk '{print $1}' | cut -f2 -d-) echo "CBMC_LATEST=$CBMC_LATEST" >> $GITHUB_ENV # check whether the version has changed at all @@ -47,8 +47,8 @@ jobs: elif ! git ls-remote --exit-code origin cbmc-$CBMC_LATEST ; then CBMC_LATEST_MAJOR=$(echo $CBMC_LATEST | cut -f1 -d.) CBMC_LATEST_MINOR=$(echo $CBMC_LATEST | cut -f2 -d.) - sed -i "s/^CBMC_MAJOR=.*/CBMC_MAJOR=\"$CBMC_MAJOR\"/" kani-dependencies - sed -i "s/^CBMC_MINOR=.*/CBMC_MINOR=\"$CBMC_MINOR\"/" kani-dependencies + sed -i "s/^CBMC_MAJOR=.*/CBMC_MAJOR=\"$CBMC_LATEST_MAJOR\"/" kani-dependencies + sed -i "s/^CBMC_MINOR=.*/CBMC_MINOR=\"$CBMC_LATEST_MINOR\"/" kani-dependencies sed -i "s/^CBMC_VERSION=.*/CBMC_VERSION=\"$CBMC_LATEST\"/" kani-dependencies git diff if ! ./scripts/kani-regression.sh ; then From ec29ffdba0f13c3a3309d5a82ada9262a18fd432 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Apr 2024 23:24:37 -0700 Subject: [PATCH 13/52] Automatic cargo update to 2024-04-29 (#3165) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 133 ++++++++++++++--------------------------------------- 1 file changed, 35 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 627755fd91b3..62f2c913238d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,12 +83,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.5.0" @@ -277,7 +271,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags", "crossterm_winapi", "libc", "parking_lot", @@ -323,9 +317,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "getopts" @@ -355,9 +349,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", ] @@ -524,9 +518,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -668,9 +662,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -678,15 +672,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -801,11 +795,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -864,7 +858,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -909,18 +903,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", @@ -1213,9 +1207,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "unsafe-libyaml" @@ -1296,11 +1290,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys", ] [[package]] @@ -1315,22 +1309,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -1339,46 +1318,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -1391,48 +1352,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" @@ -1441,9 +1378,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ "memchr", ] From 1371195e0238b38e2d10351bd192ee61ef5ce2aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:20:41 -0400 Subject: [PATCH 14/52] Bump tests/perf/s2n-quic from `9730578` to `1436af7` (#3166) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `9730578` to `1436af7`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 9730578c0d56..1436af712b6e 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 9730578c0d562d80bbbe663161b3a5408ed3116c +Subproject commit 1436af712b6e73edc11640dc7c3cae23e456c0a8 From 5d64679463ec74df0415c1fcf701b098fd5af96e Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 30 Apr 2024 22:35:51 +0200 Subject: [PATCH 15/52] Do not assume that ZST-typed symbols refer to unique objects (#3134) The Rust specification does not guarantee that ZST-typed symbols are backed by unique objects, and `rustc` appears to make use of this as can be demonstrated for both locals and statics. For parameters no such example has been found, but as there remains a lack of a guarantee we err on the safe side. Resolves: #3129 --- .../codegen/intrinsic.rs | 32 ++++++++++----- .../codegen_cprover_gotoc/codegen/place.rs | 39 +++++++++++++++++-- library/kani/src/mem.rs | 33 ++++------------ .../function-contract/valid_ptr.expected | 2 +- tests/expected/zst/expected | 4 ++ tests/expected/zst/main.rs | 19 +++++++++ tests/kani/Closure/zst_param.rs | 3 +- tests/kani/MemPredicates/thin_ptr_validity.rs | 2 +- 8 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 tests/expected/zst/expected create mode 100644 tests/expected/zst/main.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 8a61d46ed518..4d201a6f8cc4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -1221,7 +1221,9 @@ impl<'tcx> GotocCtx<'tcx> { // `raw_eq` determines whether the raw bytes of two values are equal. // https://doc.rust-lang.org/core/intrinsics/fn.raw_eq.html // - // The implementation below calls `memcmp` and returns equal if the result is zero. + // The implementation below calls `memcmp` and returns equal if the result is zero, and + // immediately returns zero when ZSTs are compared to mimic what compare_bytes and our memcmp + // hook do. // // TODO: It's UB to call `raw_eq` if any of the bytes in the first or second // arguments are uninitialized. At present, we cannot detect if there is @@ -1240,13 +1242,17 @@ impl<'tcx> GotocCtx<'tcx> { let dst = fargs.remove(0).cast_to(Type::void_pointer()); let val = fargs.remove(0).cast_to(Type::void_pointer()); let layout = self.layout_of_stable(ty); - let sz = Expr::int_constant(layout.size.bytes(), Type::size_t()) - .with_size_of_annotation(self.codegen_ty_stable(ty)); - let e = BuiltinFn::Memcmp - .call(vec![dst, val, sz], loc) - .eq(Type::c_int().zero()) - .cast_to(Type::c_bool()); - self.codegen_expr_to_place_stable(p, e) + if layout.size.bytes() == 0 { + self.codegen_expr_to_place_stable(p, Expr::int_constant(1, Type::c_bool())) + } else { + let sz = Expr::int_constant(layout.size.bytes(), Type::size_t()) + .with_size_of_annotation(self.codegen_ty_stable(ty)); + let e = BuiltinFn::Memcmp + .call(vec![dst, val, sz], loc) + .eq(Type::c_int().zero()) + .cast_to(Type::c_bool()); + self.codegen_expr_to_place_stable(p, e) + } } // This is an operation that is primarily relevant for stacked borrow @@ -1856,8 +1862,14 @@ impl<'tcx> GotocCtx<'tcx> { "`dst` must be properly aligned", loc, ); - let expr = dst.dereference().assign(src, loc); - Stmt::block(vec![align_check, expr], loc) + let deref = dst.dereference(); + if deref.typ().sizeof(&self.symbol_table) == 0 { + // do not attempt to dereference (and assign) a ZST + align_check + } else { + let expr = deref.assign(src, loc); + Stmt::block(vec![align_check, expr], loc) + } } /// Sets `count * size_of::()` bytes of memory starting at `dst` to `val` diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index d24e5448c595..56b7da2e7628 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -11,7 +11,7 @@ use crate::codegen_cprover_gotoc::codegen::typ::std_pointee_type; use crate::codegen_cprover_gotoc::utils::{dynamic_fat_ptr, slice_fat_ptr}; use crate::codegen_cprover_gotoc::GotocCtx; use crate::unwrap_or_return_codegen_unimplemented; -use cbmc::goto_program::{Expr, Location, Type}; +use cbmc::goto_program::{Expr, ExprValue, Location, Stmt, Type}; use rustc_middle::ty::layout::LayoutOf; use rustc_smir::rustc_internal; use rustc_target::abi::{TagEncoding, Variants}; @@ -636,6 +636,14 @@ impl<'tcx> GotocCtx<'tcx> { } } + fn is_zst_object(&self, expr: &Expr) -> bool { + match expr.value() { + ExprValue::Symbol { .. } => expr.typ().sizeof(&self.symbol_table) == 0, + ExprValue::Member { lhs, .. } => self.is_zst_object(lhs), + _ => false, + } + } + /// Codegen the reference to a given place. /// We currently have a somewhat weird way of handling ZST. /// - For `*(&T)` where `T: Unsized`, the projection's `goto_expr` is a thin pointer, so we @@ -647,8 +655,33 @@ impl<'tcx> GotocCtx<'tcx> { let projection = unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(place)); if self.use_thin_pointer_stable(place_ty) { - // Just return the address of the place dereferenced. - projection.goto_expr.address_of() + // For ZST objects rustc does not necessarily generate any actual objects. + let need_not_be_an_object = self.is_zst_object(&projection.goto_expr); + let address_of = projection.goto_expr.clone().address_of(); + if need_not_be_an_object { + // Create a non-deterministic numeric value, assume it is non-zero and (when + // interpreted as an address) of proper alignment for the type, and cast that + // numeric value to a pointer type. + let loc = projection.goto_expr.location(); + let (var, decl) = + self.decl_temp_variable(Type::size_t(), Some(Type::size_t().nondet()), *loc); + let assume_non_zero = + Stmt::assume(var.clone().neq(Expr::int_constant(0, var.typ().clone())), *loc); + let layout = self.layout_of_stable(place_ty); + let alignment = Expr::int_constant(layout.align.abi.bytes(), var.typ().clone()); + let assume_aligned = Stmt::assume( + var.clone().rem(alignment).eq(Expr::int_constant(0, var.typ().clone())), + *loc, + ); + let cast_to_pointer_type = var.cast_to(address_of.typ().clone()).as_stmt(*loc); + Expr::statement_expression( + vec![decl, assume_non_zero, assume_aligned, cast_to_pointer_type], + address_of.typ().clone(), + ) + } else { + // Just return the address of the place dereferenced. + address_of + } } else if place_ty == pointee_type(self.local_ty_stable(place.local)).unwrap() { // Just return the fat pointer if this is a simple &(*local). projection.fat_ptr_goto_expr.unwrap() diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index 43062ebed6a1..b857247021ec 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -61,25 +61,18 @@ where crate::assert(!ptr.is_null(), "Expected valid pointer, but found `null`"); let (thin_ptr, metadata) = ptr.to_raw_parts(); - can_read(&metadata, thin_ptr) -} - -fn can_read(metadata: &M, data_ptr: *const ()) -> bool -where - M: PtrProperties, - T: ?Sized, -{ - let marker = Internal; - let sz = metadata.pointee_size(marker); - if metadata.dangling(marker) as *const _ == data_ptr { - crate::assert(sz == 0, "Dangling pointer is only valid for zero-sized access") + let sz = metadata.pointee_size(Internal); + if sz == 0 { + true // ZST pointers are always valid } else { + // Note that this branch can't be tested in concrete execution as `is_read_ok` needs to be + // stubbed. crate::assert( - is_read_ok(data_ptr, sz), + is_read_ok(thin_ptr, sz), "Expected valid pointer, but found dangling pointer", ); + true } - true } mod private { @@ -256,18 +249,6 @@ mod tests { assert_valid_ptr(vec_ptr); } - #[test] - #[should_panic(expected = "Dangling pointer is only valid for zero-sized access")] - fn test_dangling_char() { - test_dangling_of_t::(); - } - - #[test] - #[should_panic(expected = "Dangling pointer is only valid for zero-sized access")] - fn test_dangling_slice() { - test_dangling_of_t::<&str>(); - } - #[test] #[should_panic(expected = "Expected valid pointer, but found `null`")] fn test_null_fat_ptr() { diff --git a/tests/expected/function-contract/valid_ptr.expected b/tests/expected/function-contract/valid_ptr.expected index a9dc8dd5992d..f45cb4e2e826 100644 --- a/tests/expected/function-contract/valid_ptr.expected +++ b/tests/expected/function-contract/valid_ptr.expected @@ -1,5 +1,5 @@ Checking harness pre_condition::harness_invalid_ptr... -Failed Checks: Dangling pointer is only valid for zero-sized access +Failed Checks: Expected valid pointer, but found dangling pointer Checking harness pre_condition::harness_stack_ptr... VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/zst/expected b/tests/expected/zst/expected new file mode 100644 index 000000000000..bec891bea92c --- /dev/null +++ b/tests/expected/zst/expected @@ -0,0 +1,4 @@ +Status: FAILURE\ +Description: "dereference failure: pointer NULL" + +VERIFICATION:- FAILED diff --git a/tests/expected/zst/main.rs b/tests/expected/zst/main.rs new file mode 100644 index 000000000000..587e2608c870 --- /dev/null +++ b/tests/expected/zst/main.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// This example demonstrates that rustc may choose not to allocate unique locations to ZST objects. +#[repr(C)] +#[derive(Copy, Clone)] +struct Z(i8, i64); + +struct Y; + +#[kani::proof] +fn test_z() -> Z { + let m = Y; + let n = Y; + let zz = Z(1, -1); + + let ptr: *const Z = if &n as *const _ == &m as *const _ { std::ptr::null() } else { &zz }; + unsafe { *ptr } +} diff --git a/tests/kani/Closure/zst_param.rs b/tests/kani/Closure/zst_param.rs index 3eee1e5ac672..7a93619a9e16 100644 --- a/tests/kani/Closure/zst_param.rs +++ b/tests/kani/Closure/zst_param.rs @@ -17,7 +17,8 @@ fn check_zst_param() { let input = kani::any(); let closure = |a: Void, out: usize, b: Void| { kani::cover!(); - assert!(&a as *const Void != &b as *const Void, "Should succeed"); + assert!(&a as *const Void != std::ptr::null(), "Should succeed"); + assert!(&b as *const Void != std::ptr::null(), "Should succeed"); out }; let output = invoke(input, closure); diff --git a/tests/kani/MemPredicates/thin_ptr_validity.rs b/tests/kani/MemPredicates/thin_ptr_validity.rs index 638b7e1d1fc8..49e9c403cda8 100644 --- a/tests/kani/MemPredicates/thin_ptr_validity.rs +++ b/tests/kani/MemPredicates/thin_ptr_validity.rs @@ -46,10 +46,10 @@ mod invalid_access { } #[kani::proof] - #[kani::should_panic] pub fn check_invalid_zst() { let raw_ptr: *const [char; 0] = unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) } as *const _; + // ZST pointer are always valid assert_valid_ptr(raw_ptr); } From 7bc0cb8be079b9ae62fced4e02d26a65c9aa723d Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Fri, 3 May 2024 14:48:09 -0400 Subject: [PATCH 16/52] Fix copyright check for `expected` tests (#3170) This PR modifies the pattern used to exclude files from the copyright check for `expected` files. This ensures we check the copyright in files under `tests/expected/` while it skips the check for `expected` and `*.expected` files. It also adds/modifies copyright headers for some files that weren't being checked until now. Resolves #3141 --- scripts/ci/copyright-exclude | 2 +- tests/expected/coroutines/main.rs | 2 +- tests/expected/coroutines/pin/main.rs | 2 +- tests/expected/function-contract/modifies/refcell_fixme.rs | 2 ++ tests/expected/offset-wraps-around/main.rs | 2 +- tests/expected/slice_c_str/c_str_fixme.rs | 2 ++ 6 files changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/ci/copyright-exclude b/scripts/ci/copyright-exclude index 358edc372af2..157d88fbc379 100644 --- a/scripts/ci/copyright-exclude +++ b/scripts/ci/copyright-exclude @@ -10,7 +10,7 @@ Cargo.lock LICENSE-APACHE LICENSE-MIT editorconfig -expected +expected$ gitattributes gitignore gitmodules diff --git a/tests/expected/coroutines/main.rs b/tests/expected/coroutines/main.rs index a49d9944d0f1..d94524c05d63 100644 --- a/tests/expected/coroutines/main.rs +++ b/tests/expected/coroutines/main.rs @@ -1,4 +1,4 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT #![feature(coroutines, coroutine_trait)] diff --git a/tests/expected/coroutines/pin/main.rs b/tests/expected/coroutines/pin/main.rs index 0052715377ec..7d33005bb138 100644 --- a/tests/expected/coroutines/pin/main.rs +++ b/tests/expected/coroutines/pin/main.rs @@ -1,4 +1,4 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // Test contains a call to a coroutine via a Pin diff --git a/tests/expected/function-contract/modifies/refcell_fixme.rs b/tests/expected/function-contract/modifies/refcell_fixme.rs index 8ae9cb390eb7..9cfac8777959 100644 --- a/tests/expected/function-contract/modifies/refcell_fixme.rs +++ b/tests/expected/function-contract/modifies/refcell_fixme.rs @@ -1,3 +1,5 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT use std::cell::RefCell; use std::ops::Deref; diff --git a/tests/expected/offset-wraps-around/main.rs b/tests/expected/offset-wraps-around/main.rs index 62ec6902020b..3e83eacc4c2d 100644 --- a/tests/expected/offset-wraps-around/main.rs +++ b/tests/expected/offset-wraps-around/main.rs @@ -1,4 +1,4 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // Check that a high offset causes a "wrapping around" behavior in CBMC. diff --git a/tests/expected/slice_c_str/c_str_fixme.rs b/tests/expected/slice_c_str/c_str_fixme.rs index 894746772100..ede6c814e1a0 100644 --- a/tests/expected/slice_c_str/c_str_fixme.rs +++ b/tests/expected/slice_c_str/c_str_fixme.rs @@ -1,3 +1,5 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT #![feature(rustc_private)] #![feature(c_str_literals)] //! FIXME: From df2c1fb63ca248eb0009783e9b131413ec6bcd23 Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Fri, 3 May 2024 15:25:17 -0400 Subject: [PATCH 17/52] Remove kani::Arbitrary from the modifies contract instrumentation (#3169) This is an additional fix for https://github.com/model-checking/kani/pull/3098. With this fix, Kani should be able to check for contracts using modifies clauses that contain references to types that doesn't implement `kani::Arbitrary`. The verification will still fail if the same contract is used as a verified stub. --------- Signed-off-by: Felipe R. Monteiro --- .../src/sysroot/contracts/check.rs | 54 +++++++++++++++---- ...simple_only_verification_modifies.expected | 1 + .../simple_only_verification_modifies.rs | 28 ++++++++++ 3 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 tests/expected/function-contract/modifies/simple_only_verification_modifies.expected create mode 100644 tests/expected/function-contract/modifies/simple_only_verification_modifies.rs diff --git a/library/kani_macros/src/sysroot/contracts/check.rs b/library/kani_macros/src/sysroot/contracts/check.rs index f18b56f934ea..516bd187ba7f 100644 --- a/library/kani_macros/src/sysroot/contracts/check.rs +++ b/library/kani_macros/src/sysroot/contracts/check.rs @@ -13,6 +13,8 @@ use super::{ ContractConditionsData, ContractConditionsHandler, }; +const WRAPPER_ARG_PREFIX: &str = "_wrapper_arg_"; + impl<'a> ContractConditionsHandler<'a> { /// Create the body of a check function. /// @@ -60,7 +62,11 @@ impl<'a> ContractConditionsHandler<'a> { let wrapper_args = if let Some(wrapper_call_args) = inner.iter_mut().find_map(|stmt| try_as_wrapper_call_args(stmt, &wrapper_name)) { - let wrapper_args = make_wrapper_args(wrapper_call_args.len(), attr.len()); + let wrapper_args = make_wrapper_idents( + wrapper_call_args.len(), + attr.len(), + WRAPPER_ARG_PREFIX, + ); wrapper_call_args .extend(wrapper_args.clone().map(|a| Expr::Verbatim(quote!(#a)))); wrapper_args @@ -124,20 +130,43 @@ impl<'a> ContractConditionsHandler<'a> { /// Emit a modifies wrapper, possibly augmenting a prior, existing one. /// - /// We only augment if this clause is a `modifies` clause. In that case we - /// expand its signature with one new argument of type `&impl Arbitrary` for - /// each expression in the clause. + /// We only augment if this clause is a `modifies` clause. Before, + /// we annotated the wrapper arguments with `impl kani::Arbitrary`, + /// so Rust would infer the proper types for each argument. + /// We want to remove the restriction that these arguments must + /// implement `kani::Arbitrary` for checking. Now, we annotate each + /// argument with a generic type parameter, so the compiler can + /// continue inferring the correct types. pub fn emit_augmented_modifies_wrapper(&mut self) { if let ContractConditionsData::Modifies { attr } = &self.condition_type { - let wrapper_args = make_wrapper_args(self.annotated_fn.sig.inputs.len(), attr.len()); + let wrapper_args = make_wrapper_idents( + self.annotated_fn.sig.inputs.len(), + attr.len(), + WRAPPER_ARG_PREFIX, + ); + // Generate a unique type parameter identifier + let type_params = make_wrapper_idents( + self.annotated_fn.sig.inputs.len(), + attr.len(), + "WrapperArgType", + ); let sig = &mut self.annotated_fn.sig; - for arg in wrapper_args.clone() { + for (arg, arg_type) in wrapper_args.clone().zip(type_params) { + // Add the type parameter to the function signature's generic parameters list + sig.generics.params.push(syn::GenericParam::Type(syn::TypeParam { + attrs: vec![], + ident: arg_type.clone(), + colon_token: None, + bounds: Default::default(), + eq_token: None, + default: None, + })); let lifetime = syn::Lifetime { apostrophe: Span::call_site(), ident: arg.clone() }; sig.inputs.push(FnArg::Typed(syn::PatType { attrs: vec![], colon_token: Token![:](Span::call_site()), pat: Box::new(syn::Pat::Verbatim(quote!(#arg))), - ty: Box::new(syn::Type::Verbatim(quote!(&#lifetime impl kani::Arbitrary))), + ty: Box::new(syn::parse_quote! { &#arg_type }), })); sig.generics.params.push(syn::GenericParam::Lifetime(syn::LifetimeParam { lifetime, @@ -146,6 +175,7 @@ impl<'a> ContractConditionsHandler<'a> { attrs: vec![], })); } + self.output.extend(quote!(#[kanitool::modifies(#(#wrapper_args),*)])) } self.emit_common_header(); @@ -191,10 +221,14 @@ fn try_as_wrapper_call_args<'a>( } } -/// Make `num` [`Ident`]s with the names `_wrapper_arg_{i}` with `i` starting at `low` and +/// Make `num` [`Ident`]s with the names `prefix{i}` with `i` starting at `low` and /// increasing by one each time. -fn make_wrapper_args(low: usize, num: usize) -> impl Iterator + Clone { - (low..).map(|i| Ident::new(&format!("_wrapper_arg_{i}"), Span::mixed_site())).take(num) +fn make_wrapper_idents( + low: usize, + num: usize, + prefix: &'static str, +) -> impl Iterator + Clone + 'static { + (low..).map(move |i| Ident::new(&format!("{prefix}{i}"), Span::mixed_site())).take(num) } #[cfg(test)] diff --git a/tests/expected/function-contract/modifies/simple_only_verification_modifies.expected b/tests/expected/function-contract/modifies/simple_only_verification_modifies.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/function-contract/modifies/simple_only_verification_modifies.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies/simple_only_verification_modifies.rs b/tests/expected/function-contract/modifies/simple_only_verification_modifies.rs new file mode 100644 index 000000000000..4988dcb69c56 --- /dev/null +++ b/tests/expected/function-contract/modifies/simple_only_verification_modifies.rs @@ -0,0 +1,28 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +//! Check that is possible to use `modifies` clause for verification, but not stubbing. +//! Here, we cover the case when the modifies clause contains references to function +//! parameters of generic types. Noticed that here the type T is not annotated with +//! `kani::Arbitrary` since this is no longer a requirement if the contract is only +//! use for verification. + +pub mod contracts { + #[kani::modifies(x)] + #[kani::modifies(y)] + pub fn swap(x: &mut T, y: &mut T) { + core::mem::swap(x, y) + } +} + +mod verify { + use super::*; + + #[kani::proof_for_contract(contracts::swap)] + pub fn check_swap_primitive() { + let mut x: u8 = kani::any(); + let mut y: u8 = kani::any(); + contracts::swap(&mut x, &mut y) + } +} From 5edab5d1b1ff44548d34a88a0dc1e1814c39a8aa Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 8 May 2024 23:18:05 -0700 Subject: [PATCH 18/52] Annotate loop contracts as statement expression --- cprover_bindings/src/goto_program/stmt.rs | 22 ++- cprover_bindings/src/irep/to_irep.rs | 12 +- .../codegen_cprover_gotoc/codegen/block.rs | 34 ++++- .../codegen_cprover_gotoc/codegen/function.rs | 3 + .../codegen/statement.rs | 16 ++- .../codegen_cprover_gotoc/context/goto_ctx.rs | 8 +- .../context/loop_contracts_ctx.rs | 135 ++++++++++++++++++ .../src/codegen_cprover_gotoc/context/mod.rs | 1 + .../codegen_cprover_gotoc/overrides/hooks.rs | 3 + .../overrides/loop_contracts_hooks.rs | 61 ++++++++ .../codegen_cprover_gotoc/overrides/mod.rs | 1 + kani_metadata/src/unstable.rs | 2 + library/kani/src/lib.rs | 4 + library/kani/src/loop_contracts.rs | 23 +++ library/kani_macros/src/lib.rs | 11 +- .../src/sysroot/loop_contracts/mod.rs | 70 +++++++++ 16 files changed, 391 insertions(+), 15 deletions(-) create mode 100644 kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs create mode 100644 kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs create mode 100644 library/kani/src/loop_contracts.rs create mode 100644 library/kani_macros/src/sysroot/loop_contracts/mod.rs diff --git a/cprover_bindings/src/goto_program/stmt.rs b/cprover_bindings/src/goto_program/stmt.rs index 951e58f9a954..012e23f71e2b 100644 --- a/cprover_bindings/src/goto_program/stmt.rs +++ b/cprover_bindings/src/goto_program/stmt.rs @@ -82,7 +82,10 @@ pub enum StmtBody { arguments: Vec, }, /// `goto dest;` - Goto(InternedString), + Goto { + dest: InternedString, + loop_invariants: Option, + }, /// `if (i) { t } else { e }` Ifthenelse { i: Expr, @@ -179,7 +182,7 @@ impl Stmt { stmt!(Assign { lhs, rhs }, loc) } - /// `assert(cond, property_class, commment);` + /// `assert(cond, property_class, comment);` pub fn assert(cond: Expr, property_name: &str, message: &str, loc: Location) -> Self { assert!(cond.typ().is_bool()); assert!(!property_name.is_empty() && !message.is_empty()); @@ -188,7 +191,7 @@ impl Stmt { let loc_with_property = Location::create_location_with_property(message, property_name, loc); - // Chose InternedString to seperate out codegen from the cprover_bindings logic + // Chose InternedString to separate out codegen from the cprover_bindings logic let property_class = property_name.intern(); let msg = message.into(); @@ -283,7 +286,18 @@ impl Stmt { pub fn goto>(dest: T, loc: Location) -> Self { let dest = dest.into(); assert!(!dest.is_empty()); - stmt!(Goto(dest), loc) + stmt!(Goto { dest, loop_invariants: None }, loc) + } + + /// `goto dest;` with loop invariant + pub fn goto_with_loop_inv>( + dest: T, + loop_invariants: Expr, + loc: Location, + ) -> Self { + let dest = dest.into(); + assert!(!dest.is_empty()); + stmt!(Goto { dest, loop_invariants: Some(loop_invariants) }, loc) } /// `if (i) { t } else { e }` or `if (i) { t }` diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 9ec8c49e80bf..ff5fc2ce7cde 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -469,8 +469,16 @@ impl ToIrep for StmtBody { arguments_irep(arguments.iter(), mm), ], ), - StmtBody::Goto(dest) => code_irep(IrepId::Goto, vec![]) - .with_named_sub(IrepId::Destination, Irep::just_string_id(dest.to_string())), + StmtBody::Goto { dest, loop_invariants } => { + let stmt_goto = code_irep(IrepId::Goto, vec![]) + .with_named_sub(IrepId::Destination, Irep::just_string_id(dest.to_string())); + if loop_invariants.is_some() { + stmt_goto + .with_named_sub(IrepId::CSpecLoopInvariant, loop_invariants.clone().unwrap().to_irep(mm)) + } else { + stmt_goto + } + } StmtBody::Ifthenelse { i, t, e } => code_irep( IrepId::Ifthenelse, vec![ diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs index 5fe28097a2e0..76cdfb63e56b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs @@ -21,12 +21,23 @@ impl<'tcx> GotocCtx<'tcx> { debug!(?bb, "codegen_block"); let label = bb_label(bb); let check_coverage = self.queries.args().check_coverage; + + // record the seen bbidx if loop contracts enabled + if self.loop_contracts_ctx.loop_contracts_enabled() { + self.loop_contracts_ctx.add_new_seen_bbidx(bb); + } + // the first statement should be labelled. if there is no statements, then the // terminator should be labelled. match bbd.statements.len() { 0 => { let term = &bbd.terminator; - let tcode = self.codegen_terminator(term); + let tcode = if self.loop_contracts_ctx.loop_contracts_enabled() { + let codegen_result = self.codegen_terminator(term); + self.loop_contracts_ctx.push_onto_block(codegen_result) + } else { + self.codegen_terminator(term) + }; // When checking coverage, the `coverage` check should be // labelled instead. if check_coverage { @@ -40,7 +51,12 @@ impl<'tcx> GotocCtx<'tcx> { } _ => { let stmt = &bbd.statements[0]; - let scode = self.codegen_statement(stmt); + let scode = if self.loop_contracts_ctx.loop_contracts_enabled() { + let codegen_result = self.codegen_statement(stmt); + self.loop_contracts_ctx.push_onto_block(codegen_result) + } else { + self.codegen_statement(stmt) + }; // When checking coverage, the `coverage` check should be // labelled instead. if check_coverage { @@ -58,7 +74,12 @@ impl<'tcx> GotocCtx<'tcx> { let cover = self.codegen_coverage(span); self.current_fn_mut().push_onto_block(cover); } - let stmt = self.codegen_statement(s); + let stmt = if self.loop_contracts_ctx.loop_contracts_enabled() { + let codegen_result = self.codegen_statement(s); + self.loop_contracts_ctx.push_onto_block(codegen_result) + } else { + self.codegen_statement(s) + }; self.current_fn_mut().push_onto_block(stmt); } let term = &bbd.terminator; @@ -67,7 +88,12 @@ impl<'tcx> GotocCtx<'tcx> { let cover = self.codegen_coverage(span); self.current_fn_mut().push_onto_block(cover); } - let tcode = self.codegen_terminator(term); + let tcode = if self.loop_contracts_ctx.loop_contracts_enabled() { + let codegen_result = self.codegen_terminator(term); + self.loop_contracts_ctx.push_onto_block(codegen_result) + } else { + self.codegen_terminator(term) + }; self.current_fn_mut().push_onto_block(tcode); } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index d55696bfdc87..08f117511d15 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -56,6 +56,9 @@ impl<'tcx> GotocCtx<'tcx> { let old_sym = self.symbol_table.lookup(&name).unwrap(); let _trace_span = debug_span!("CodegenFunction", name = instance.name()).entered(); + if self.loop_contracts_ctx.loop_contracts_enabled() { + self.loop_contracts_ctx.enter_new_function(); + } if old_sym.is_function_definition() { debug!("Double codegen of {:?}", old_sym); } else { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 9a78515fde90..aed3f42c0156 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -125,12 +125,24 @@ impl<'tcx> GotocCtx<'tcx> { /// /// See also [`GotocCtx::codegen_statement`] for ordinary [Statement]s. pub fn codegen_terminator(&mut self, term: &Terminator) -> Stmt { - let loc = self.codegen_span_stable(term.span); + let loc: Location = self.codegen_span_stable(term.span); let _trace_span = debug_span!("CodegenTerminator", statement = ?term.kind).entered(); debug!("handling terminator {:?}", term); //TODO: Instead of doing location::none(), and updating, just putit in when we make the stmt. match &term.kind { - TerminatorKind::Goto { target } => Stmt::goto(bb_label(*target), loc), + TerminatorKind::Goto { target } => { + if self.loop_contracts_ctx.loop_contracts_enabled() + && self.loop_contracts_ctx.is_loop_latch(target) + { + Stmt::goto_with_loop_inv( + bb_label(*target), + self.loop_contracts_ctx.extract_block(), + loc, + ) + } else { + Stmt::goto(bb_label(*target), loc) + } + } TerminatorKind::SwitchInt { discr, targets } => { self.codegen_switch_int(discr, targets, loc) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs index 095f907228a4..4119c6ab2b11 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -14,6 +14,7 @@ //! Any MIR specific functionality (e.g. codegen etc) should live in specialized files that use //! this structure as input. use super::current_fn::CurrentFnCtx; +use super::loop_contracts_ctx::LoopContractsCtx; use super::vtable_ctx::VtableCtx; use crate::codegen_cprover_gotoc::overrides::{fn_hooks, GotocHooks}; use crate::codegen_cprover_gotoc::utils::full_crate_name; @@ -70,6 +71,8 @@ pub struct GotocCtx<'tcx> { pub concurrent_constructs: UnsupportedConstructs, /// The body transformation agent. pub transformer: BodyTransformation, + /// The context for loop contracts code generation. + pub loop_contracts_ctx: LoopContractsCtx, } /// Constructor @@ -83,6 +86,8 @@ impl<'tcx> GotocCtx<'tcx> { let fhks = fn_hooks(); let symbol_table = SymbolTable::new(machine_model.clone()); let emit_vtable_restrictions = queries.args().emit_vtable_restrictions; + let loop_contracts_enabled = + queries.args().unstable_features.contains(&"loop-contracts".to_string()); GotocCtx { tcx, queries, @@ -99,6 +104,7 @@ impl<'tcx> GotocCtx<'tcx> { unsupported_constructs: FxHashMap::default(), concurrent_constructs: FxHashMap::default(), transformer, + loop_contracts_ctx: LoopContractsCtx::new(loop_contracts_enabled), } } } @@ -278,7 +284,7 @@ impl<'tcx> GotocCtx<'tcx> { Type::union_tag(union_name) } - /// Makes a `__attribute__((constructor)) fnname() {body}` initalizer function + /// Makes a `__attribute__((constructor)) fnname() {body}` initializer function pub fn register_initializer(&mut self, var_name: &str, body: Stmt) -> &Symbol { let fn_name = Self::initializer_fn_name(var_name); let pretty_name = format!("{var_name}::init"); diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs new file mode 100644 index 000000000000..f7d0c09e1921 --- /dev/null +++ b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs @@ -0,0 +1,135 @@ +use crate::codegen_cprover_gotoc::codegen::bb_label; +use cbmc::goto_program::{CIntType, Expr, Stmt, StmtBody, Type}; +use stable_mir::mir::BasicBlockIdx; +use std::collections::HashSet; + +pub struct LoopContractsCtx { + /// the GOTO block compiled from the corresponding loop invariants + invariants_block: Vec, + /// Which codegen state + stage: LoopContractsStage, + /// If enable loop contracts + loop_contracts_enabled: bool, + /// Seen basic block indexes. Used to decide if a jump is backward + seen_bbidx: HashSet, + /// Current unused bbidx label + current_bbidx_label: Option, + /// The lhs of evaluation of the loop invariant + loop_invariant_lhs: Option, +} + +/// We define two states: +/// 1. loop invariants block +/// In this state, we push all codegen stmts into the invariant block. +/// We enter this state when codegen for `KaniLoopInvariantBegin`. +/// We exit this state when codegen for `KaniLoopInvariantEnd`. +/// 2. loop latch block +/// In this state, we codegen a statement expression from the +/// invariant_block annotate the statement expression to the named sub +/// of the next backward jumping we codegen. +/// We enter this state when codegen for `KaniLoopInvariantEnd`. +/// We exit this state when codegen for the first backward jumping. +#[allow(dead_code)] +#[derive(Debug, PartialEq)] +enum LoopContractsStage { + /// Codegen for user code as usual + UserCode, + /// Codegen for loop invariants + InvariantBlock, + /// Codegen for loop latch node + FindingLatchNode, +} + +/// Constructor +impl LoopContractsCtx { + pub fn new(loop_contracts_enabled: bool) -> Self { + Self { + invariants_block: Vec::new(), + stage: LoopContractsStage::UserCode, + loop_contracts_enabled: loop_contracts_enabled, + seen_bbidx: HashSet::new(), + current_bbidx_label: None, + loop_invariant_lhs: None, + } + } +} + +/// Getters +impl LoopContractsCtx { + pub fn loop_contracts_enabled(&self) -> bool { + self.loop_contracts_enabled + } + + /// decide if a GOTO with `target` is backward jump + pub fn is_loop_latch(&self, target: &BasicBlockIdx) -> bool { + self.stage == LoopContractsStage::FindingLatchNode && self.seen_bbidx.contains(target) + } +} + +/// Setters +impl LoopContractsCtx { + /// Returns the current block as a statement expression. + /// Exit loop latch block. + pub fn extract_block(&mut self) -> Expr { + assert!(self.loop_invariant_lhs.is_some()); + self.stage = LoopContractsStage::UserCode; + self.invariants_block.push(self.loop_invariant_lhs.as_ref().unwrap().clone()); + + // The first statement is the GOTO in the rhs of __kani_loop_invariant_begin() + // Ignore it + self.invariants_block.remove(0); + + Expr::statement_expression( + std::mem::take(&mut self.invariants_block), + Type::CInteger(CIntType::Bool), + ) + .cast_to(Type::bool()) + } + + /// Push the `s` onto the block if it is in the loop invariant block + /// and return `skip`. Otherwise, do nothing and return `s`. + pub fn push_onto_block(&mut self, s: Stmt) -> Stmt { + if self.stage == LoopContractsStage::InvariantBlock { + // Attach the lable to the first Stmt in that block and reset it. + let to_push = if self.current_bbidx_label.is_none() { + s.clone() + } else { + s.clone().with_label(self.current_bbidx_label.clone().unwrap()) + }; + self.current_bbidx_label = None; + + match s.body() { + StmtBody::Assign { lhs, rhs: _ } => { + let lhs_stmt = lhs.clone().as_stmt(*s.location()); + self.loop_invariant_lhs = Some(lhs_stmt.clone()); + self.invariants_block.push(to_push); + } + _ => { + self.invariants_block.push(to_push); + } + }; + Stmt::skip(*s.location()) + } else { + s + } + } + + pub fn enter_loop_invariant_block(&mut self) { + assert!(self.invariants_block.is_empty()); + self.stage = LoopContractsStage::InvariantBlock; + } + + pub fn exit_loop_invariant_block(&mut self) { + self.stage = LoopContractsStage::FindingLatchNode; + } + + /// Enter a new function, reset the seen_bbidx set + pub fn enter_new_function(&mut self) { + self.seen_bbidx = HashSet::new() + } + + pub fn add_new_seen_bbidx(&mut self, bbidx: BasicBlockIdx) { + self.seen_bbidx.insert(bbidx); + self.current_bbidx_label = Some(bb_label(bbidx)); + } +} diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/mod.rs b/kani-compiler/src/codegen_cprover_gotoc/context/mod.rs index 0053b9add18b..0978b299e309 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/mod.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/mod.rs @@ -8,6 +8,7 @@ mod current_fn; mod goto_ctx; +mod loop_contracts_ctx; mod vtable_ctx; pub use goto_ctx::GotocCtx; diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 18cc44b7b20d..793c842d5367 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -8,6 +8,7 @@ //! It would be too nasty if we spread around these sort of undocumented hooks in place, so //! this module addresses this issue. +use super::loop_contracts_hooks::{LoopInvariantBegin, LoopInvariantEnd}; use crate::codegen_cprover_gotoc::codegen::{bb_label, PropertyClass}; use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::attributes::matches_diagnostic as matches_function; @@ -402,6 +403,8 @@ pub fn fn_hooks() -> GotocHooks { Rc::new(RustAlloc), Rc::new(MemCmp), Rc::new(UntrackedDeref), + Rc::new(LoopInvariantBegin), + Rc::new(LoopInvariantEnd), ], } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs new file mode 100644 index 000000000000..893265e4773a --- /dev/null +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs @@ -0,0 +1,61 @@ +use super::hooks::GotocHook; +use crate::codegen_cprover_gotoc::codegen::bb_label; +use crate::codegen_cprover_gotoc::GotocCtx; +use crate::kani_middle::attributes::matches_diagnostic as matches_function; +use cbmc::goto_program::{Expr, Stmt}; +use rustc_middle::ty::TyCtxt; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::{BasicBlockIdx, Place}; +use stable_mir::ty::Span; + +pub struct LoopInvariantBegin; + +impl GotocHook for LoopInvariantBegin { + fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { + matches_function(tcx, instance.def, "KaniLoopInvariantBegin") + } + + fn handle( + &self, + gcx: &mut GotocCtx, + _instance: Instance, + fargs: Vec, + _assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + assert_eq!(fargs.len(), 0); + let loc = gcx.codegen_span_stable(span); + + // Start to record loop invariant statement + gcx.loop_contracts_ctx.enter_loop_invariant_block(); + + Stmt::goto(bb_label(target.unwrap()), loc) + } +} + +pub struct LoopInvariantEnd; + +impl GotocHook for LoopInvariantEnd { + fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { + matches_function(tcx, instance.def, "KaniLoopInvariantEnd") + } + + fn handle( + &self, + gcx: &mut GotocCtx, + _instance: Instance, + fargs: Vec, + _assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + assert_eq!(fargs.len(), 0); + let loc = gcx.codegen_span_stable(span); + + // Stop to record loop invariant statement + gcx.loop_contracts_ctx.exit_loop_invariant_block(); + + Stmt::goto(bb_label(target.unwrap()), loc) + } +} diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/mod.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/mod.rs index 3dda5076fb89..dfe92a31d6c0 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/mod.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/mod.rs @@ -7,5 +7,6 @@ //! Instead, we use a "hook" to generate the correct CBMC intrinsic. mod hooks; +mod loop_contracts_hooks; pub use hooks::{fn_hooks, GotocHooks}; diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index 878b468dbdc3..9fd21075c16e 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -82,6 +82,8 @@ pub enum UnstableFeature { LineCoverage, /// Enable function contracts [RFC 9](https://model-checking.github.io/kani/rfc/rfcs/0009-function-contracts.html) FunctionContracts, + /// Enable loop contracts [RFC 12] + LoopContracts, /// Memory predicate APIs. MemPredicates, /// Automatically check that no invalid value is produced which is considered UB in Rust. diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 8161ad7f052b..3126b88bf599 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -340,3 +340,7 @@ pub use core::assert as __kani__workaround_core_assert; pub use kani_macros::*; pub mod contracts; + +mod loop_contracts; + +pub use loop_contracts::{kani_loop_invariant_begin_marker, kani_loop_invariant_end_marker}; diff --git a/library/kani/src/loop_contracts.rs b/library/kani/src/loop_contracts.rs new file mode 100644 index 000000000000..c3ef5932b7dd --- /dev/null +++ b/library/kani/src/loop_contracts.rs @@ -0,0 +1,23 @@ +/// This function is only used for loop contract annotation. +/// It behaves as a placeholder to telling us where the loop invariants stmts begin. +#[inline(never)] +#[rustc_diagnostic_item = "KaniLoopInvariantBegin"] +#[doc(hidden)] +#[crate::unstable( + feature = "loop-contracts", + issue = 3168, + reason = "experimental loop contracts support" +)] +pub const fn kani_loop_invariant_begin_marker() {} + +/// This function is only used for loop contract annotation. +/// It behaves as a placeholder to telling us where the loop invariants stmts end. +#[inline(never)] +#[rustc_diagnostic_item = "KaniLoopInvariantEnd"] +#[doc(hidden)] +#[crate::unstable( + feature = "loop-contracts", + issue = 3168, + reason = "experimental loop contracts support" +)] +pub const fn kani_loop_invariant_end_marker() {} diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 7b51a94fa824..3ddb3c74be35 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -202,13 +202,18 @@ pub fn modifies(attr: TokenStream, item: TokenStream) -> TokenStream { attr_impl::modifies(attr, item) } +/// Add a loop invariant to this loop. +/// +/// The contents of the attribute is a condition that should be satisfied at the +/// beginning of every iteration of the loop. +/// All Rust syntax is supported, even calling other functions, but +/// the computations must be side effect free, e.g. it cannot perform I/O or use +/// mutable memory. #[proc_macro_attribute] pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { attr_impl::loop_invariant(attr, item) } -static mut LOOP_INVARIANT_COUNT: u32 = 0; - /// This module implements Kani attributes in a way that only Kani's compiler can understand. /// This code should only be activated when pre-building Kani's sysroot. #[cfg(kani_sysroot)] @@ -216,9 +221,11 @@ mod sysroot { use proc_macro_error::{abort, abort_call_site}; mod contracts; + mod loop_contracts; use contracts::helpers::*; pub use contracts::{ensures, modifies, proof_for_contract, requires, stub_verified}; + pub use loop_contracts::loop_invariant; use super::*; diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs new file mode 100644 index 000000000000..7f3a4c0cbb8d --- /dev/null +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -0,0 +1,70 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Implementation of the loop contracts code generation. +//! + +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use proc_macro_error::abort_call_site; +use quote::quote; +use syn::{Expr, Stmt}; + +pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { + let mut loop_stmt: Stmt = syn::parse(item.clone()).unwrap(); + + // Annotate a place holder function call at the end of the loop. + match loop_stmt { + Stmt::Expr(ref mut e, _) => match e { + Expr::While(ref mut ew) => { + // A while loop of the form + // ``` rust + // while guard { + // body + // } + // ``` + // is annotated as + // ``` rust + // while guard{ + // body + // kani::kani_loop_invariant_begin_marker(); + // let __kani_loop_invariant: bool = inv; + // kani::kani_loop_invariant_end_marker(); + // } + // ``` + let mut to_parse = quote!( + let __kani_loop_invariant: bool = ); + to_parse.extend(TokenStream2::from(attr.clone())); + to_parse.extend(quote!(;)); + let inv_assign_stmt: Stmt = syn::parse(to_parse.into()).unwrap(); + + // kani::kani_loop_invariant_begin_marker(); + let inv_begin_stmt: Stmt = syn::parse( + quote!( + kani::kani_loop_invariant_begin_marker();) + .into(), + ) + .unwrap(); + + // kani::kani_loop_invariant_end_marker(); + let inv_end_stmt: Stmt = syn::parse( + quote!( + kani::kani_loop_invariant_end_marker();) + .into(), + ) + .unwrap(); + ew.body.stmts.push(inv_begin_stmt); + ew.body.stmts.push(inv_assign_stmt); + ew.body.stmts.push(inv_end_stmt); + } + _ => (), + }, + _ => abort_call_site!("`#[kani::loop_invariant]` is not only supported for while-loops."; + note = "for now, loop contracts is only supported for while-loops."; + ), + } + + quote!(; + #loop_stmt;) + .into() +} From c8ecefe46a09b5cd398f349b7d390b504f66ecbd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 May 2024 22:47:56 -0700 Subject: [PATCH 19/52] Automatic cargo update to 2024-05-06 (#3172) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 63 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62f2c913238d..6100ab068816 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,47 +25,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys", @@ -79,9 +80,9 @@ checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" @@ -179,9 +180,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "comfy-table" @@ -387,6 +388,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -496,9 +503,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "linear-map" @@ -608,9 +615,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -631,9 +638,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -903,18 +910,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", @@ -1393,18 +1400,18 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" dependencies = [ "proc-macro2", "quote", From bda1f3c81d4adfecacc0438a7254598d88155009 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 20:08:30 +0100 Subject: [PATCH 20/52] Bump tests/perf/s2n-quic from `1436af7` to `6dd41e0` (#3174) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `1436af7` to `6dd41e0`.
Commits
  • 6dd41e0 build: fix clippy warnings for 1.78 (#2199)
  • de5c33e refactor(s2n-quic-core): improve reassembler error handling (#2197)
  • b085808 chore(s2n-quic-crypto): remove custom aesgcm implementation (#2186)
  • 7188ce4 feat(dc): DcSupportedVersions transport parameter (#2193)
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 1436af712b6e..6dd41e09195b 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 1436af712b6e73edc11640dc7c3cae23e456c0a8 +Subproject commit 6dd41e09195bc22dbac93a48f8ab35f8063726dc From eaf5b42f7a7f05720c6cc0329d629842fd8ca564 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 7 May 2024 03:22:42 +0200 Subject: [PATCH 21/52] Avoid unnecessary uses of Location::none() (#3173) We should try to produce a source location wherever possible to ease debugging and coverage reporting. --- .../codegen/intrinsic.rs | 120 ++++++++++-------- .../codegen/statement.rs | 43 +++---- .../codegen_cprover_gotoc/overrides/hooks.rs | 10 +- 3 files changed, 93 insertions(+), 80 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 4d201a6f8cc4..3a4e84a70d98 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -37,7 +37,7 @@ impl<'tcx> GotocCtx<'tcx> { let arg1 = fargs.remove(0); let arg2 = fargs.remove(0); let expr = f(arg1, arg2); - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, Location::none()) } /// Given a call to an compiler intrinsic, generate the call and the `goto` terminator @@ -149,7 +149,7 @@ impl<'tcx> GotocCtx<'tcx> { mm, ); let expr = BuiltinFn::$f.call(casted_fargs, loc); - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, loc) }}; } @@ -166,7 +166,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); let res = a.$f(b); - let expr_place = self.codegen_expr_to_place_stable(place, res); + let expr_place = self.codegen_expr_to_place_stable(place, res, loc); Stmt::block(vec![div_overflow_check, expr_place], loc) }}; } @@ -187,7 +187,7 @@ impl<'tcx> GotocCtx<'tcx> { let arg1 = fargs.remove(0); let arg2 = fargs.remove(0); let expr = arg1.$f(arg2, self.symbol_table.machine_model()); - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, loc) }}; } @@ -196,7 +196,7 @@ impl<'tcx> GotocCtx<'tcx> { macro_rules! codegen_count_intrinsic { ($builtin: ident, $allow_zero: expr) => {{ let arg = fargs.remove(0); - self.codegen_expr_to_place_stable(place, arg.$builtin($allow_zero)) + self.codegen_expr_to_place_stable(place, arg.$builtin($allow_zero), loc) }}; } @@ -209,7 +209,7 @@ impl<'tcx> GotocCtx<'tcx> { // We assume that the intrinsic has type checked at this point, so // we can use the place type as the expression type. let expr = self.codegen_allocation(&alloc, place_ty, Some(span)); - self.codegen_expr_to_place_stable(&place, expr) + self.codegen_expr_to_place_stable(&place, expr, loc) }}; } @@ -220,7 +220,7 @@ impl<'tcx> GotocCtx<'tcx> { let target_ty = args.0[0].expect_ty(); let arg = fargs.remove(0); let size_align = self.size_and_align_of_dst(*target_ty, arg); - self.codegen_expr_to_place_stable(place, size_align.$which) + self.codegen_expr_to_place_stable(place, size_align.$which, loc) }}; } @@ -271,7 +271,7 @@ impl<'tcx> GotocCtx<'tcx> { (var1.clone()).$op(var2).with_location(loc) }; let assign_stmt = (var1.clone()).assign(op_expr, loc); - let res_stmt = self.codegen_expr_to_place_stable(place, tmp.clone()); + let res_stmt = self.codegen_expr_to_place_stable(place, tmp.clone(), loc); Stmt::atomic_block(vec![decl_stmt, assign_stmt, res_stmt], loc) }}; } @@ -284,7 +284,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, "https://github.com/model-checking/kani/issues/new/choose", ); - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, loc) }}; } @@ -387,12 +387,14 @@ impl<'tcx> GotocCtx<'tcx> { "atomic_xsub_acqrel" => codegen_atomic_binop!(sub), "atomic_xsub_release" => codegen_atomic_binop!(sub), "atomic_xsub_relaxed" => codegen_atomic_binop!(sub), - "bitreverse" => self.codegen_expr_to_place_stable(place, fargs.remove(0).bitreverse()), + "bitreverse" => { + self.codegen_expr_to_place_stable(place, fargs.remove(0).bitreverse(), loc) + } // black_box is an identity function that hints to the compiler // to be maximally pessimistic to limit optimizations - "black_box" => self.codegen_expr_to_place_stable(place, fargs.remove(0)), + "black_box" => self.codegen_expr_to_place_stable(place, fargs.remove(0), loc), "breakpoint" => Stmt::skip(loc), - "bswap" => self.codegen_expr_to_place_stable(place, fargs.remove(0).bswap()), + "bswap" => self.codegen_expr_to_place_stable(place, fargs.remove(0).bswap(), loc), "caller_location" => self.codegen_unimplemented_stmt( intrinsic, loc, @@ -423,7 +425,7 @@ impl<'tcx> GotocCtx<'tcx> { let sig = instance.ty().kind().fn_sig().unwrap().skip_binder(); let ty = pointee_type_stable(sig.inputs()[0]).unwrap(); let e = self.codegen_get_discriminant(fargs.remove(0).dereference(), ty, ret_ty); - self.codegen_expr_to_place_stable(place, e) + self.codegen_expr_to_place_stable(place, e, loc) } "exact_div" => self.codegen_exact_div(fargs, place, loc), "exp2f32" => codegen_simple_intrinsic!(Exp2f), @@ -460,9 +462,9 @@ impl<'tcx> GotocCtx<'tcx> { "is_val_statically_known" => { // Returning false is sound according do this intrinsic's documentation: // https://doc.rust-lang.org/nightly/std/intrinsics/fn.is_val_statically_known.html - self.codegen_expr_to_place_stable(place, Expr::c_false()) + self.codegen_expr_to_place_stable(place, Expr::c_false(), loc) } - "likely" => self.codegen_expr_to_place_stable(place, fargs.remove(0)), + "likely" => self.codegen_expr_to_place_stable(place, fargs.remove(0), loc), "log10f32" => unstable_codegen!(codegen_simple_intrinsic!(Log10f)), "log10f64" => unstable_codegen!(codegen_simple_intrinsic!(Log10)), "log2f32" => unstable_codegen!(codegen_simple_intrinsic!(Log2f)), @@ -490,7 +492,7 @@ impl<'tcx> GotocCtx<'tcx> { "powif32" => unstable_codegen!(codegen_simple_intrinsic!(Powif)), "powif64" => unstable_codegen!(codegen_simple_intrinsic!(Powi)), "pref_align_of" => codegen_intrinsic_const!(), - "ptr_guaranteed_cmp" => self.codegen_ptr_guaranteed_cmp(fargs, place), + "ptr_guaranteed_cmp" => self.codegen_ptr_guaranteed_cmp(fargs, place, loc), "ptr_offset_from" => self.codegen_ptr_offset_from(fargs, place, loc), "ptr_offset_from_unsigned" => self.codegen_ptr_offset_from_unsigned(fargs, place, loc), "raw_eq" => self.codegen_intrinsic_raw_eq(instance, fargs, place, loc), @@ -575,16 +577,18 @@ impl<'tcx> GotocCtx<'tcx> { place, loc, ), - "transmute" => self.codegen_intrinsic_transmute(fargs, ret_ty, place), + "transmute" => self.codegen_intrinsic_transmute(fargs, ret_ty, place, loc), "truncf32" => codegen_simple_intrinsic!(Truncf), "truncf64" => codegen_simple_intrinsic!(Trunc), "type_id" => codegen_intrinsic_const!(), "type_name" => codegen_intrinsic_const!(), "typed_swap" => self.codegen_swap(fargs, farg_types, loc), "unaligned_volatile_load" => { - unstable_codegen!( - self.codegen_expr_to_place_stable(place, fargs.remove(0).dereference()) - ) + unstable_codegen!(self.codegen_expr_to_place_stable( + place, + fargs.remove(0).dereference(), + loc + )) } "unchecked_add" | "unchecked_mul" | "unchecked_shl" | "unchecked_shr" | "unchecked_sub" => { @@ -592,7 +596,7 @@ impl<'tcx> GotocCtx<'tcx> { } "unchecked_div" => codegen_op_with_div_overflow_check!(div), "unchecked_rem" => codegen_op_with_div_overflow_check!(rem), - "unlikely" => self.codegen_expr_to_place_stable(place, fargs.remove(0)), + "unlikely" => self.codegen_expr_to_place_stable(place, fargs.remove(0), loc), "unreachable" => unreachable!( "Expected `std::intrinsics::unreachable` to be handled by `TerminatorKind::Unreachable`" ), @@ -634,7 +638,8 @@ impl<'tcx> GotocCtx<'tcx> { if !arg.typ().is_integer() { self.intrinsics_typecheck_fail(span, "ctpop", "integer type", arg_rust_ty) } else { - self.codegen_expr_to_place_stable(&target_place, arg.popcount()) + let loc = self.codegen_span_stable(span); + self.codegen_expr_to_place_stable(&target_place, arg.popcount(), loc) } } @@ -723,6 +728,7 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_expr_to_place_stable( place, Expr::statement_expression(vec![res.as_stmt(loc)], result_type), + loc, ) } @@ -756,7 +762,7 @@ impl<'tcx> GotocCtx<'tcx> { "exact_div division does not overflow", loc, ), - self.codegen_expr_to_place_stable(p, a.div(b)), + self.codegen_expr_to_place_stable(p, a.div(b), loc), ], loc, ) @@ -857,7 +863,7 @@ impl<'tcx> GotocCtx<'tcx> { self.store_concurrent_construct(intrinsic, loc); let var1_ref = fargs.remove(0); let var1 = var1_ref.dereference().with_location(loc); - let res_stmt = self.codegen_expr_to_place_stable(p, var1); + let res_stmt = self.codegen_expr_to_place_stable(p, var1, loc); Stmt::atomic_block(vec![res_stmt], loc) } @@ -897,7 +903,7 @@ impl<'tcx> GotocCtx<'tcx> { let tuple_expr = Expr::struct_expr_from_values(res_type, vec![tmp, Expr::c_true()], &self.symbol_table) .with_location(loc); - let res_stmt = self.codegen_expr_to_place_stable(p, tuple_expr); + let res_stmt = self.codegen_expr_to_place_stable(p, tuple_expr, loc); Stmt::atomic_block(vec![decl_stmt, cond_update_stmt, res_stmt], loc) } @@ -925,7 +931,7 @@ impl<'tcx> GotocCtx<'tcx> { self.decl_temp_variable(var1.typ().clone(), Some(var1.to_owned()), loc); let var2 = fargs.remove(0).with_location(loc); let assign_stmt = var1.assign(var2, loc); - let res_stmt = self.codegen_expr_to_place_stable(place, tmp); + let res_stmt = self.codegen_expr_to_place_stable(place, tmp, loc); Stmt::atomic_block(vec![decl_stmt, assign_stmt, res_stmt], loc) } @@ -1003,7 +1009,7 @@ impl<'tcx> GotocCtx<'tcx> { // fail on passing a reference to it unless we codegen this zero check. let copy_if_nontrivial = count_bytes.is_zero().ternary(dst, copy_call); let copy_expr = if let Some(p) = p { - self.codegen_expr_to_place_stable(p, copy_if_nontrivial) + self.codegen_expr_to_place_stable(p, copy_if_nontrivial, loc) } else { copy_if_nontrivial.as_stmt(loc) }; @@ -1057,14 +1063,19 @@ impl<'tcx> GotocCtx<'tcx> { // // This intrinsic replaces `ptr_guaranteed_eq` and `ptr_guaranteed_ne`: // https://doc.rust-lang.org/beta/std/primitive.pointer.html#method.guaranteed_eq - fn codegen_ptr_guaranteed_cmp(&mut self, mut fargs: Vec, p: &Place) -> Stmt { + fn codegen_ptr_guaranteed_cmp( + &mut self, + mut fargs: Vec, + p: &Place, + loc: Location, + ) -> Stmt { let a = fargs.remove(0); let b = fargs.remove(0); let place_type = self.place_ty_stable(p); let res_type = self.codegen_ty_stable(place_type); let eq_expr = a.eq(b); let cmp_expr = eq_expr.ternary(res_type.one(), res_type.zero()); - self.codegen_expr_to_place_stable(p, cmp_expr) + self.codegen_expr_to_place_stable(p, cmp_expr, loc) } /// Computes the offset from a pointer. @@ -1112,7 +1123,7 @@ impl<'tcx> GotocCtx<'tcx> { // Re-compute `dst_ptr` with standard addition to avoid conversion let dst_ptr = src_ptr.plus(offset); - let expr_place = self.codegen_expr_to_place_stable(p, dst_ptr); + let expr_place = self.codegen_expr_to_place_stable(p, dst_ptr, loc); Stmt::block(vec![bytes_overflow_check, overflow_check, expr_place], loc) } @@ -1131,7 +1142,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); - let offset_expr = self.codegen_expr_to_place_stable(p, offset_expr); + let offset_expr = self.codegen_expr_to_place_stable(p, offset_expr, loc); Stmt::block(vec![overflow_check, offset_expr], loc) } @@ -1163,7 +1174,8 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); - let offset_expr = self.codegen_expr_to_place_stable(p, offset_expr.cast_to(Type::size_t())); + let offset_expr = + self.codegen_expr_to_place_stable(p, offset_expr.cast_to(Type::size_t()), loc); Stmt::block(vec![overflow_check, non_negative_check, offset_expr], loc) } @@ -1210,12 +1222,18 @@ impl<'tcx> GotocCtx<'tcx> { /// Note(std): An earlier attempt to add alignment checks for both the argument and result types /// had catastrophic results in the regression. Hence, we don't perform any additional checks /// and only encode the transmute operation here. - fn codegen_intrinsic_transmute(&mut self, mut fargs: Vec, ret_ty: Ty, p: &Place) -> Stmt { + fn codegen_intrinsic_transmute( + &mut self, + mut fargs: Vec, + ret_ty: Ty, + p: &Place, + loc: Location, + ) -> Stmt { assert!(fargs.len() == 1, "transmute had unexpected arguments {fargs:?}"); let arg = fargs.remove(0); let cbmc_ret_ty = self.codegen_ty_stable(ret_ty); let expr = arg.transmute_to(cbmc_ret_ty, &self.symbol_table); - self.codegen_expr_to_place_stable(p, expr) + self.codegen_expr_to_place_stable(p, expr, loc) } // `raw_eq` determines whether the raw bytes of two values are equal. @@ -1243,7 +1261,7 @@ impl<'tcx> GotocCtx<'tcx> { let val = fargs.remove(0).cast_to(Type::void_pointer()); let layout = self.layout_of_stable(ty); if layout.size.bytes() == 0 { - self.codegen_expr_to_place_stable(p, Expr::int_constant(1, Type::c_bool())) + self.codegen_expr_to_place_stable(p, Expr::int_constant(1, Type::c_bool()), loc) } else { let sz = Expr::int_constant(layout.size.bytes(), Type::size_t()) .with_size_of_annotation(self.codegen_ty_stable(ty)); @@ -1251,21 +1269,16 @@ impl<'tcx> GotocCtx<'tcx> { .call(vec![dst, val, sz], loc) .eq(Type::c_int().zero()) .cast_to(Type::c_bool()); - self.codegen_expr_to_place_stable(p, e) + self.codegen_expr_to_place_stable(p, e, loc) } } // This is an operation that is primarily relevant for stacked borrow // checks. For Kani, we simply return the pointer. - fn codegen_retag_box_to_raw( - &mut self, - mut fargs: Vec, - p: &Place, - _loc: Location, - ) -> Stmt { + fn codegen_retag_box_to_raw(&mut self, mut fargs: Vec, p: &Place, loc: Location) -> Stmt { assert_eq!(fargs.len(), 1, "raw_box_to_box expected one argument"); let arg = fargs.remove(0); - self.codegen_expr_to_place_stable(p, arg) + self.codegen_expr_to_place_stable(p, arg, loc) } fn vtable_info( @@ -1273,7 +1286,7 @@ impl<'tcx> GotocCtx<'tcx> { info: VTableInfo, mut fargs: Vec, place: &Place, - _loc: Location, + loc: Location, ) -> Stmt { assert_eq!(fargs.len(), 1, "vtable intrinsics expects one raw pointer argument"); let vtable_obj = fargs @@ -1285,7 +1298,7 @@ impl<'tcx> GotocCtx<'tcx> { VTableInfo::Size => vtable_obj.member(typ::VTABLE_SIZE_FIELD, &self.symbol_table), VTableInfo::Align => vtable_obj.member(typ::VTABLE_ALIGN_FIELD, &self.symbol_table), }; - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, loc) } /// Gets the length for a `simd_shuffle*` instance, which comes in two @@ -1465,7 +1478,8 @@ impl<'tcx> GotocCtx<'tcx> { } self.tcx.dcx().abort_if_errors(); - self.codegen_expr_to_place_stable(p, vec.index_array(index)) + let loc = self.codegen_span_stable(span); + self.codegen_expr_to_place_stable(p, vec.index_array(index), loc) } /// Insert is a generic update of a single value in a SIMD vector. @@ -1512,7 +1526,7 @@ impl<'tcx> GotocCtx<'tcx> { vec![ decl, tmp.clone().index_array(index).assign(newval.cast_to(elem_ty), loc), - self.codegen_expr_to_place_stable(p, tmp), + self.codegen_expr_to_place_stable(p, tmp, loc), ], loc, ) @@ -1585,7 +1599,8 @@ impl<'tcx> GotocCtx<'tcx> { // Create the vector comparison expression let e = f(arg1, arg2, ret_typ); - self.codegen_expr_to_place_stable(p, e) + let loc = self.codegen_span_stable(span); + self.codegen_expr_to_place_stable(p, e, loc) } /// Codegen for `simd_div` and `simd_rem` intrinsics. @@ -1655,7 +1670,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); let res = op_fun(a, b); - let expr_place = self.codegen_expr_to_place_stable(p, res); + let expr_place = self.codegen_expr_to_place_stable(p, res, loc); Stmt::block(vec![check_stmt, expr_place], loc) } @@ -1713,7 +1728,7 @@ impl<'tcx> GotocCtx<'tcx> { _ => unreachable!("expected a simd shift intrinsic"), }; let res = op_fun(values, distances); - let expr_place = self.codegen_expr_to_place_stable(p, res); + let expr_place = self.codegen_expr_to_place_stable(p, res, loc); if distance_is_signed { let negative_check_stmt = self.codegen_assert_assume( @@ -1806,7 +1821,8 @@ impl<'tcx> GotocCtx<'tcx> { .collect(); self.tcx.dcx().abort_if_errors(); let cbmc_ret_ty = self.codegen_ty_stable(rust_ret_type); - self.codegen_expr_to_place_stable(p, Expr::vector_expr(cbmc_ret_ty, elems)) + let loc = self.codegen_span_stable(span); + self.codegen_expr_to_place_stable(p, Expr::vector_expr(cbmc_ret_ty, elems), loc) } /// A volatile load of a memory location: @@ -1836,7 +1852,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); let expr = src.dereference(); - let res_stmt = self.codegen_expr_to_place_stable(p, expr); + let res_stmt = self.codegen_expr_to_place_stable(p, expr, loc); Stmt::block(vec![align_check, res_stmt], loc) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index aed3f42c0156..5c7479d9fd66 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -163,7 +163,7 @@ impl<'tcx> GotocCtx<'tcx> { TerminatorKind::Return => { let rty = self.current_fn().instance_stable().fn_abi().unwrap().ret.ty; if rty.kind().is_unit() { - self.codegen_ret_unit() + self.codegen_ret_unit(loc) } else { let place = Place::from(RETURN_LOCAL); let place_expr = unwrap_or_return_codegen_unimplemented_stmt!( @@ -323,17 +323,12 @@ impl<'tcx> GotocCtx<'tcx> { } /// A special case handler to codegen `return ();` - fn codegen_ret_unit(&mut self) -> Stmt { + fn codegen_ret_unit(&mut self, loc: Location) -> Stmt { let is_file_local = false; let ty = self.codegen_ty_unit(); - let var = self.ensure_global_var( - FN_RETURN_VOID_VAR_NAME, - is_file_local, - ty, - Location::none(), - |_, _| None, - ); - Stmt::ret(Some(var), Location::none()) + let var = + self.ensure_global_var(FN_RETURN_VOID_VAR_NAME, is_file_local, ty, loc, |_, _| None); + Stmt::ret(Some(var), loc) } /// Generates Goto-C for MIR [TerminatorKind::Drop] calls. We only handle code _after_ Rust's "drop elaboration" @@ -584,13 +579,11 @@ impl<'tcx> GotocCtx<'tcx> { if instance.is_foreign_item() { vec![self.codegen_foreign_call(func_exp, fargs, destination, loc)] } else { - vec![ - self.codegen_expr_to_place_stable( - destination, - func_exp.call(fargs), - ) - .with_location(loc), - ] + vec![self.codegen_expr_to_place_stable( + destination, + func_exp.call(fargs), + loc, + )] } } }; @@ -612,8 +605,7 @@ impl<'tcx> GotocCtx<'tcx> { // Actually generate the function call and return. Stmt::block( vec![ - self.codegen_expr_to_place_stable(destination, func_expr.call(fargs)) - .with_location(loc), + self.codegen_expr_to_place_stable(destination, func_expr.call(fargs), loc), Stmt::goto(bb_label(target.unwrap()), loc), ], loc, @@ -710,7 +702,7 @@ impl<'tcx> GotocCtx<'tcx> { // Virtual function call and corresponding nonnull assertion. let call = fn_ptr.dereference().call(fargs.to_vec()); - let call_stmt = self.codegen_expr_to_place_stable(place, call).with_location(loc); + let call_stmt = self.codegen_expr_to_place_stable(place, call, loc); let call_stmt = if self.vtable_ctx.emit_vtable_restrictions { self.virtual_call_with_restricted_fn_ptr(trait_fat_ptr.typ().clone(), idx, call_stmt) } else { @@ -725,13 +717,18 @@ impl<'tcx> GotocCtx<'tcx> { /// A MIR [Place] is an L-value (i.e. the LHS of an assignment). /// /// In Kani, we slightly optimize the special case for Unit and don't assign anything. - pub(crate) fn codegen_expr_to_place_stable(&mut self, place: &Place, expr: Expr) -> Stmt { + pub(crate) fn codegen_expr_to_place_stable( + &mut self, + place: &Place, + expr: Expr, + loc: Location, + ) -> Stmt { if self.place_ty_stable(place).kind().is_unit() { - expr.as_stmt(Location::none()) + expr.as_stmt(loc) } else { unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place_stable(place)) .goto_expr - .assign(expr, Location::none()) + .assign(expr, loc) } } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 793c842d5367..dcfc269b9354 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -13,7 +13,7 @@ use crate::codegen_cprover_gotoc::codegen::{bb_label, PropertyClass}; use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::attributes::matches_diagnostic as matches_function; use crate::unwrap_or_return_codegen_unimplemented_stmt; -use cbmc::goto_program::{BuiltinFn, Expr, Location, Stmt, Type}; +use cbmc::goto_program::{BuiltinFn, Expr, Stmt, Type}; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; @@ -236,9 +236,9 @@ impl GotocHook for IsReadOk { Stmt::block( vec![ ret_place.goto_expr.assign(Expr::read_ok(ptr, size).cast_to(ret_type), loc), - Stmt::goto(bb_label(target), Location::none()), + Stmt::goto(bb_label(target), loc), ], - Location::none(), + loc, ) } } @@ -278,9 +278,9 @@ impl GotocHook for RustAlloc { .cast_to(Type::unsigned_int(8).to_pointer()), loc, ), - Stmt::goto(bb_label(target), Location::none()), + Stmt::goto(bb_label(target), loc), ], - Location::none(), + loc, ) } } From cf5a8f9c8c045789fb18b7f6728311ff0aa09fe0 Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Tue, 7 May 2024 17:29:54 +0100 Subject: [PATCH 22/52] Update Rust dependencies (#3175) Update Cargo.lock with the following package version changes: ``` anyhow 1.0.82 -> 1.0.83 getrandom 0.2.14 -> 0.2.15 num-bigint 0.4.4 -> 0.4.5 ryu 1.0.17 -> 1.0.18 syn 2.0.60 -> 2.0.61 winnow 0.6.7 -> 0.6.8 ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- Cargo.lock | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6100ab068816..b66cdbe5ac37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "autocfg" @@ -169,7 +169,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -481,7 +481,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -586,11 +586,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -880,9 +879,9 @@ checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -925,7 +924,7 @@ checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1031,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1046,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -1084,7 +1083,7 @@ checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1150,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1385,9 +1384,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -1415,5 +1414,5 @@ checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] From 584a3de4494c0cb0be8258fc23e7141120681a84 Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Tue, 7 May 2024 23:51:52 +0100 Subject: [PATCH 23/52] Bump Kani version to 0.51.0 (#3176) For reference, here is the auto-generated changelog ## What's Changed * Upgrade toolchain to 2024-04-18 and improve toolchain workflow by @celinval in https://github.com/model-checking/kani/pull/3149 * Automatic toolchain upgrade to nightly-2024-04-19 by @github-actions in https://github.com/model-checking/kani/pull/3150 * Stabilize cover statement and update contracts RFC by @celinval in https://github.com/model-checking/kani/pull/3091 * Automatic toolchain upgrade to nightly-2024-04-20 by @github-actions in https://github.com/model-checking/kani/pull/3154 * Bump tests/perf/s2n-quic from `2d5e891` to `5f88e54` by @dependabot in https://github.com/model-checking/kani/pull/3140 * Automatic cargo update to 2024-04-22 by @github-actions in https://github.com/model-checking/kani/pull/3157 * Automatic toolchain upgrade to nightly-2024-04-21 by @github-actions in https://github.com/model-checking/kani/pull/3158 * Bump tests/perf/s2n-quic from `5f88e54` to `9730578` by @dependabot in https://github.com/model-checking/kani/pull/3159 * Fix cargo audit error by @jaisnan in https://github.com/model-checking/kani/pull/3160 * Fix cbmc-update CI job by @tautschnig in https://github.com/model-checking/kani/pull/3156 * Automatic cargo update to 2024-04-29 by @github-actions in https://github.com/model-checking/kani/pull/3165 * Bump tests/perf/s2n-quic from `9730578` to `1436af7` by @dependabot in https://github.com/model-checking/kani/pull/3166 * Do not assume that ZST-typed symbols refer to unique objects by @tautschnig in https://github.com/model-checking/kani/pull/3134 * Fix copyright check for `expected` tests by @adpaco-aws in https://github.com/model-checking/kani/pull/3170 * Remove kani::Arbitrary from the modifies contract instrumentation by @feliperodri in https://github.com/model-checking/kani/pull/3169 * Automatic cargo update to 2024-05-06 by @github-actions in https://github.com/model-checking/kani/pull/3172 * Bump tests/perf/s2n-quic from `1436af7` to `6dd41e0` by @dependabot in https://github.com/model-checking/kani/pull/3174 * Avoid unnecessary uses of Location::none() by @tautschnig in https://github.com/model-checking/kani/pull/3173 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.50.0...kani-0.51.0 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> --- CHANGELOG.md | 13 +++++++++++++ Cargo.lock | 18 +++++++++--------- Cargo.toml | 2 +- cprover_bindings/Cargo.toml | 2 +- kani-compiler/Cargo.toml | 2 +- kani-driver/Cargo.toml | 2 +- kani_metadata/Cargo.toml | 2 +- library/kani/Cargo.toml | 2 +- library/kani_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 31 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e06601dfc3..7fc4cac54063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ This file contains notable changes (e.g. breaking changes, major changes, etc.) This file was introduced starting Kani 0.23.0, so it only contains changes from version 0.23.0 onwards. +## [0.51.0] + +## What's Changed + +* Do not assume that ZST-typed symbols refer to unique objects by @tautschnig in https://github.com/model-checking/kani/pull/3134 +* Remove `kani::Arbitrary` from the `modifies` contract instrumentation by @feliperodri in https://github.com/model-checking/kani/pull/3169 +* Emit source locations whenever possible to ease debugging and coverage reporting by @tautschnig in https://github.com/model-checking/kani/pull/3173 +* Rust toolchain upgraded to `nightly-2024-04-21` by @celinval + + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.50.0...kani-0.51.0 + + ## [0.50.0] ### Major Changes diff --git a/Cargo.lock b/Cargo.lock index b66cdbe5ac37..f3df3618f7b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "build-kani" -version = "0.50.0" +version = "0.51.0" dependencies = [ "anyhow", "cargo_metadata", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.50.0" +version = "0.51.0" dependencies = [ "lazy_static", "linear-map", @@ -411,14 +411,14 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kani" -version = "0.50.0" +version = "0.51.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.50.0" +version = "0.51.0" dependencies = [ "clap", "cprover_bindings", @@ -439,7 +439,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.50.0" +version = "0.51.0" dependencies = [ "anyhow", "cargo_metadata", @@ -467,7 +467,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.50.0" +version = "0.51.0" dependencies = [ "anyhow", "home", @@ -476,7 +476,7 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.50.0" +version = "0.51.0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -486,7 +486,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.50.0" +version = "0.51.0" dependencies = [ "clap", "cprover_bindings", @@ -992,7 +992,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "std" -version = "0.50.0" +version = "0.51.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index 93affb02856f..7b7d5cbe60a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.50.0" +version = "0.51.0" edition = "2021" description = "A bit-precise model checker for Rust." readme = "README.md" diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index d199558ff16a..844accd3914b 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index 244172715056..7315ff224bd8 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index ab83c2202bc9..6b25fe02a263 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.50.0" +version = "0.51.0" edition = "2021" description = "Build a project with Kani and run all proof harnesses" license = "MIT OR Apache-2.0" diff --git a/kani_metadata/Cargo.toml b/kani_metadata/Cargo.toml index 7936d943556b..645ed4da5fd2 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 97952dd7ab9e..4b158e41daa0 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index a54fb44d8b6c..0f9dda7136ad 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 6f9a380fc584..b83c9d3dcba0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -5,7 +5,7 @@ # Note: this package is intentionally named std to make sure the names of # standard library symbols are preserved name = "std" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/tools/build-kani/Cargo.toml b/tools/build-kani/Cargo.toml index eabb396e0923..21ad6be2a4b0 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.50.0" +version = "0.51.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From 52878c57fa26a83deb435a0ade3b2ad0209e681d Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 8 May 2024 23:36:03 -0700 Subject: [PATCH 24/52] Remove leftover code --- library/kani/src/lib.rs | 19 ----- library/kani_macros/src/lib.rs | 72 ------------------- .../kani_macros/src/sysroot/contracts/mod.rs | 2 +- .../src/sysroot/loop_contracts/mod.rs | 2 +- 4 files changed, 2 insertions(+), 93 deletions(-) diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 3126b88bf599..001f415fbe55 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -140,25 +140,6 @@ pub const fn assert(cond: bool, msg: &'static str) { #[rustc_diagnostic_item = "KaniCover"] pub const fn cover(_cond: bool, _msg: &'static str) {} -/// This function is only used for loop contract annotation. -/// It behaves as a placeholder to telling us where is the loop head and loop end -/// that we want to annotate to. -#[inline(never)] -#[rustc_diagnostic_item = "KaniLoopInvariant"] -pub const fn kani_loop_invariant(_cond: bool) {} - -/// This function is only used for loop contract annotation. -/// It behaves as a placeholder to telling us where the loop invariants stmts begin. -#[inline(never)] -#[rustc_diagnostic_item = "KaniLoopInvariantBegin"] -pub const fn kani_loop_invariant_begin() {} - -/// This function is only used for loop contract annotation. -/// It behaves as a placeholder to telling us where the loop invariants stmts end. -#[inline(never)] -#[rustc_diagnostic_item = "KaniLoopInvariantEnd"] -pub const fn kani_loop_invariant_end() {} - /// This creates an symbolic *valid* value of type `T`. You can assign the return value of this /// function to a variable that you want to make symbolic. /// diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 3ddb3c74be35..1d617fd52e81 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -223,7 +223,6 @@ mod sysroot { mod contracts; mod loop_contracts; - use contracts::helpers::*; pub use contracts::{ensures, modifies, proof_for_contract, requires, stub_verified}; pub use loop_contracts::loop_invariant; @@ -365,77 +364,6 @@ mod sysroot { kani_attribute!(stub); kani_attribute!(unstable); kani_attribute!(unwind); - - pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { - // Update the loop counter to distinguish loop invariants for different loops. - unsafe { - LOOP_INVARIANT_COUNT += 1; - } - - let mut loop_stmt: Stmt = syn::parse(item.clone()).unwrap(); - // Parse the loop invariant expression. - let inv_expr: Expr = chunks_by(TokenStream2::from(attr.clone()), is_token_stream_2_comma) - .map(syn::parse2::) - .filter_map(|expr| match expr { - Err(_) => None, - Ok(expr) => Some(expr), - }) - .collect::>()[0] - .clone(); - - // Annotate a place holder function call - // kani::kani_loop_invariant() - // at the end of the loop. - match loop_stmt { - Stmt::Expr(ref mut e, _) => match e { - Expr::While(ref mut ew) => { - // A while loop of the form - // ``` rust - // while guard { - // body - // } - // ``` - // is annotated as - // ``` rust - // kani::kani_loop_invariant_begin(); - // let __kani_loop_invariant_1 = ||(inv); - // kani::kani_loop_invariant_end(); - // while guard{ - // body - // kani::kani_loop_invariant(true); - // } - // ``` - let end_stmt: Stmt = syn::parse( - quote!( - kani::kani_loop_invariant(true);) - .into(), - ) - .unwrap(); - ew.body.stmts.push(end_stmt); - } - _ => (), - }, - _ => todo!("implement support for loops other than while loops."), - } - - // instrument the invariants as a closure, - // and the call to the closure between two placeholders - // TODO: all variables in inv_expr should be reference. - unsafe { - let closre_name = Ident::new( - &format!("__kani_loop_invariant_{LOOP_INVARIANT_COUNT}"), - Span::call_site(), - ); - quote!( - let #closre_name = ||(#inv_expr); - kani::kani_loop_invariant_begin(); - #closre_name(); - kani::kani_loop_invariant_end(); - #loop_stmt - ) - .into() - } - } } /// This module provides dummy implementations of Kani attributes which cannot be interpreted by diff --git a/library/kani_macros/src/sysroot/contracts/mod.rs b/library/kani_macros/src/sysroot/contracts/mod.rs index a9fad0afda2d..02d2b98eb8db 100644 --- a/library/kani_macros/src/sysroot/contracts/mod.rs +++ b/library/kani_macros/src/sysroot/contracts/mod.rs @@ -238,7 +238,7 @@ use syn::{parse_macro_input, Expr, ItemFn}; mod bootstrap; mod check; -pub mod helpers; +mod helpers; mod initialize; mod replace; mod shared; diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 7f3a4c0cbb8d..e85357a988d1 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -64,7 +64,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { ), } - quote!(; + quote!( #loop_stmt;) .into() } From 6547dcd2003197f31cf8f156d94ce27b8496cff6 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 8 May 2024 23:39:48 -0700 Subject: [PATCH 25/52] Remove unused import --- library/kani_macros/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 1d617fd52e81..59a001901c85 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -15,15 +15,11 @@ mod derive; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; -use proc_macro2::TokenStream as TokenStream2; - #[cfg(kani_sysroot)] use sysroot as attr_impl; -use proc_macro2::{Ident, Span}; #[cfg(not(kani_sysroot))] use regular as attr_impl; -use syn::{Expr, Stmt}; /// Marks a Kani proof harness /// From 319a8b95189e787a0f4ac7c42fe04d5600a188ad Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 8 May 2024 23:48:20 -0700 Subject: [PATCH 26/52] Fix format --- cprover_bindings/src/irep/to_irep.rs | 6 ++++-- .../src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs | 5 ++++- .../codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs | 3 +++ library/kani/src/loop_contracts.rs | 3 +++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index ff5fc2ce7cde..76ad47338e27 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -473,8 +473,10 @@ impl ToIrep for StmtBody { let stmt_goto = code_irep(IrepId::Goto, vec![]) .with_named_sub(IrepId::Destination, Irep::just_string_id(dest.to_string())); if loop_invariants.is_some() { - stmt_goto - .with_named_sub(IrepId::CSpecLoopInvariant, loop_invariants.clone().unwrap().to_irep(mm)) + stmt_goto.with_named_sub( + IrepId::CSpecLoopInvariant, + loop_invariants.clone().unwrap().to_irep(mm), + ) } else { stmt_goto } diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs index f7d0c09e1921..5e914fb380ff 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs @@ -1,3 +1,6 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + use crate::codegen_cprover_gotoc::codegen::bb_label; use cbmc::goto_program::{CIntType, Expr, Stmt, StmtBody, Type}; use stable_mir::mir::BasicBlockIdx; @@ -46,7 +49,7 @@ impl LoopContractsCtx { Self { invariants_block: Vec::new(), stage: LoopContractsStage::UserCode, - loop_contracts_enabled: loop_contracts_enabled, + loop_contracts_enabled, seen_bbidx: HashSet::new(), current_bbidx_label: None, loop_invariant_lhs: None, diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs index 893265e4773a..f52769124a49 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs @@ -1,3 +1,6 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + use super::hooks::GotocHook; use crate::codegen_cprover_gotoc::codegen::bb_label; use crate::codegen_cprover_gotoc::GotocCtx; diff --git a/library/kani/src/loop_contracts.rs b/library/kani/src/loop_contracts.rs index c3ef5932b7dd..90949c71ff3c 100644 --- a/library/kani/src/loop_contracts.rs +++ b/library/kani/src/loop_contracts.rs @@ -1,3 +1,6 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + /// This function is only used for loop contract annotation. /// It behaves as a placeholder to telling us where the loop invariants stmts begin. #[inline(never)] From 2569af0adb6084213375a0efc16906677456aa3a Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Thu, 9 May 2024 16:35:12 -0700 Subject: [PATCH 27/52] Loop invariants should be operands of named subs --- .../src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs index 5e914fb380ff..7f42bc71396f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs @@ -87,6 +87,7 @@ impl LoopContractsCtx { Type::CInteger(CIntType::Bool), ) .cast_to(Type::bool()) + .and(Expr::bool_true()) } /// Push the `s` onto the block if it is in the loop invariant block From 159e6adb6118cab347e0dc647d34782b9827f8d3 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 6 Aug 2024 13:40:04 -0500 Subject: [PATCH 28/52] Allow cloned reachability checks --- .../codegen/statement.rs | 2 +- .../context/loop_contracts_ctx.rs | 5 ++- kani-driver/src/call_goto_instrument.rs | 21 ++++++++++ kani-driver/src/cbmc_property_renderer.rs | 14 +++++-- .../ui/loop_contracts/small_slice_eq/expected | 1 + .../small_slice_eq/small_slcie_eq.rs | 40 +++++++++++++++++++ 6 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 tests/ui/loop_contracts/small_slice_eq/expected create mode 100644 tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 953afc3ec8d6..f6954a92ddda 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -186,7 +186,7 @@ impl<'tcx> GotocCtx<'tcx> { { Stmt::goto_with_loop_inv( bb_label(*target), - self.loop_contracts_ctx.extract_block(), + self.loop_contracts_ctx.extract_block(loc), loc, ) } else { diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs index 7f42bc71396f..0cec93876cc8 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::codegen_cprover_gotoc::codegen::bb_label; -use cbmc::goto_program::{CIntType, Expr, Stmt, StmtBody, Type}; +use cbmc::goto_program::{CIntType, Expr, Location, Stmt, StmtBody, Type}; use stable_mir::mir::BasicBlockIdx; use std::collections::HashSet; @@ -73,7 +73,7 @@ impl LoopContractsCtx { impl LoopContractsCtx { /// Returns the current block as a statement expression. /// Exit loop latch block. - pub fn extract_block(&mut self) -> Expr { + pub fn extract_block(&mut self, loc: Location) -> Expr { assert!(self.loop_invariant_lhs.is_some()); self.stage = LoopContractsStage::UserCode; self.invariants_block.push(self.loop_invariant_lhs.as_ref().unwrap().clone()); @@ -85,6 +85,7 @@ impl LoopContractsCtx { Expr::statement_expression( std::mem::take(&mut self.invariants_block), Type::CInteger(CIntType::Bool), + loc, ) .cast_to(Type::bool()) .and(Expr::bool_true()) diff --git a/kani-driver/src/call_goto_instrument.rs b/kani-driver/src/call_goto_instrument.rs index d31d4dae90f6..58bd23460124 100644 --- a/kani-driver/src/call_goto_instrument.rs +++ b/kani-driver/src/call_goto_instrument.rs @@ -39,6 +39,15 @@ impl KaniSession { self.instrument_contracts(harness, output)?; + if self + .args + .common_args + .unstable_features + .contains(kani_metadata::UnstableFeature::LoopContracts) + { + self.instrument_loop_contracts(output)?; + } + if self.args.checks.undefined_function_on() { self.add_library(output)?; self.undefined_functions(output)?; @@ -183,6 +192,18 @@ impl KaniSession { self.call_goto_instrument(&args) } + /// Apply annotated loop contracts. + pub fn instrument_loop_contracts(&self, file: &Path) -> Result<()> { + let args: Vec = vec![ + "--apply-loop-contracts".into(), + "--loop-contracts-no-unwind".into(), + "--disable-loop-contracts-side-effect-check".into(), + file.into(), + file.into(), + ]; + self.call_goto_instrument(&args) + } + /// Generate a .demangled.c file from the .c file using the `prettyName`s from the symbol table /// /// Currently, only top-level function names and (most) type names are demangled. diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index 4f32028b5866..919c3ca94319 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -815,7 +815,7 @@ fn annotate_properties_with_reach_results( mut properties: Vec, reach_checks: Vec, ) -> Vec { - let mut reach_map: HashMap = HashMap::new(); + let mut reach_map: HashMap> = HashMap::new(); let reach_desc_pat = Regex::new("KANI_CHECK_ID_.*_([0-9])*").unwrap(); // Collect data (ID, status) from reachability checks for reach_check in reach_checks { @@ -826,8 +826,7 @@ fn annotate_properties_with_reach_results( let check_id_str = format!("[{check_id}]"); // Get the status and insert into `reach_map` let status = reach_check.status; - let res_ins = reach_map.insert(check_id_str, status); - assert!(res_ins.is_none()); + reach_map.entry(check_id_str).or_insert(Vec::new()).push(status); } for prop in properties.iter_mut() { @@ -841,7 +840,14 @@ fn annotate_properties_with_reach_results( let reach_status_opt = reach_map.get(prop_match_id); // Update the reachability status of the property if let Some(reach_status) = reach_status_opt { - prop.reach = Some(*reach_status); + for status in reach_status { + if prop.reach.is_none() + || prop.reach.unwrap() == CheckStatus::Satisfied + || prop.reach.unwrap() == CheckStatus::Success + { + prop.reach = Some(*status); + } + } } } } diff --git a/tests/ui/loop_contracts/small_slice_eq/expected b/tests/ui/loop_contracts/small_slice_eq/expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/ui/loop_contracts/small_slice_eq/expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs b/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs new file mode 100644 index 000000000000..4cd45eff3b76 --- /dev/null +++ b/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs @@ -0,0 +1,40 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts --keep-temps --enable-unstable --cbmc-args --arrays-uf-always --no-standard-checks + +// Check if loop contracts are correctly applied. + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] +#![feature(ptr_sub_ptr)] +unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool { + debug_assert_eq!(x.len(), y.len()); + unsafe { + let (mut px, mut py) = (x.as_ptr(), y.as_ptr()); + let (pxend, pyend) = (px.add(x.len() - 4), py.add(y.len() - 4)); + #[kani::loop_invariant( px as isize >= x.as_ptr() as isize + && py as isize >= y.as_ptr() as isize + && px as isize - x.as_ptr() as isize == (py as isize - y.as_ptr() as isize))] + while px < pxend { + let vx = (px as *const u32).read_unaligned(); + let vy = (py as *const u32).read_unaligned(); + if vx != vy { + return false; + } + px = px.add(4); + py = py.add(4); + }; + let vx = (pxend as *const u32).read_unaligned(); + let vy = (pyend as *const u32).read_unaligned(); + vx == vy + } +} +#[kani::proof] +fn main() { + let mut a = [1;2000]; + let mut b = [1;2000]; + unsafe{ + small_slice_eq(&mut a, &mut b); + } +} From fbd17d66b1a70d97a2271784ab895386b53d7823 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 6 Aug 2024 13:44:30 -0500 Subject: [PATCH 29/52] Fix format --- kani-driver/src/cbmc_property_renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index 919c3ca94319..24aa2fe185b6 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -826,7 +826,7 @@ fn annotate_properties_with_reach_results( let check_id_str = format!("[{check_id}]"); // Get the status and insert into `reach_map` let status = reach_check.status; - reach_map.entry(check_id_str).or_insert(Vec::new()).push(status); + reach_map.entry(check_id_str).or_default().push(status); } for prop in properties.iter_mut() { From 1a81de24cb29f391addca0839090fb03ce47d097 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 7 Aug 2024 01:19:39 -0500 Subject: [PATCH 30/52] Fix format --- .../kani_macros/src/sysroot/loop_contracts/mod.rs | 3 +-- .../loop_contracts/small_slice_eq/small_slcie_eq.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index e85357a988d1..985b1e185480 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -63,8 +63,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { note = "for now, loop contracts is only supported for while-loops."; ), } - quote!( - #loop_stmt;) + {#loop_stmt}) .into() } diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs b/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs index 4cd45eff3b76..43568a31e288 100644 --- a/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs +++ b/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs @@ -13,7 +13,7 @@ unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool { unsafe { let (mut px, mut py) = (x.as_ptr(), y.as_ptr()); let (pxend, pyend) = (px.add(x.len() - 4), py.add(y.len() - 4)); - #[kani::loop_invariant( px as isize >= x.as_ptr() as isize + #[kani::loop_invariant( px as isize >= x.as_ptr() as isize && py as isize >= y.as_ptr() as isize && px as isize - x.as_ptr() as isize == (py as isize - y.as_ptr() as isize))] while px < pxend { @@ -24,17 +24,18 @@ unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool { } px = px.add(4); py = py.add(4); - }; + } let vx = (pxend as *const u32).read_unaligned(); let vy = (pyend as *const u32).read_unaligned(); vx == vy } } + #[kani::proof] fn main() { - let mut a = [1;2000]; - let mut b = [1;2000]; - unsafe{ - small_slice_eq(&mut a, &mut b); + let mut a = [1; 2000]; + let mut b = [1; 2000]; + unsafe { + small_slice_eq(&mut a, &mut b); } } From 3596dbeaaa3887c1401af0f4cdd5c85f951c9fe3 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 13 Aug 2024 02:39:03 -0500 Subject: [PATCH 31/52] Apply Adrian's comments --- cprover_bindings/src/goto_program/stmt.rs | 2 ++ cprover_bindings/src/irep/to_irep.rs | 7 ++-- .../context/loop_contracts_ctx.rs | 3 +- kani-driver/src/cbmc_property_renderer.rs | 1 + .../src/sysroot/loop_contracts/mod.rs | 32 ++++++++++--------- .../loop_contracts/simple_while_loop/expected | 2 ++ .../simple_while_loop/simple_while_loop.rs | 24 ++++++++++++++ .../ui/loop_contracts/small_slice_eq/expected | 1 + .../{small_slcie_eq.rs => small_slice_eq.rs} | 7 ++-- 9 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 tests/ui/loop_contracts/simple_while_loop/expected create mode 100644 tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs rename tests/ui/loop_contracts/small_slice_eq/{small_slcie_eq.rs => small_slice_eq.rs} (74%) diff --git a/cprover_bindings/src/goto_program/stmt.rs b/cprover_bindings/src/goto_program/stmt.rs index 012e23f71e2b..63e91be3acc6 100644 --- a/cprover_bindings/src/goto_program/stmt.rs +++ b/cprover_bindings/src/goto_program/stmt.rs @@ -84,6 +84,8 @@ pub enum StmtBody { /// `goto dest;` Goto { dest: InternedString, + // The loop invariants annotated to the goto, which can be + // applied as loop contracts in CBMC if it is a backward goto. loop_invariants: Option, }, /// `if (i) { t } else { e }` diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 452804cb5c8f..4ed1ba5604d9 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -519,11 +519,8 @@ impl ToIrep for StmtBody { StmtBody::Goto { dest, loop_invariants } => { let stmt_goto = code_irep(IrepId::Goto, vec![]) .with_named_sub(IrepId::Destination, Irep::just_string_id(dest.to_string())); - if loop_invariants.is_some() { - stmt_goto.with_named_sub( - IrepId::CSpecLoopInvariant, - loop_invariants.clone().unwrap().to_irep(mm), - ) + if let Some(inv) = loop_invariants { + stmt_goto.with_named_sub(IrepId::CSpecLoopInvariant, inv.to_irep(mm)) } else { stmt_goto } diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs index 0cec93876cc8..d7d9f8a8a39c 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs @@ -32,7 +32,6 @@ pub struct LoopContractsCtx { /// of the next backward jumping we codegen. /// We enter this state when codegen for `KaniLoopInvariantEnd`. /// We exit this state when codegen for the first backward jumping. -#[allow(dead_code)] #[derive(Debug, PartialEq)] enum LoopContractsStage { /// Codegen for user code as usual @@ -95,7 +94,7 @@ impl LoopContractsCtx { /// and return `skip`. Otherwise, do nothing and return `s`. pub fn push_onto_block(&mut self, s: Stmt) -> Stmt { if self.stage == LoopContractsStage::InvariantBlock { - // Attach the lable to the first Stmt in that block and reset it. + // Attach the label to the first `Stmt` in that block and reset it. let to_push = if self.current_bbidx_label.is_none() { s.clone() } else { diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index 24aa2fe185b6..03867ecf444a 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -841,6 +841,7 @@ fn annotate_properties_with_reach_results( // Update the reachability status of the property if let Some(reach_status) = reach_status_opt { for status in reach_status { + // Report if any copy of `prop` is not success. if prop.reach.is_none() || prop.reach.unwrap() == CheckStatus::Satisfied || prop.reach.unwrap() == CheckStatus::Success diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 985b1e185480..742ef4f541d0 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -10,6 +10,23 @@ use proc_macro_error::abort_call_site; use quote::quote; use syn::{Expr, Stmt}; +/// Expand loop contracts macros. +/// +/// A while loop of the form +/// ``` rust +/// while guard { +/// body +/// } +/// ``` +/// will be annotated as +/// ``` rust +/// while guard{ +/// body +/// kani::kani_loop_invariant_begin_marker(); +/// let __kani_loop_invariant: bool = inv; +/// kani::kani_loop_invariant_end_marker(); +/// } +/// ``` pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { let mut loop_stmt: Stmt = syn::parse(item.clone()).unwrap(); @@ -17,21 +34,6 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { match loop_stmt { Stmt::Expr(ref mut e, _) => match e { Expr::While(ref mut ew) => { - // A while loop of the form - // ``` rust - // while guard { - // body - // } - // ``` - // is annotated as - // ``` rust - // while guard{ - // body - // kani::kani_loop_invariant_begin_marker(); - // let __kani_loop_invariant: bool = inv; - // kani::kani_loop_invariant_end_marker(); - // } - // ``` let mut to_parse = quote!( let __kani_loop_invariant: bool = ); to_parse.extend(TokenStream2::from(attr.clone())); diff --git a/tests/ui/loop_contracts/simple_while_loop/expected b/tests/ui/loop_contracts/simple_while_loop/expected new file mode 100644 index 000000000000..000dba6ada5c --- /dev/null +++ b/tests/ui/loop_contracts/simple_while_loop/expected @@ -0,0 +1,2 @@ +- Description: "Check that loop invariant is preserved" +VERIFICATION:- SUCCESSFUL diff --git a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs new file mode 100644 index 000000000000..d4530896178a --- /dev/null +++ b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: + +//! Check if loop contracts is correctly applied. + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + + +#[kani::proof] +fn main() { + let mut x: u8 = kani::any_where(|i| *i >= 2); + + + #[kani::loop_invariant(x >= 2)] + while x > 2{ + x = x - 1; + }; + + + assert!(x == 2); +} diff --git a/tests/ui/loop_contracts/small_slice_eq/expected b/tests/ui/loop_contracts/small_slice_eq/expected index 34c886c358cb..000dba6ada5c 100644 --- a/tests/ui/loop_contracts/small_slice_eq/expected +++ b/tests/ui/loop_contracts/small_slice_eq/expected @@ -1 +1,2 @@ +- Description: "Check that loop invariant is preserved" VERIFICATION:- SUCCESSFUL diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs similarity index 74% rename from tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs rename to tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs index 43568a31e288..8e65798f533a 100644 --- a/tests/ui/loop_contracts/small_slice_eq/small_slcie_eq.rs +++ b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs @@ -1,9 +1,12 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z loop-contracts --keep-temps --enable-unstable --cbmc-args --arrays-uf-always --no-standard-checks +// kani-flags: -Z loop-contracts --enable-unstable --cbmc-args --arrays-uf-always --no-standard-checks -// Check if loop contracts are correctly applied. +//! Check if loop contracts are correctly applied. The flag --no-standard-checks should be +//! removed once same_object predicate is supported in loop contracts. +//! This function is originally the std function +//! https://github.com/rust-lang/rust/blob/master/library/core/src/str/pattern.rs#L1885 #![feature(stmt_expr_attributes)] #![feature(proc_macro_hygiene)] From 52101072fc574ae4f4329ec40584c0c0b43a3ca0 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 13 Aug 2024 12:34:13 -0500 Subject: [PATCH 32/52] Use with_loop_contracts --- cprover_bindings/src/goto_program/stmt.rs | 21 +++++++++---------- .../codegen/statement.rs | 7 ++----- .../simple_while_loop/simple_while_loop.rs | 7 ++----- .../small_slice_eq/small_slice_eq.rs | 2 +- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/cprover_bindings/src/goto_program/stmt.rs b/cprover_bindings/src/goto_program/stmt.rs index 63e91be3acc6..2649039dc001 100644 --- a/cprover_bindings/src/goto_program/stmt.rs +++ b/cprover_bindings/src/goto_program/stmt.rs @@ -291,17 +291,6 @@ impl Stmt { stmt!(Goto { dest, loop_invariants: None }, loc) } - /// `goto dest;` with loop invariant - pub fn goto_with_loop_inv>( - dest: T, - loop_invariants: Expr, - loc: Location, - ) -> Self { - let dest = dest.into(); - assert!(!dest.is_empty()); - stmt!(Goto { dest, loop_invariants: Some(loop_invariants) }, loc) - } - /// `if (i) { t } else { e }` or `if (i) { t }` pub fn if_then_else(i: Expr, t: Stmt, e: Option, loc: Location) -> Self { assert!(i.typ().is_bool()); @@ -341,6 +330,16 @@ impl Stmt { assert!(!label.is_empty()); stmt!(Label { label, body: self }, *self.location()) } + + /// `goto dest;` with loop invariant + pub fn with_loop_contracts(self, inv: Expr) -> Self { + if let Goto { dest, loop_invariants } = self.body() { + assert!(loop_invariants.is_none()); + stmt!(Goto { dest: *dest, loop_invariants: Some(inv) }, *self.location()) + } else { + unreachable!("Loop contracts should be annotated only to goto stmt") + } + } } /// Predicates diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index f6954a92ddda..09a8dc5c8d3e 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -184,11 +184,8 @@ impl<'tcx> GotocCtx<'tcx> { if self.loop_contracts_ctx.loop_contracts_enabled() && self.loop_contracts_ctx.is_loop_latch(target) { - Stmt::goto_with_loop_inv( - bb_label(*target), - self.loop_contracts_ctx.extract_block(loc), - loc, - ) + Stmt::goto(bb_label(*target), loc) + .with_loop_contracts(self.loop_contracts_ctx.extract_block(loc)) } else { Stmt::goto(bb_label(*target), loc) } diff --git a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs index d4530896178a..f7c7ef20392d 100644 --- a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs +++ b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs @@ -8,17 +8,14 @@ #![feature(stmt_expr_attributes)] #![feature(proc_macro_hygiene)] - #[kani::proof] fn main() { let mut x: u8 = kani::any_where(|i| *i >= 2); - #[kani::loop_invariant(x >= 2)] - while x > 2{ + while x > 2 { x = x - 1; - }; - + } assert!(x == 2); } diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs index 8e65798f533a..3fe922d934d6 100644 --- a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs +++ b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs @@ -5,7 +5,7 @@ //! Check if loop contracts are correctly applied. The flag --no-standard-checks should be //! removed once same_object predicate is supported in loop contracts. -//! This function is originally the std function +//! This function is originally the std function //! https://github.com/rust-lang/rust/blob/master/library/core/src/str/pattern.rs#L1885 #![feature(stmt_expr_attributes)] From 54168fd712f11d2075b8aa601c3409487201bf23 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 13 Aug 2024 12:54:58 -0500 Subject: [PATCH 33/52] Fix tests --- tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs index f7c7ef20392d..c959262373ac 100644 --- a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs +++ b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: +// kani-flags: -Z loop-contracts //! Check if loop contracts is correctly applied. From 64b66d3f2650d15d9e3579d1c6380b94bb429205 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 3 Sep 2024 12:49:01 -0500 Subject: [PATCH 34/52] Fix format --- kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs index ea8400ecd7cd..7170504aec78 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs @@ -37,7 +37,7 @@ impl<'tcx> GotocCtx<'tcx> { } else { self.codegen_terminator(term) }; - + self.current_fn_mut().push_onto_block(tcode.with_label(label)); } _ => { From 0b059687159e9b84403640774894c6f7d7a8b4af Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 11 Sep 2024 01:12:49 -0500 Subject: [PATCH 35/52] Refactor the loop contracts with closures --- cprover_bindings/src/irep/to_irep.rs | 5 +- .../codegen_cprover_gotoc/codegen/block.rs | 38 +-- .../codegen_cprover_gotoc/codegen/function.rs | 3 - .../codegen/statement.rs | 13 +- .../codegen_cprover_gotoc/context/goto_ctx.rs | 6 - .../context/loop_contracts_ctx.rs | 139 ---------- .../src/codegen_cprover_gotoc/context/mod.rs | 1 - .../codegen_cprover_gotoc/overrides/hooks.rs | 5 +- .../overrides/loop_contracts_hooks.rs | 47 +--- .../kani_middle/transform/loop_contracts.rs | 249 ++++++++++++++++++ .../src/kani_middle/transform/mod.rs | 3 + library/kani/src/lib.rs | 4 - library/kani/src/loop_contracts.rs | 26 -- library/kani_macros/src/lib.rs | 1 + .../src/sysroot/loop_contracts/mod.rs | 82 ++++-- 15 files changed, 331 insertions(+), 291 deletions(-) delete mode 100644 kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs create mode 100644 kani-compiler/src/kani_middle/transform/loop_contracts.rs delete mode 100644 library/kani/src/loop_contracts.rs diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 4ed1ba5604d9..48cb9706a0ba 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -520,7 +520,10 @@ impl ToIrep for StmtBody { let stmt_goto = code_irep(IrepId::Goto, vec![]) .with_named_sub(IrepId::Destination, Irep::just_string_id(dest.to_string())); if let Some(inv) = loop_invariants { - stmt_goto.with_named_sub(IrepId::CSpecLoopInvariant, inv.to_irep(mm)) + stmt_goto.with_named_sub( + IrepId::CSpecLoopInvariant, + inv.clone().and(Expr::bool_true()).to_irep(mm), + ) } else { stmt_goto } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs index 7170504aec78..1b28de887002 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs @@ -20,55 +20,25 @@ impl<'tcx> GotocCtx<'tcx> { pub fn codegen_block(&mut self, bb: BasicBlockIdx, bbd: &BasicBlock) { debug!(?bb, "codegen_block"); let label = bb_label(bb); - - // record the seen bbidx if loop contracts enabled - if self.loop_contracts_ctx.loop_contracts_enabled() { - self.loop_contracts_ctx.add_new_seen_bbidx(bb); - } - // the first statement should be labelled. if there is no statements, then the // terminator should be labelled. match bbd.statements.len() { 0 => { let term = &bbd.terminator; - let tcode = if self.loop_contracts_ctx.loop_contracts_enabled() { - let codegen_result = self.codegen_terminator(term); - self.loop_contracts_ctx.push_onto_block(codegen_result) - } else { - self.codegen_terminator(term) - }; - + let tcode = self.codegen_terminator(term); self.current_fn_mut().push_onto_block(tcode.with_label(label)); } _ => { let stmt = &bbd.statements[0]; - let scode = if self.loop_contracts_ctx.loop_contracts_enabled() { - let codegen_result = self.codegen_statement(stmt); - self.loop_contracts_ctx.push_onto_block(codegen_result) - } else { - self.codegen_statement(stmt) - }; - + let scode = self.codegen_statement(stmt); self.current_fn_mut().push_onto_block(scode.with_label(label)); for s in &bbd.statements[1..] { - let stmt = if self.loop_contracts_ctx.loop_contracts_enabled() { - let codegen_result = self.codegen_statement(s); - self.loop_contracts_ctx.push_onto_block(codegen_result) - } else { - self.codegen_statement(s) - }; + let stmt = self.codegen_statement(s); self.current_fn_mut().push_onto_block(stmt); } let term = &bbd.terminator; - - let tcode = if self.loop_contracts_ctx.loop_contracts_enabled() { - let codegen_result = self.codegen_terminator(term); - self.loop_contracts_ctx.push_onto_block(codegen_result) - } else { - self.codegen_terminator(term) - }; - + let tcode = self.codegen_terminator(term); self.current_fn_mut().push_onto_block(tcode); } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index ac9f1c30b146..34f8363f4948 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -56,9 +56,6 @@ impl<'tcx> GotocCtx<'tcx> { let old_sym = self.symbol_table.lookup(&name).unwrap(); let _trace_span = debug_span!("CodegenFunction", name = instance.name()).entered(); - if self.loop_contracts_ctx.loop_contracts_enabled() { - self.loop_contracts_ctx.enter_new_function(); - } if old_sym.is_function_definition() { debug!("Double codegen of {:?}", old_sym); } else { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 590ea61e102a..ed0178511126 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -192,21 +192,12 @@ impl<'tcx> GotocCtx<'tcx> { /// /// See also [`GotocCtx::codegen_statement`] for ordinary [Statement]s. pub fn codegen_terminator(&mut self, term: &Terminator) -> Stmt { - let loc: Location = self.codegen_span_stable(term.span); + let loc = self.codegen_span_stable(term.span); let _trace_span = debug_span!("CodegenTerminator", statement = ?term.kind).entered(); debug!("handling terminator {:?}", term); //TODO: Instead of doing location::none(), and updating, just putit in when we make the stmt. match &term.kind { - TerminatorKind::Goto { target } => { - if self.loop_contracts_ctx.loop_contracts_enabled() - && self.loop_contracts_ctx.is_loop_latch(target) - { - Stmt::goto(bb_label(*target), loc) - .with_loop_contracts(self.loop_contracts_ctx.extract_block(loc)) - } else { - Stmt::goto(bb_label(*target), loc) - } - } + TerminatorKind::Goto { target } => Stmt::goto(bb_label(*target), loc), TerminatorKind::SwitchInt { discr, targets } => { self.codegen_switch_int(discr, targets, loc) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs index 2fde1b9e21bc..3f17f4b87233 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -15,7 +15,6 @@ //! Any MIR specific functionality (e.g. codegen etc) should live in specialized files that use //! this structure as input. use super::current_fn::CurrentFnCtx; -use super::loop_contracts_ctx::LoopContractsCtx; use super::vtable_ctx::VtableCtx; use crate::codegen_cprover_gotoc::overrides::{fn_hooks, GotocHooks}; use crate::codegen_cprover_gotoc::utils::full_crate_name; @@ -75,8 +74,6 @@ pub struct GotocCtx<'tcx> { pub concurrent_constructs: UnsupportedConstructs, /// The body transformation agent. pub transformer: BodyTransformation, - /// The context for loop contracts code generation. - pub loop_contracts_ctx: LoopContractsCtx, } /// Constructor @@ -90,8 +87,6 @@ impl<'tcx> GotocCtx<'tcx> { let fhks = fn_hooks(); let symbol_table = SymbolTable::new(machine_model.clone()); let emit_vtable_restrictions = queries.args().emit_vtable_restrictions; - let loop_contracts_enabled = - queries.args().unstable_features.contains(&"loop-contracts".to_string()); GotocCtx { tcx, queries, @@ -108,7 +103,6 @@ impl<'tcx> GotocCtx<'tcx> { unsupported_constructs: FxHashMap::default(), concurrent_constructs: FxHashMap::default(), transformer, - loop_contracts_ctx: LoopContractsCtx::new(loop_contracts_enabled), } } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs deleted file mode 100644 index d7d9f8a8a39c..000000000000 --- a/kani-compiler/src/codegen_cprover_gotoc/context/loop_contracts_ctx.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -use crate::codegen_cprover_gotoc::codegen::bb_label; -use cbmc::goto_program::{CIntType, Expr, Location, Stmt, StmtBody, Type}; -use stable_mir::mir::BasicBlockIdx; -use std::collections::HashSet; - -pub struct LoopContractsCtx { - /// the GOTO block compiled from the corresponding loop invariants - invariants_block: Vec, - /// Which codegen state - stage: LoopContractsStage, - /// If enable loop contracts - loop_contracts_enabled: bool, - /// Seen basic block indexes. Used to decide if a jump is backward - seen_bbidx: HashSet, - /// Current unused bbidx label - current_bbidx_label: Option, - /// The lhs of evaluation of the loop invariant - loop_invariant_lhs: Option, -} - -/// We define two states: -/// 1. loop invariants block -/// In this state, we push all codegen stmts into the invariant block. -/// We enter this state when codegen for `KaniLoopInvariantBegin`. -/// We exit this state when codegen for `KaniLoopInvariantEnd`. -/// 2. loop latch block -/// In this state, we codegen a statement expression from the -/// invariant_block annotate the statement expression to the named sub -/// of the next backward jumping we codegen. -/// We enter this state when codegen for `KaniLoopInvariantEnd`. -/// We exit this state when codegen for the first backward jumping. -#[derive(Debug, PartialEq)] -enum LoopContractsStage { - /// Codegen for user code as usual - UserCode, - /// Codegen for loop invariants - InvariantBlock, - /// Codegen for loop latch node - FindingLatchNode, -} - -/// Constructor -impl LoopContractsCtx { - pub fn new(loop_contracts_enabled: bool) -> Self { - Self { - invariants_block: Vec::new(), - stage: LoopContractsStage::UserCode, - loop_contracts_enabled, - seen_bbidx: HashSet::new(), - current_bbidx_label: None, - loop_invariant_lhs: None, - } - } -} - -/// Getters -impl LoopContractsCtx { - pub fn loop_contracts_enabled(&self) -> bool { - self.loop_contracts_enabled - } - - /// decide if a GOTO with `target` is backward jump - pub fn is_loop_latch(&self, target: &BasicBlockIdx) -> bool { - self.stage == LoopContractsStage::FindingLatchNode && self.seen_bbidx.contains(target) - } -} - -/// Setters -impl LoopContractsCtx { - /// Returns the current block as a statement expression. - /// Exit loop latch block. - pub fn extract_block(&mut self, loc: Location) -> Expr { - assert!(self.loop_invariant_lhs.is_some()); - self.stage = LoopContractsStage::UserCode; - self.invariants_block.push(self.loop_invariant_lhs.as_ref().unwrap().clone()); - - // The first statement is the GOTO in the rhs of __kani_loop_invariant_begin() - // Ignore it - self.invariants_block.remove(0); - - Expr::statement_expression( - std::mem::take(&mut self.invariants_block), - Type::CInteger(CIntType::Bool), - loc, - ) - .cast_to(Type::bool()) - .and(Expr::bool_true()) - } - - /// Push the `s` onto the block if it is in the loop invariant block - /// and return `skip`. Otherwise, do nothing and return `s`. - pub fn push_onto_block(&mut self, s: Stmt) -> Stmt { - if self.stage == LoopContractsStage::InvariantBlock { - // Attach the label to the first `Stmt` in that block and reset it. - let to_push = if self.current_bbidx_label.is_none() { - s.clone() - } else { - s.clone().with_label(self.current_bbidx_label.clone().unwrap()) - }; - self.current_bbidx_label = None; - - match s.body() { - StmtBody::Assign { lhs, rhs: _ } => { - let lhs_stmt = lhs.clone().as_stmt(*s.location()); - self.loop_invariant_lhs = Some(lhs_stmt.clone()); - self.invariants_block.push(to_push); - } - _ => { - self.invariants_block.push(to_push); - } - }; - Stmt::skip(*s.location()) - } else { - s - } - } - - pub fn enter_loop_invariant_block(&mut self) { - assert!(self.invariants_block.is_empty()); - self.stage = LoopContractsStage::InvariantBlock; - } - - pub fn exit_loop_invariant_block(&mut self) { - self.stage = LoopContractsStage::FindingLatchNode; - } - - /// Enter a new function, reset the seen_bbidx set - pub fn enter_new_function(&mut self) { - self.seen_bbidx = HashSet::new() - } - - pub fn add_new_seen_bbidx(&mut self, bbidx: BasicBlockIdx) { - self.seen_bbidx.insert(bbidx); - self.current_bbidx_label = Some(bb_label(bbidx)); - } -} diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/mod.rs b/kani-compiler/src/codegen_cprover_gotoc/context/mod.rs index 0978b299e309..0053b9add18b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/mod.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/mod.rs @@ -8,7 +8,6 @@ mod current_fn; mod goto_ctx; -mod loop_contracts_ctx; mod vtable_ctx; pub use goto_ctx::GotocCtx; diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 7ffd976ee8a8..2bed9109918e 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -8,7 +8,7 @@ //! It would be too nasty if we spread around these sort of undocumented hooks in place, so //! this module addresses this issue. -use super::loop_contracts_hooks::{LoopInvariantBegin, LoopInvariantEnd}; +use super::loop_contracts_hooks::LoopInvariantRegister; use crate::codegen_cprover_gotoc::codegen::{bb_label, PropertyClass}; use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::attributes::matches_diagnostic as matches_function; @@ -556,8 +556,7 @@ pub fn fn_hooks() -> GotocHooks { Rc::new(MemCmp), Rc::new(UntrackedDeref), Rc::new(InitContracts), - Rc::new(LoopInvariantBegin), - Rc::new(LoopInvariantEnd), + Rc::new(LoopInvariantRegister), ], } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs index f52769124a49..b0395e8afd32 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs @@ -4,61 +4,34 @@ use super::hooks::GotocHook; use crate::codegen_cprover_gotoc::codegen::bb_label; use crate::codegen_cprover_gotoc::GotocCtx; -use crate::kani_middle::attributes::matches_diagnostic as matches_function; -use cbmc::goto_program::{Expr, Stmt}; +use crate::kani_middle::attributes::KaniAttributes; +use cbmc::goto_program::{CIntType, Expr, Stmt, Type}; use rustc_middle::ty::TyCtxt; +use rustc_span::Symbol; use stable_mir::mir::mono::Instance; use stable_mir::mir::{BasicBlockIdx, Place}; use stable_mir::ty::Span; -pub struct LoopInvariantBegin; +pub struct LoopInvariantRegister; -impl GotocHook for LoopInvariantBegin { +impl GotocHook for LoopInvariantRegister { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance.def, "KaniLoopInvariantBegin") + KaniAttributes::for_instance(tcx, instance).fn_marker() + == Some(Symbol::intern("kani_register_loop_contract")) } fn handle( &self, gcx: &mut GotocCtx, - _instance: Instance, + instance: Instance, fargs: Vec, _assign_to: &Place, target: Option, span: Span, ) -> Stmt { - assert_eq!(fargs.len(), 0); let loc = gcx.codegen_span_stable(span); - - // Start to record loop invariant statement - gcx.loop_contracts_ctx.enter_loop_invariant_block(); - - Stmt::goto(bb_label(target.unwrap()), loc) - } -} - -pub struct LoopInvariantEnd; - -impl GotocHook for LoopInvariantEnd { - fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance.def, "KaniLoopInvariantEnd") - } - - fn handle( - &self, - gcx: &mut GotocCtx, - _instance: Instance, - fargs: Vec, - _assign_to: &Place, - target: Option, - span: Span, - ) -> Stmt { - assert_eq!(fargs.len(), 0); - let loc = gcx.codegen_span_stable(span); - - // Stop to record loop invariant statement - gcx.loop_contracts_ctx.exit_loop_invariant_block(); - + let func_exp = gcx.codegen_func_expr(instance, loc); Stmt::goto(bb_label(target.unwrap()), loc) + .with_loop_contracts(func_exp.call(fargs).cast_to(Type::CInteger(CIntType::Bool))) } } diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs new file mode 100644 index 000000000000..dd8a1c8704a7 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -0,0 +1,249 @@ +use crate::kani_middle::codegen_units::CodegenUnit; +use crate::kani_middle::find_fn_def; +use crate::kani_middle::transform::body::{MutableBody, SourceInstruction}; +use crate::kani_middle::transform::{TransformPass, TransformationType}; +use crate::kani_middle::KaniAttributes; +use crate::kani_queries::QueryDb; +use crate::stable_mir::CrateDef; +use rustc_middle::ty::TyCtxt; +use rustc_span::Symbol; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::{BasicBlockIdx, Body, Operand, Terminator, TerminatorKind}; +use stable_mir::ty::{FnDef, RigidTy}; +use stable_mir::DefId; +use std::collections::VecDeque; +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; +use tracing::trace; + +/// This pass will perform the following operations: +/// 1. Replace the body of `kani_register_loop_contract` by `kani::internal::run_contract_fn` +/// to invoke the closure. +/// +/// 2. Replace the dummy call to the register function with the actual call, i.e., transform +/// +/// ```ignore +/// let kani_loop_invariant = || -> bool {inv}; +/// kani_register_loop_contract(kani_loop_invariant) +/// while guard { +/// loop_body; +/// kani_register_loop_contract(||->bool{true}); +/// } +/// ``` +/// +/// to +/// +/// ```ignore +/// let kani_loop_invariant = || -> bool {inv}; +/// while guard { +/// loop_body; +/// kani_register_loop_contract(kani_loop_invariant); +/// } +/// +/// ``` +/// +/// 3. Move the call to the register function to the loop latch terminator. This is required +/// as in MIR, there could be some `StorageDead` statements between register calls and +/// loop latches. +#[derive(Debug, Default)] +pub struct FunctionWithLoopContractPass { + /// Cache KaniRunContract function used to implement contracts. + run_contract_fn: Option, + /// Function and Arguments of register functions. + registered_args: HashMap)>, + /// The terminator we are moving to the loop latch. + loop_terminator: Option, +} + +impl TransformPass for FunctionWithLoopContractPass { + fn transformation_type() -> TransformationType + where + Self: Sized, + { + TransformationType::Stubbing + } + + fn is_enabled(&self, _query_db: &QueryDb) -> bool + where + Self: Sized, + { + true + } + + /// Transform the function body by replacing it with the stub body. + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + trace!(function=?instance.name(), "FunctionWithLoopContractPass::transform"); + match instance.ty().kind().rigid().unwrap() { + RigidTy::FnDef(_func, args) => { + if KaniAttributes::for_instance(tcx, instance).fn_marker() + == Some(Symbol::intern("kani_register_loop_contract")) + { + // Replace the body of the register function with `run_contract_fn`'s. + let run = Instance::resolve(self.run_contract_fn.unwrap(), args).unwrap(); + (true, run.body().unwrap()) + } else { + // Replace the dummy register call with the actual register call. + let mut new_body = MutableBody::from(body); + let mut contain_loop_contracts: bool = false; + + // Visit basic blocks in control flow order. + let mut visited: HashSet = HashSet::new(); + let mut queue: VecDeque = VecDeque::new(); + queue.push_back(0); + + while let Some(bbidx) = queue.pop_front() { + visited.insert(bbidx); + // We only need to transform basic block with terminators as calls + // to the register functions, no matter dummy or actual calls. + let terminator = new_body.blocks()[bbidx].terminator.clone(); + if let TerminatorKind::Call { + func: terminator_func, + args: terminator_args, + destination, + target, + unwind, + } = &terminator.kind + { + // Get the function signature of the terminator call. + let fn_kind = terminator_func.ty(&[]).unwrap().kind(); + let RigidTy::FnDef(fn_def, ..) = fn_kind.rigid().unwrap() else { + unreachable!() + }; + + if KaniAttributes::for_def_id(tcx, fn_def.def_id()).fn_marker() + == Some(Symbol::intern("kani_register_loop_contract")) + { + contain_loop_contracts = true; + + if self.registered_args.contains_key(&fn_def.def_id()) { + // This call is a dummy call as it is not the first call + // to the register function. + // Replace it with `self.loop_terminator`. + self.loop_terminator = Some(Terminator { + kind: TerminatorKind::Call { + func: self.registered_args[&fn_def.def_id()].0.clone(), + args: self.registered_args[&fn_def.def_id()].1.to_vec(), + destination: destination.clone(), + target: target.clone(), + unwind: unwind.clone(), + }, + span: terminator.span, + }); + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: bbidx }, + Terminator { + kind: TerminatorKind::Goto { target: target.unwrap() }, + span: terminator.span, + }, + ); + // Then move the loop terminator to the loop latch. + self.move_loop_terminator_to_loop_latch( + bbidx, + &mut new_body, + &mut visited, + ); + } else { + // This call is an actual call as it is the first call + // to the register function. + self.registered_args.insert( + fn_def.def_id(), + (terminator_func.clone(), terminator_args.clone()), + ); + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: bbidx }, + Terminator { + kind: TerminatorKind::Goto { target: target.unwrap() }, + span: terminator.span, + }, + ); + } + } + } + + // Add successors of the current basic blocks to + // the visiting queue. + for to_visit in terminator.successors() { + if visited.contains(&to_visit) { + continue; + } + queue.push_back(to_visit); + } + } + (contain_loop_contracts, new_body.into()) + } + } + _ => { + /* static variables case */ + (false, body) + } + } + } +} + +impl FunctionWithLoopContractPass { + pub fn new(tcx: TyCtxt, _unit: &CodegenUnit) -> FunctionWithLoopContractPass { + let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); + assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); + FunctionWithLoopContractPass { + run_contract_fn, + registered_args: HashMap::new(), + loop_terminator: None, + } + } + + // Replace the next loop latch---a terminator that targets some basic block in `visited`--- + // with `self.loop_terminator`. + // We assume that there is no branching terminator with more than one targets between the + // current basic block `bbidx` and the next loop latch. + fn move_loop_terminator_to_loop_latch( + &mut self, + bbidx: BasicBlockIdx, + new_body: &mut MutableBody, + visited: &mut HashSet, + ) { + let mut current_bbidx = bbidx; + while self.loop_terminator.is_some() { + if new_body.blocks()[current_bbidx].terminator.successors().len() != 1 { + // Assume that there is no branching between the register function cal + // and the loop latch. + unreachable!() + } + let target = new_body.blocks()[current_bbidx].terminator.successors()[0]; + + if visited.contains(&target) { + // Current basic block is the loop latch. + let Some(Terminator { + kind: + TerminatorKind::Call { + func: ref loop_terminator_func, + args: ref loop_terminator_args, + destination: ref loop_terminator_destination, + target: _loop_terminator_target, + unwind: ref loop_terminator_unwind, + }, + span: loop_terminator_span, + }) = self.loop_terminator + else { + unreachable!() + }; + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: current_bbidx }, + Terminator { + kind: TerminatorKind::Call { + func: loop_terminator_func.clone(), + args: loop_terminator_args.clone(), + destination: loop_terminator_destination.clone(), + target: Some(target), + unwind: loop_terminator_unwind.clone(), + }, + span: loop_terminator_span, + }, + ); + self.loop_terminator = None; + } else { + visited.insert(target); + current_bbidx = target; + } + } + } +} diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 2d963cd1d6eb..549124143fac 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -23,6 +23,7 @@ use crate::kani_middle::transform::check_uninit::{DelayedUbPass, UninitPass}; use crate::kani_middle::transform::check_values::ValidValuePass; use crate::kani_middle::transform::contracts::{AnyModifiesPass, FunctionWithContractPass}; use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; +use crate::kani_middle::transform::loop_contracts::FunctionWithLoopContractPass; use crate::kani_middle::transform::stubs::{ExternFnStubPass, FnStubPass}; use crate::kani_queries::QueryDb; use dump_mir_pass::DumpMirPass; @@ -41,6 +42,7 @@ mod contracts; mod dump_mir_pass; mod internal_mir; mod kani_intrinsics; +mod loop_contracts; mod stubs; /// Object used to retrieve a transformed instance body. @@ -74,6 +76,7 @@ impl BodyTransformation { // body that is relevant for this harness. transformer.add_pass(queries, AnyModifiesPass::new(tcx, &unit)); transformer.add_pass(queries, ValidValuePass { check_type: check_type.clone() }); + transformer.add_pass(queries, FunctionWithLoopContractPass::new(tcx, &unit)); // Putting `UninitPass` after `ValidValuePass` makes sure that the code generated by // `UninitPass` does not get unnecessarily instrumented by valid value checks. However, it // would also make sense to check that the values are initialized before checking their diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 20589fe3a969..c3e9ae5497cc 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -87,7 +87,3 @@ macro_rules! implies { pub(crate) use kani_macros::unstable_feature as unstable; pub mod contracts; - -mod loop_contracts; - -pub use loop_contracts::{kani_loop_invariant_begin_marker, kani_loop_invariant_end_marker}; diff --git a/library/kani/src/loop_contracts.rs b/library/kani/src/loop_contracts.rs deleted file mode 100644 index 90949c71ff3c..000000000000 --- a/library/kani/src/loop_contracts.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -/// This function is only used for loop contract annotation. -/// It behaves as a placeholder to telling us where the loop invariants stmts begin. -#[inline(never)] -#[rustc_diagnostic_item = "KaniLoopInvariantBegin"] -#[doc(hidden)] -#[crate::unstable( - feature = "loop-contracts", - issue = 3168, - reason = "experimental loop contracts support" -)] -pub const fn kani_loop_invariant_begin_marker() {} - -/// This function is only used for loop contract annotation. -/// It behaves as a placeholder to telling us where the loop invariants stmts end. -#[inline(never)] -#[rustc_diagnostic_item = "KaniLoopInvariantEnd"] -#[doc(hidden)] -#[crate::unstable( - feature = "loop-contracts", - issue = 3168, - reason = "experimental loop contracts support" -)] -pub const fn kani_loop_invariant_end_marker() {} diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 0979275dcde2..f23819099cea 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -8,6 +8,7 @@ // So we have to enable this on the commandline (see kani-rustc) with: // RUSTFLAGS="-Zcrate-attr=feature(register_tool) -Zcrate-attr=register_tool(kanitool)" #![feature(proc_macro_diagnostic)] +#![feature(proc_macro_span)] mod derive; // proc_macro::quote is nightly-only, so we'll cobble things together instead diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 742ef4f541d0..32cb52dfd3ea 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -5,11 +5,23 @@ //! use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; use proc_macro_error::abort_call_site; -use quote::quote; +use quote::{format_ident, quote}; +use syn::spanned::Spanned; use syn::{Expr, Stmt}; +fn generate_unique_id_from_span(stmt: &Stmt) -> String { + // Extract the span of the expression + let span = stmt.span().unwrap(); + + // Get the start and end line and column numbers + let start = span.start(); + let end = span.end(); + + // Create a tuple of location information (file path, start line, start column, end line, end column) + format!("_{:?}_{:?}_{:?}_{:?}", start.line(), start.column(), end.line(), end.column()) +} + /// Expand loop contracts macros. /// /// A while loop of the form @@ -20,43 +32,50 @@ use syn::{Expr, Stmt}; /// ``` /// will be annotated as /// ``` rust -/// while guard{ +/// #[inline(never)] +/// #[kanitool::fn_marker = "kani_register_loop_contract"] +/// const fn kani_register_loop_contract_id T>(f: F) -> T { +/// unreachable!() +/// } +/// let __kani_loop_invariant_id = || -> bool {inv}; +/// // The register function call with the actual invariant. +/// kani_register_loop_contract_id(__kani_loop_invariant_id); +/// while guard { /// body -/// kani::kani_loop_invariant_begin_marker(); -/// let __kani_loop_invariant: bool = inv; -/// kani::kani_loop_invariant_end_marker(); +/// // Call to the register function with a dummy argument +/// // for the sake of bypassing borrow checks. +/// kani_register_loop_contract_id(||->bool{true}); /// } /// ``` pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { + // parse the stmt of the loop let mut loop_stmt: Stmt = syn::parse(item.clone()).unwrap(); - // Annotate a place holder function call at the end of the loop. + // name of the loop invariant as closure of the form + // __kani_loop_invariant_#startline_#startcol_#endline_#endcol + let mut inv_name: String = "__kani_loop_invariant".to_owned(); + let loop_id = generate_unique_id_from_span(&loop_stmt); + inv_name.push_str(&loop_id); + let inv_ident = format_ident!("{}", inv_name); + + // expr of the loop invariant + let inv_expr: Expr = syn::parse(attr).unwrap(); + + // ident of the register function + let mut register_name: String = "kani_register_loop_contract".to_owned(); + register_name.push_str(&loop_id); + let register_ident = format_ident!("{}", register_name); + match loop_stmt { Stmt::Expr(ref mut e, _) => match e { Expr::While(ref mut ew) => { - let mut to_parse = quote!( - let __kani_loop_invariant: bool = ); - to_parse.extend(TokenStream2::from(attr.clone())); - to_parse.extend(quote!(;)); - let inv_assign_stmt: Stmt = syn::parse(to_parse.into()).unwrap(); - - // kani::kani_loop_invariant_begin_marker(); - let inv_begin_stmt: Stmt = syn::parse( - quote!( - kani::kani_loop_invariant_begin_marker();) - .into(), - ) - .unwrap(); - - // kani::kani_loop_invariant_end_marker(); + // kani_register_loop_contract(#inv_ident); let inv_end_stmt: Stmt = syn::parse( quote!( - kani::kani_loop_invariant_end_marker();) + #register_ident(||->bool{true});) .into(), ) .unwrap(); - ew.body.stmts.push(inv_begin_stmt); - ew.body.stmts.push(inv_assign_stmt); ew.body.stmts.push(inv_end_stmt); } _ => (), @@ -66,6 +85,17 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { ), } quote!( - {#loop_stmt}) + { + // Dummy function used to force the compiler to capture the environment. + // We cannot call closures inside constant functions. + // This function gets replaced by `kani::internal::call_closure`. + #[inline(never)] + #[kanitool::fn_marker = "kani_register_loop_contract"] + const fn #register_ident T>(f: F) -> T { + unreachable!() + } + let mut #inv_ident = || -> bool {#inv_expr}; + #register_ident(#inv_ident); + #loop_stmt}) .into() } From 3624655b2a4d71fa798a514f064e6abda533ad6d Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 11 Sep 2024 01:15:28 -0500 Subject: [PATCH 36/52] Add missing copyright --- kani-compiler/src/kani_middle/transform/loop_contracts.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index dd8a1c8704a7..42aa0d82350a 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -1,3 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This module contains code related to the MIR-to-MIR pass to enable loop contracts. +//! + use crate::kani_middle::codegen_units::CodegenUnit; use crate::kani_middle::find_fn_def; use crate::kani_middle::transform::body::{MutableBody, SourceInstruction}; From 68554dfee505dfc122940739f59c5c0c914b0617 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 11 Sep 2024 01:46:11 -0500 Subject: [PATCH 37/52] Use proc_macro_error2 --- library/kani_macros/src/sysroot/loop_contracts/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 32cb52dfd3ea..aa524f666298 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -5,7 +5,7 @@ //! use proc_macro::TokenStream; -use proc_macro_error::abort_call_site; +use proc_macro_error2::abort_call_site; use quote::{format_ident, quote}; use syn::spanned::Spanned; use syn::{Expr, Stmt}; From 75a82d5c9e6df4fd28252f0ab9a997d0dd299e58 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 11 Sep 2024 11:24:52 -0500 Subject: [PATCH 38/52] Provide locals to ty() --- kani-compiler/src/kani_middle/transform/loop_contracts.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 42aa0d82350a..ade3b2f2c20a 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -111,9 +111,12 @@ impl TransformPass for FunctionWithLoopContractPass { } = &terminator.kind { // Get the function signature of the terminator call. - let fn_kind = terminator_func.ty(&[]).unwrap().kind(); + let fn_kind = match terminator_func.ty(new_body.locals()) { + Ok(fn_ty) => fn_ty.kind(), + _ => continue, + }; let RigidTy::FnDef(fn_def, ..) = fn_kind.rigid().unwrap() else { - unreachable!() + continue; }; if KaniAttributes::for_def_id(tcx, fn_def.def_id()).fn_marker() From 20b8de046333f9b7f02525612fa56809b9ef03c7 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 11 Sep 2024 14:15:29 -0500 Subject: [PATCH 39/52] Do loop contracts transformation only for harnesses --- .../kani_middle/transform/loop_contracts.rs | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index ade3b2f2c20a..985d1339d52b 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -124,17 +124,19 @@ impl TransformPass for FunctionWithLoopContractPass { { contain_loop_contracts = true; - if self.registered_args.contains_key(&fn_def.def_id()) { + if let std::collections::hash_map::Entry::Occupied(entry) = + self.registered_args.entry(fn_def.def_id()) + { // This call is a dummy call as it is not the first call // to the register function. // Replace it with `self.loop_terminator`. self.loop_terminator = Some(Terminator { kind: TerminatorKind::Call { - func: self.registered_args[&fn_def.def_id()].0.clone(), - args: self.registered_args[&fn_def.def_id()].1.to_vec(), + func: entry.get().0.clone(), + args: entry.get().1.to_vec(), destination: destination.clone(), - target: target.clone(), - unwind: unwind.clone(), + target: *target, + unwind: *unwind, }, span: terminator.span, }); @@ -190,13 +192,19 @@ impl TransformPass for FunctionWithLoopContractPass { } impl FunctionWithLoopContractPass { - pub fn new(tcx: TyCtxt, _unit: &CodegenUnit) -> FunctionWithLoopContractPass { - let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); - assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); - FunctionWithLoopContractPass { - run_contract_fn, - registered_args: HashMap::new(), - loop_terminator: None, + pub fn new(tcx: TyCtxt, unit: &CodegenUnit) -> FunctionWithLoopContractPass { + if let Some(_harness) = unit.harnesses.first() { + let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); + assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); + FunctionWithLoopContractPass { + run_contract_fn, + registered_args: HashMap::new(), + loop_terminator: None, + } + } else { + // If reachability mode is PubFns or Tests, we just remove any loop contract logic. + // Note that in this path there is no proof harness. + FunctionWithLoopContractPass::default() } } @@ -243,7 +251,7 @@ impl FunctionWithLoopContractPass { args: loop_terminator_args.clone(), destination: loop_terminator_destination.clone(), target: Some(target), - unwind: loop_terminator_unwind.clone(), + unwind: *loop_terminator_unwind, }, span: loop_terminator_span, }, From 90c1c5c1c16164996238ecc0dc68ee1ec6c6d240 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Fri, 20 Sep 2024 11:33:44 -0500 Subject: [PATCH 40/52] Move loop-contracts-hook and add more documentation --- .../codegen_cprover_gotoc/overrides/hooks.rs | 40 ++++++++++++++++++- .../overrides/loop_contracts_hooks.rs | 37 ----------------- .../codegen_cprover_gotoc/overrides/mod.rs | 1 - .../kani_middle/transform/loop_contracts.rs | 10 ++--- kani_metadata/src/unstable.rs | 2 +- .../src/sysroot/loop_contracts/mod.rs | 32 ++++++++------- 6 files changed, 63 insertions(+), 59 deletions(-) delete mode 100644 kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 2bed9109918e..ff3fe01711bb 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -8,14 +8,16 @@ //! It would be too nasty if we spread around these sort of undocumented hooks in place, so //! this module addresses this issue. -use super::loop_contracts_hooks::LoopInvariantRegister; use crate::codegen_cprover_gotoc::codegen::{bb_label, PropertyClass}; use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::attributes::matches_diagnostic as matches_function; +use crate::kani_middle::attributes::KaniAttributes; use crate::unwrap_or_return_codegen_unimplemented_stmt; +use cbmc::goto_program::CIntType; use cbmc::goto_program::{BuiltinFn, Expr, Stmt, Type}; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; +use rustc_span::Symbol; use stable_mir::mir::mono::Instance; use stable_mir::mir::{BasicBlockIdx, Place}; use stable_mir::{ty::Span, CrateDef}; @@ -540,6 +542,42 @@ impl GotocHook for InitContracts { } } +/// A loop contract register function call is assumed to be +/// 1. of form `kani_register_loop_contract(inv)` where `inv` +/// is the closure wrapping loop invariants +/// 2. is the last statement in some loop, so that its `target`` is +/// the head of the loop +/// +/// Such call will be translate to +/// ```c +/// goto target +/// ``` +/// with loop invariants (call to the register function) annotated as +/// a named sub of the `goto`. +pub struct LoopInvariantRegister; + +impl GotocHook for LoopInvariantRegister { + fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { + KaniAttributes::for_instance(tcx, instance).fn_marker() + == Some(Symbol::intern("kani_register_loop_contract")) + } + + fn handle( + &self, + gcx: &mut GotocCtx, + instance: Instance, + fargs: Vec, + _assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + let loc = gcx.codegen_span_stable(span); + let func_exp = gcx.codegen_func_expr(instance, loc); + Stmt::goto(bb_label(target.unwrap()), loc) + .with_loop_contracts(func_exp.call(fargs).cast_to(Type::CInteger(CIntType::Bool))) + } +} + pub fn fn_hooks() -> GotocHooks { GotocHooks { hooks: vec![ diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs deleted file mode 100644 index b0395e8afd32..000000000000 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/loop_contracts_hooks.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -use super::hooks::GotocHook; -use crate::codegen_cprover_gotoc::codegen::bb_label; -use crate::codegen_cprover_gotoc::GotocCtx; -use crate::kani_middle::attributes::KaniAttributes; -use cbmc::goto_program::{CIntType, Expr, Stmt, Type}; -use rustc_middle::ty::TyCtxt; -use rustc_span::Symbol; -use stable_mir::mir::mono::Instance; -use stable_mir::mir::{BasicBlockIdx, Place}; -use stable_mir::ty::Span; - -pub struct LoopInvariantRegister; - -impl GotocHook for LoopInvariantRegister { - fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - KaniAttributes::for_instance(tcx, instance).fn_marker() - == Some(Symbol::intern("kani_register_loop_contract")) - } - - fn handle( - &self, - gcx: &mut GotocCtx, - instance: Instance, - fargs: Vec, - _assign_to: &Place, - target: Option, - span: Span, - ) -> Stmt { - let loc = gcx.codegen_span_stable(span); - let func_exp = gcx.codegen_func_expr(instance, loc); - Stmt::goto(bb_label(target.unwrap()), loc) - .with_loop_contracts(func_exp.call(fargs).cast_to(Type::CInteger(CIntType::Bool))) - } -} diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/mod.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/mod.rs index dfe92a31d6c0..3dda5076fb89 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/mod.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/mod.rs @@ -7,6 +7,5 @@ //! Instead, we use a "hook" to generate the correct CBMC intrinsic. mod hooks; -mod loop_contracts_hooks; pub use hooks::{fn_hooks, GotocHooks}; diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 985d1339d52b..51e2a6a27b4a 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -48,9 +48,9 @@ use tracing::trace; /// /// ``` /// -/// 3. Move the call to the register function to the loop latch terminator. This is required -/// as in MIR, there could be some `StorageDead` statements between register calls and -/// loop latches. +/// 3. Move the call to the register function to the loop latch terminator +/// to make sure that they will be correctly codegened as a backward goto +/// with loop contracts annotated. #[derive(Debug, Default)] pub struct FunctionWithLoopContractPass { /// Cache KaniRunContract function used to implement contracts. @@ -69,11 +69,11 @@ impl TransformPass for FunctionWithLoopContractPass { TransformationType::Stubbing } - fn is_enabled(&self, _query_db: &QueryDb) -> bool + fn is_enabled(&self, query_db: &QueryDb) -> bool where Self: Sized, { - true + query_db.args().unstable_features.contains(&"loop-contracts".to_string()) } /// Transform the function body by replacing it with the stub body. diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index c1fb730bfec1..3a690eff7c6e 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -83,7 +83,7 @@ pub enum UnstableFeature { SourceCoverage, /// Enable function contracts [RFC 9](https://model-checking.github.io/kani/rfc/rfcs/0009-function-contracts.html) FunctionContracts, - /// Enable loop contracts [RFC 12] + /// Enable loop contracts [RFC 12](https://model-checking.github.io/kani/rfc/rfcs/0012-loop-contracts.html) LoopContracts, /// Memory predicate APIs. MemPredicates, diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index aa524f666298..9a1579012e9c 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -10,18 +10,6 @@ use quote::{format_ident, quote}; use syn::spanned::Spanned; use syn::{Expr, Stmt}; -fn generate_unique_id_from_span(stmt: &Stmt) -> String { - // Extract the span of the expression - let span = stmt.span().unwrap(); - - // Get the start and end line and column numbers - let start = span.start(); - let end = span.end(); - - // Create a tuple of location information (file path, start line, start column, end line, end column) - format!("_{:?}_{:?}_{:?}_{:?}", start.line(), start.column(), end.line(), end.column()) -} - /// Expand loop contracts macros. /// /// A while loop of the form @@ -78,9 +66,13 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { .unwrap(); ew.body.stmts.push(inv_end_stmt); } - _ => (), + _ => { + abort_call_site!("`#[kani::loop_invariant]` is now only supported for while-loops."; + note = "for now, loop contracts is only supported for while-loops."; + ) + } }, - _ => abort_call_site!("`#[kani::loop_invariant]` is not only supported for while-loops."; + _ => abort_call_site!("`#[kani::loop_invariant]` is now only supported for while-loops."; note = "for now, loop contracts is only supported for while-loops."; ), } @@ -99,3 +91,15 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { #loop_stmt}) .into() } + +fn generate_unique_id_from_span(stmt: &Stmt) -> String { + // Extract the span of the expression + let span = stmt.span().unwrap(); + + // Get the start and end line and column numbers + let start = span.start(); + let end = span.end(); + + // Create a tuple of location information (file path, start line, start column, end line, end column) + format!("_{:?}_{:?}_{:?}_{:?}", start.line(), start.column(), end.line(), end.column()) +} From 608baeb4ab97249029ab4bbf41d77985af7974b5 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Fri, 20 Sep 2024 12:12:31 -0500 Subject: [PATCH 41/52] Fix format --- kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index ff3fe01711bb..4d998a1498d8 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -547,7 +547,7 @@ impl GotocHook for InitContracts { /// is the closure wrapping loop invariants /// 2. is the last statement in some loop, so that its `target`` is /// the head of the loop -/// +/// /// Such call will be translate to /// ```c /// goto target From 4617a2249f325768737787f03340ea8122ec1202 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 8 Oct 2024 14:48:22 -0500 Subject: [PATCH 42/52] Refactor to avoid violating borrow check --- .../codegen_cprover_gotoc/overrides/hooks.rs | 19 +- .../src/kani_middle/transform/body.rs | 9 + .../kani_middle/transform/loop_contracts.rs | 407 ++++++++++-------- .../src/kani_middle/transform/mod.rs | 4 +- kani-driver/src/call_goto_instrument.rs | 4 + .../src/sysroot/loop_contracts/mod.rs | 26 +- 6 files changed, 281 insertions(+), 188 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 4d998a1498d8..983326fb97a0 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -573,8 +573,23 @@ impl GotocHook for LoopInvariantRegister { ) -> Stmt { let loc = gcx.codegen_span_stable(span); let func_exp = gcx.codegen_func_expr(instance, loc); - Stmt::goto(bb_label(target.unwrap()), loc) - .with_loop_contracts(func_exp.call(fargs).cast_to(Type::CInteger(CIntType::Bool))) + // The last basic block in the register function are statements used to update the closure. + // We first codegen for them. + let body = gcx.transformer.body(gcx.tcx, instance); + let loop_head_block = &body.blocks[body.blocks.len() - 1].statements; + let mut loop_inv_stmts: Vec = Vec::new(); + for stmt in loop_head_block { + loop_inv_stmts.push(gcx.codegen_statement(&stmt)); + } + + loop_inv_stmts + .push(func_exp.call(fargs).cast_to(Type::CInteger(CIntType::Bool)).as_stmt(loc)); + + Stmt::goto(bb_label(target.unwrap()), loc).with_loop_contracts(Expr::statement_expression( + std::mem::take(&mut loop_inv_stmts), + Type::CInteger(CIntType::Bool), + loc, + )) } } diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 24fe5d8b2b2e..38686fe85b83 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -428,6 +428,15 @@ impl MutableBody { self.blocks.push(BasicBlock { statements: Vec::default(), terminator }) } + /// Replace statements from the given basic block + pub fn replace_statements( + &mut self, + source_instruction: &SourceInstruction, + new_stmts: Vec, + ) { + self.blocks.get_mut(source_instruction.bb()).unwrap().statements = new_stmts; + } + /// Replace a terminator from the given basic block pub fn replace_terminator( &mut self, diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 51e2a6a27b4a..b451a07cfcb9 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -4,71 +4,83 @@ //! This module contains code related to the MIR-to-MIR pass to enable loop contracts. //! -use crate::kani_middle::codegen_units::CodegenUnit; use crate::kani_middle::find_fn_def; -use crate::kani_middle::transform::body::{MutableBody, SourceInstruction}; -use crate::kani_middle::transform::{TransformPass, TransformationType}; +use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction}; +use crate::kani_middle::transform::{BodyTransformation, CallGraph, TransformationResult}; use crate::kani_middle::KaniAttributes; use crate::kani_queries::QueryDb; use crate::stable_mir::CrateDef; use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; -use stable_mir::mir::mono::Instance; -use stable_mir::mir::{BasicBlockIdx, Body, Operand, Terminator, TerminatorKind}; -use stable_mir::ty::{FnDef, RigidTy}; +use stable_mir::mir::mono::{Instance, MonoItem}; +use stable_mir::mir::{ + BasicBlock, BasicBlockIdx, Body, ConstOperand, Operand, Rvalue, Statement, StatementKind, + Terminator, TerminatorKind, +}; +use stable_mir::ty::{FnDef, MirConst, RigidTy}; use stable_mir::DefId; use std::collections::VecDeque; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; -use tracing::trace; + +use super::GlobalPass; /// This pass will perform the following operations: /// 1. Replace the body of `kani_register_loop_contract` by `kani::internal::run_contract_fn` /// to invoke the closure. /// -/// 2. Replace the dummy call to the register function with the actual call, i.e., transform +/// 2. Transform loops with contracts from +/// ```ignore +/// bb_idx: { +/// loop_head_stmts +/// _v = kani_register_loop_contract(move args) -> [return: terminator_target]; +/// } +/// +/// ... +/// loop_body_blocks +/// ... /// -/// ```ignore -/// let kani_loop_invariant = || -> bool {inv}; -/// kani_register_loop_contract(kani_loop_invariant) -/// while guard { -/// loop_body; -/// kani_register_loop_contract(||->bool{true}); -/// } -/// ``` +/// loop_latch_block: { +/// loop_latch_stmts +/// goto -> bb_idx; +/// } +/// ``` +/// to blocks +/// ```ignore +/// bb_idx: { +/// _v = true +/// goto -> terminator_target +/// } /// -/// to +/// ... +/// loop_body_blocks +/// ... /// -/// ```ignore -/// let kani_loop_invariant = || -> bool {inv}; -/// while guard { -/// loop_body; -/// kani_register_loop_contract(kani_loop_invariant); -/// } +/// loop_latch_block: { +/// loop_latch_stmts +/// goto -> bb_new_loop_latch; +/// } /// -/// ``` +/// bb_new_loop_latch: { +/// loop_head_body +/// _v = kani_register_loop_contract(move args) -> [return: bb_idx]; +/// } +/// ``` /// -/// 3. Move the call to the register function to the loop latch terminator -/// to make sure that they will be correctly codegened as a backward goto -/// with loop contracts annotated. +/// 3. Move the statements `loop_head_stmts` of loop head with contracts to the body of register +/// functions for later codegen them as statement_expression in CBMC loop contracts. #[derive(Debug, Default)] -pub struct FunctionWithLoopContractPass { +pub struct LoopContractPass { /// Cache KaniRunContract function used to implement contracts. run_contract_fn: Option, - /// Function and Arguments of register functions. - registered_args: HashMap)>, - /// The terminator we are moving to the loop latch. - loop_terminator: Option, + /// The bb_idx of the new loop latch block. + /// Keys are the original loop head. + new_loop_latches: HashMap, + /// Statements of loop head with loop contracts. + registered_stmts: HashMap>, } -impl TransformPass for FunctionWithLoopContractPass { - fn transformation_type() -> TransformationType - where - Self: Sized, - { - TransformationType::Stubbing - } - +impl GlobalPass for LoopContractPass { fn is_enabled(&self, query_db: &QueryDb) -> bool where Self: Sized, @@ -76,38 +88,146 @@ impl TransformPass for FunctionWithLoopContractPass { query_db.args().unstable_features.contains(&"loop-contracts".to_string()) } - /// Transform the function body by replacing it with the stub body. - fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { - trace!(function=?instance.name(), "FunctionWithLoopContractPass::transform"); + /// Run a transformation pass on the whole codegen unit. + fn transform( + &mut self, + tcx: TyCtxt, + _call_graph: &CallGraph, + _starting_items: &[MonoItem], + instances: Vec, + transformer: &mut BodyTransformation, + ) { + // First transform functions with loop contracts. + for instance in &instances { + let body = instance.body().unwrap(); + let (modified, new_body) = self.transform_main_body(tcx, body, *instance); + if modified { + transformer.cache.entry(*instance).and_modify(|transformation_result| { + *transformation_result = TransformationResult::Modified(new_body); + }); + } + } + + // Move loop head statements with loop contracts to the corresponding register functions. + for instance in &instances { + let body = instance.body().unwrap(); + let (modified, new_body) = self.transform_register_body(tcx, body, *instance); + if modified { + transformer.cache.entry(*instance).and_modify(|transformation_result| { + *transformation_result = TransformationResult::Modified(new_body); + }); + } + } + } +} + +impl LoopContractPass { + pub fn new(tcx: TyCtxt) -> LoopContractPass { + let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); + assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); + LoopContractPass { + run_contract_fn, + new_loop_latches: HashMap::new(), + registered_stmts: HashMap::new(), + } + } + + /// Transform bodies of loop contract register functions. + /// 1. Replace the body of the register function with `run_contract_fn`'s. + /// 2. Move statements `loop_head_stmts` of loop head with contracts to the body of register functions + /// and make them unreachable. We will later codegen them when when codegen the calls to register functions. + fn transform_register_body( + &mut self, + tcx: TyCtxt, + body: Body, + instance: Instance, + ) -> (bool, Body) { match instance.ty().kind().rigid().unwrap() { RigidTy::FnDef(_func, args) => { if KaniAttributes::for_instance(tcx, instance).fn_marker() == Some(Symbol::intern("kani_register_loop_contract")) + && self.registered_stmts.contains_key(&instance.def.def_id()) { // Replace the body of the register function with `run_contract_fn`'s. let run = Instance::resolve(self.run_contract_fn.unwrap(), args).unwrap(); - (true, run.body().unwrap()) + + // Move the stmts `loop_head_stmts` of loop head with contracts to the corresponding register functions. + let mut new_body = MutableBody::from(run.body().unwrap()); + new_body.insert_bb( + BasicBlock { + statements: self.registered_stmts[&instance.def.def_id()].clone(), + terminator: Terminator { + kind: TerminatorKind::Goto { target: new_body.blocks().len() }, + span: new_body.blocks()[0].terminator.span, + }, + }, + &mut SourceInstruction::Terminator { bb: 0 }, + InsertPosition::Before, + ); + new_body.replace_terminator( + &mut SourceInstruction::Terminator { bb: 0 }, + Terminator { + kind: TerminatorKind::Goto { target: new_body.blocks().len() - 2 }, + span: new_body.blocks()[0].terminator.span, + }, + ); + (true, new_body.into()) + } else { + (false, body) + } + } + _ => { + /* static variables case */ + (false, body) + } + } + } + + /// Transform main function bodies with loop contracts. + fn transform_main_body(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + match instance.ty().kind().rigid().unwrap() { + RigidTy::FnDef(_func, _args) => { + if KaniAttributes::for_instance(tcx, instance).fn_marker() + == Some(Symbol::intern("kani_register_loop_contract")) + { + // Register functions will be handled by `transform_register_body`. + (false, body) } else { - // Replace the dummy register call with the actual register call. let mut new_body = MutableBody::from(body); let mut contain_loop_contracts: bool = false; - // Visit basic blocks in control flow order. + // Visit basic blocks in control flow order (BFS). let mut visited: HashSet = HashSet::new(); let mut queue: VecDeque = VecDeque::new(); queue.push_back(0); - while let Some(bbidx) = queue.pop_front() { - visited.insert(bbidx); - // We only need to transform basic block with terminators as calls - // to the register functions, no matter dummy or actual calls. - let terminator = new_body.blocks()[bbidx].terminator.clone(); + while let Some(bb_idx) = queue.pop_front() { + visited.insert(bb_idx); + + let terminator = new_body.blocks()[bb_idx].terminator.clone(); + + // Redirect loop latches to the new latches. + if let TerminatorKind::Goto { target: terminator_target } = &terminator.kind + { + if self.new_loop_latches.contains_key(terminator_target) { + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: bb_idx }, + Terminator { + kind: TerminatorKind::Goto { + target: self.new_loop_latches[terminator_target], + }, + span: terminator.span, + }, + ); + } + } + if let TerminatorKind::Call { func: terminator_func, args: terminator_args, - destination, - target, - unwind, + destination: terminator_destination, + target: terminator_target, + unwind: terminator_unwind, } = &terminator.kind { // Get the function signature of the terminator call. @@ -119,55 +239,78 @@ impl TransformPass for FunctionWithLoopContractPass { continue; }; + // The basic blocks end with register functions are loop head blocks. if KaniAttributes::for_def_id(tcx, fn_def.def_id()).fn_marker() == Some(Symbol::intern("kani_register_loop_contract")) { contain_loop_contracts = true; - if let std::collections::hash_map::Entry::Occupied(entry) = - self.registered_args.entry(fn_def.def_id()) - { - // This call is a dummy call as it is not the first call - // to the register function. - // Replace it with `self.loop_terminator`. - self.loop_terminator = Some(Terminator { + // Replace the original loop head block + // ```ignore + // bb_idx: { + // loop_head_stmts + // _v = kani_register_loop_contract(move args) -> [return: terminator_target]; + // } + // ``` + // with + // ```ignore + // bb_idx: { + // _v = true; + // goto -> terminator_target + // } + // ``` + let mut new_loop_head_block_stmts: Vec = Vec::new(); + new_loop_head_block_stmts.push(Statement { + kind: StatementKind::Assign( + terminator_destination.clone(), + Rvalue::Use(Operand::Constant(ConstOperand { + span: terminator.span, + user_ty: None, + const_: MirConst::from_bool(true), + })), + ), + span: terminator.span, + }); + + self.registered_stmts.insert( + fn_def.def_id(), + new_body.blocks()[bb_idx].statements.clone(), + ); + + new_body.replace_statements( + &SourceInstruction::Terminator { bb: bb_idx }, + new_loop_head_block_stmts, + ); + + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: bb_idx }, + Terminator { + kind: TerminatorKind::Goto { target: bb_idx }, + span: terminator.span, + }, + ); + + // Insert a new basic block as the loop latch block, and later redirect + // all latches to the new loop latch block. + // ----- + // bb_new_loop_latch: { + // _v = kani_register_loop_contract(move args) -> [return: bb_idx]; + // } + new_body.insert_terminator( + &mut SourceInstruction::Terminator { bb: bb_idx }, + InsertPosition::After, + Terminator { kind: TerminatorKind::Call { - func: entry.get().0.clone(), - args: entry.get().1.to_vec(), - destination: destination.clone(), - target: *target, - unwind: *unwind, + func: terminator_func.clone(), + args: terminator_args.clone(), + destination: terminator_destination.clone(), + target: *terminator_target, + unwind: terminator_unwind.clone(), }, span: terminator.span, - }); - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: bbidx }, - Terminator { - kind: TerminatorKind::Goto { target: target.unwrap() }, - span: terminator.span, - }, - ); - // Then move the loop terminator to the loop latch. - self.move_loop_terminator_to_loop_latch( - bbidx, - &mut new_body, - &mut visited, - ); - } else { - // This call is an actual call as it is the first call - // to the register function. - self.registered_args.insert( - fn_def.def_id(), - (terminator_func.clone(), terminator_args.clone()), - ); - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: bbidx }, - Terminator { - kind: TerminatorKind::Goto { target: target.unwrap() }, - span: terminator.span, - }, - ); - } + }, + ); + self.new_loop_latches.insert(bb_idx, new_body.blocks().len() - 1); } } @@ -190,77 +333,3 @@ impl TransformPass for FunctionWithLoopContractPass { } } } - -impl FunctionWithLoopContractPass { - pub fn new(tcx: TyCtxt, unit: &CodegenUnit) -> FunctionWithLoopContractPass { - if let Some(_harness) = unit.harnesses.first() { - let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); - assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); - FunctionWithLoopContractPass { - run_contract_fn, - registered_args: HashMap::new(), - loop_terminator: None, - } - } else { - // If reachability mode is PubFns or Tests, we just remove any loop contract logic. - // Note that in this path there is no proof harness. - FunctionWithLoopContractPass::default() - } - } - - // Replace the next loop latch---a terminator that targets some basic block in `visited`--- - // with `self.loop_terminator`. - // We assume that there is no branching terminator with more than one targets between the - // current basic block `bbidx` and the next loop latch. - fn move_loop_terminator_to_loop_latch( - &mut self, - bbidx: BasicBlockIdx, - new_body: &mut MutableBody, - visited: &mut HashSet, - ) { - let mut current_bbidx = bbidx; - while self.loop_terminator.is_some() { - if new_body.blocks()[current_bbidx].terminator.successors().len() != 1 { - // Assume that there is no branching between the register function cal - // and the loop latch. - unreachable!() - } - let target = new_body.blocks()[current_bbidx].terminator.successors()[0]; - - if visited.contains(&target) { - // Current basic block is the loop latch. - let Some(Terminator { - kind: - TerminatorKind::Call { - func: ref loop_terminator_func, - args: ref loop_terminator_args, - destination: ref loop_terminator_destination, - target: _loop_terminator_target, - unwind: ref loop_terminator_unwind, - }, - span: loop_terminator_span, - }) = self.loop_terminator - else { - unreachable!() - }; - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: current_bbidx }, - Terminator { - kind: TerminatorKind::Call { - func: loop_terminator_func.clone(), - args: loop_terminator_args.clone(), - destination: loop_terminator_destination.clone(), - target: Some(target), - unwind: *loop_terminator_unwind, - }, - span: loop_terminator_span, - }, - ); - self.loop_terminator = None; - } else { - visited.insert(target); - current_bbidx = target; - } - } - } -} diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 549124143fac..43edbe1a9c7d 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -23,7 +23,7 @@ use crate::kani_middle::transform::check_uninit::{DelayedUbPass, UninitPass}; use crate::kani_middle::transform::check_values::ValidValuePass; use crate::kani_middle::transform::contracts::{AnyModifiesPass, FunctionWithContractPass}; use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; -use crate::kani_middle::transform::loop_contracts::FunctionWithLoopContractPass; +use crate::kani_middle::transform::loop_contracts::LoopContractPass; use crate::kani_middle::transform::stubs::{ExternFnStubPass, FnStubPass}; use crate::kani_queries::QueryDb; use dump_mir_pass::DumpMirPass; @@ -76,7 +76,6 @@ impl BodyTransformation { // body that is relevant for this harness. transformer.add_pass(queries, AnyModifiesPass::new(tcx, &unit)); transformer.add_pass(queries, ValidValuePass { check_type: check_type.clone() }); - transformer.add_pass(queries, FunctionWithLoopContractPass::new(tcx, &unit)); // Putting `UninitPass` after `ValidValuePass` makes sure that the code generated by // `UninitPass` does not get unnecessarily instrumented by valid value checks. However, it // would also make sense to check that the values are initialized before checking their @@ -198,6 +197,7 @@ pub struct GlobalPasses { impl GlobalPasses { pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { let mut global_passes = GlobalPasses { global_passes: vec![] }; + global_passes.add_global_pass(queries, LoopContractPass::new(tcx)); global_passes.add_global_pass(queries, DelayedUbPass::new(CheckType::new_assert(tcx))); global_passes.add_global_pass(queries, DumpMirPass::new(tcx)); global_passes diff --git a/kani-driver/src/call_goto_instrument.rs b/kani-driver/src/call_goto_instrument.rs index 58bd23460124..85264be53975 100644 --- a/kani-driver/src/call_goto_instrument.rs +++ b/kani-driver/src/call_goto_instrument.rs @@ -197,6 +197,10 @@ impl KaniSession { let args: Vec = vec![ "--apply-loop-contracts".into(), "--loop-contracts-no-unwind".into(), + // Because loop contracts now are wrapped in a closure which will be a side-effect expression in CBMC even they + // may not contain side-effect. So we disable the side-effect check for now and will implement a better check + // instead of simply rejecting function calls and statement expressions. + // See issue: diffblue/cbmc#8393 "--disable-loop-contracts-side-effect-check".into(), file.into(), file.into(), diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 9a1579012e9c..786ee58daa80 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -8,7 +8,8 @@ use proc_macro::TokenStream; use proc_macro_error2::abort_call_site; use quote::{format_ident, quote}; use syn::spanned::Spanned; -use syn::{Expr, Stmt}; +use syn::token::AndAnd; +use syn::{BinOp, Expr, ExprBinary, Stmt}; /// Expand loop contracts macros. /// @@ -25,14 +26,8 @@ use syn::{Expr, Stmt}; /// const fn kani_register_loop_contract_id T>(f: F) -> T { /// unreachable!() /// } -/// let __kani_loop_invariant_id = || -> bool {inv}; -/// // The register function call with the actual invariant. -/// kani_register_loop_contract_id(__kani_loop_invariant_id); -/// while guard { +/// while kani_register_loop_contract_id(|| -> bool {inv};) && guard { /// body -/// // Call to the register function with a dummy argument -/// // for the sake of bypassing borrow checks. -/// kani_register_loop_contract_id(||->bool{true}); /// } /// ``` pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { @@ -44,7 +39,6 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { let mut inv_name: String = "__kani_loop_invariant".to_owned(); let loop_id = generate_unique_id_from_span(&loop_stmt); inv_name.push_str(&loop_id); - let inv_ident = format_ident!("{}", inv_name); // expr of the loop invariant let inv_expr: Expr = syn::parse(attr).unwrap(); @@ -57,14 +51,18 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { match loop_stmt { Stmt::Expr(ref mut e, _) => match e { Expr::While(ref mut ew) => { - // kani_register_loop_contract(#inv_ident); - let inv_end_stmt: Stmt = syn::parse( + let new_cond: Expr = syn::parse( quote!( - #register_ident(||->bool{true});) + #register_ident(||->bool{#inv_expr})) .into(), ) .unwrap(); - ew.body.stmts.push(inv_end_stmt); + *(ew.cond) = Expr::Binary(ExprBinary { + attrs: Vec::new(), + left: Box::new(new_cond), + op: BinOp::And(AndAnd::default()), + right: ew.cond.clone(), + }); } _ => { abort_call_site!("`#[kani::loop_invariant]` is now only supported for while-loops."; @@ -86,8 +84,6 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { const fn #register_ident T>(f: F) -> T { unreachable!() } - let mut #inv_ident = || -> bool {#inv_expr}; - #register_ident(#inv_ident); #loop_stmt}) .into() } From 077985daac3d010c8c8fc23e2251d0adead042d5 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 8 Oct 2024 14:57:24 -0500 Subject: [PATCH 43/52] Fix format --- kani-driver/src/call_goto_instrument.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-driver/src/call_goto_instrument.rs b/kani-driver/src/call_goto_instrument.rs index 85264be53975..f98f48128c75 100644 --- a/kani-driver/src/call_goto_instrument.rs +++ b/kani-driver/src/call_goto_instrument.rs @@ -199,7 +199,7 @@ impl KaniSession { "--loop-contracts-no-unwind".into(), // Because loop contracts now are wrapped in a closure which will be a side-effect expression in CBMC even they // may not contain side-effect. So we disable the side-effect check for now and will implement a better check - // instead of simply rejecting function calls and statement expressions. + // instead of simply rejecting function calls and statement expressions. // See issue: diffblue/cbmc#8393 "--disable-loop-contracts-side-effect-check".into(), file.into(), From ab4f484c23b02740d2cb850d66d8902d9f109bb6 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 8 Oct 2024 15:14:42 -0500 Subject: [PATCH 44/52] Fix format --- .../src/codegen_cprover_gotoc/overrides/hooks.rs | 2 +- .../src/kani_middle/transform/loop_contracts.rs | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 3552a543ed08..328ca698c582 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -10,8 +10,8 @@ use crate::codegen_cprover_gotoc::GotocCtx; use crate::codegen_cprover_gotoc::codegen::{PropertyClass, bb_label}; -use crate::kani_middle::attributes::matches_diagnostic as matches_function; use crate::kani_middle::attributes::KaniAttributes; +use crate::kani_middle::attributes::matches_diagnostic as matches_function; use crate::unwrap_or_return_codegen_unimplemented_stmt; use cbmc::goto_program::CIntType; use cbmc::goto_program::{BuiltinFn, Expr, Stmt, Type}; diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index b451a07cfcb9..a3f44e7af4f5 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -4,21 +4,21 @@ //! This module contains code related to the MIR-to-MIR pass to enable loop contracts. //! +use crate::kani_middle::KaniAttributes; use crate::kani_middle::find_fn_def; use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction}; use crate::kani_middle::transform::{BodyTransformation, CallGraph, TransformationResult}; -use crate::kani_middle::KaniAttributes; use crate::kani_queries::QueryDb; use crate::stable_mir::CrateDef; use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; +use stable_mir::DefId; use stable_mir::mir::mono::{Instance, MonoItem}; use stable_mir::mir::{ BasicBlock, BasicBlockIdx, Body, ConstOperand, Operand, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use stable_mir::ty::{FnDef, MirConst, RigidTy}; -use stable_mir::DefId; use std::collections::VecDeque; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; @@ -165,7 +165,7 @@ impl LoopContractPass { InsertPosition::Before, ); new_body.replace_terminator( - &mut SourceInstruction::Terminator { bb: 0 }, + &SourceInstruction::Terminator { bb: 0 }, Terminator { kind: TerminatorKind::Goto { target: new_body.blocks().len() - 2 }, span: new_body.blocks()[0].terminator.span, @@ -259,8 +259,7 @@ impl LoopContractPass { // goto -> terminator_target // } // ``` - let mut new_loop_head_block_stmts: Vec = Vec::new(); - new_loop_head_block_stmts.push(Statement { + let new_loop_head_block_stmts: Vec = vec![Statement { kind: StatementKind::Assign( terminator_destination.clone(), Rvalue::Use(Operand::Constant(ConstOperand { @@ -270,7 +269,7 @@ impl LoopContractPass { })), ), span: terminator.span, - }); + }]; self.registered_stmts.insert( fn_def.def_id(), @@ -305,7 +304,7 @@ impl LoopContractPass { args: terminator_args.clone(), destination: terminator_destination.clone(), target: *terminator_target, - unwind: terminator_unwind.clone(), + unwind: *terminator_unwind, }, span: terminator.span, }, From 04492fb06f78e0ac005670a87d775ce265ae19d8 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 8 Oct 2024 16:43:10 -0500 Subject: [PATCH 45/52] Ignore loop contract macros when loop contracts disabled --- .../kani_middle/transform/loop_contracts.rs | 183 +++++++++++++++--- .../src/kani_middle/transform/mod.rs | 2 +- .../small_slice_eq/small_slice_eq.rs | 2 +- 3 files changed, 160 insertions(+), 27 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index a3f44e7af4f5..150e6a91fc12 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -78,14 +78,16 @@ pub struct LoopContractPass { new_loop_latches: HashMap, /// Statements of loop head with loop contracts. registered_stmts: HashMap>, + /// If loop contracts is enabled. + loop_contracts_enabled: bool, } impl GlobalPass for LoopContractPass { - fn is_enabled(&self, query_db: &QueryDb) -> bool + fn is_enabled(&self, _query_db: &QueryDb) -> bool where Self: Sized, { - query_db.args().unstable_features.contains(&"loop-contracts".to_string()) + true } /// Run a transformation pass on the whole codegen unit. @@ -97,38 +99,169 @@ impl GlobalPass for LoopContractPass { instances: Vec, transformer: &mut BodyTransformation, ) { - // First transform functions with loop contracts. - for instance in &instances { - let body = instance.body().unwrap(); - let (modified, new_body) = self.transform_main_body(tcx, body, *instance); - if modified { - transformer.cache.entry(*instance).and_modify(|transformation_result| { - *transformation_result = TransformationResult::Modified(new_body); - }); + if self.loop_contracts_enabled { + // First transform functions with loop contracts. + for instance in &instances { + let body = instance.body().unwrap(); + let (modified, new_body) = self.transform_main_body(tcx, body, *instance); + if modified { + transformer.cache.entry(*instance).and_modify(|transformation_result| { + *transformation_result = TransformationResult::Modified(new_body); + }); + } } - } - // Move loop head statements with loop contracts to the corresponding register functions. - for instance in &instances { - let body = instance.body().unwrap(); - let (modified, new_body) = self.transform_register_body(tcx, body, *instance); - if modified { - transformer.cache.entry(*instance).and_modify(|transformation_result| { - *transformation_result = TransformationResult::Modified(new_body); - }); + // Move loop head statements with loop contracts to the corresponding register functions. + for instance in &instances { + let body = instance.body().unwrap(); + let (modified, new_body) = self.transform_register_body(tcx, body, *instance); + if modified { + transformer.cache.entry(*instance).and_modify(|transformation_result| { + *transformation_result = TransformationResult::Modified(new_body); + }); + } + } + } else { + for instance in &instances { + let body = instance.body().unwrap(); + let (modified, new_body) = + self.remove_register_function_calls(tcx, body, *instance); + if modified { + transformer.cache.entry(*instance).and_modify(|transformation_result| { + *transformation_result = TransformationResult::Modified(new_body); + }); + } } } } } impl LoopContractPass { - pub fn new(tcx: TyCtxt) -> LoopContractPass { + pub fn new(tcx: TyCtxt, query_db: &QueryDb) -> LoopContractPass { let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); - assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); - LoopContractPass { - run_contract_fn, - new_loop_latches: HashMap::new(), - registered_stmts: HashMap::new(), + if run_contract_fn.is_some() { + LoopContractPass { + run_contract_fn, + new_loop_latches: HashMap::new(), + registered_stmts: HashMap::new(), + loop_contracts_enabled: query_db + .args() + .unstable_features + .contains(&"loop-contracts".to_string()), + } + } else { + LoopContractPass::default() + } + } + + fn remove_register_function_calls( + &mut self, + tcx: TyCtxt, + body: Body, + instance: Instance, + ) -> (bool, Body) { + match instance.ty().kind().rigid().unwrap() { + RigidTy::FnDef(_func, _args) => { + if KaniAttributes::for_instance(tcx, instance).fn_marker() + == Some(Symbol::intern("kani_register_loop_contract")) + { + (false, body) + } else { + let mut new_body = MutableBody::from(body); + let mut contain_loop_contracts: bool = false; + + // Visit basic blocks in control flow order (BFS). + let mut visited: HashSet = HashSet::new(); + let mut queue: VecDeque = VecDeque::new(); + queue.push_back(0); + + while let Some(bb_idx) = queue.pop_front() { + visited.insert(bb_idx); + + let terminator = new_body.blocks()[bb_idx].terminator.clone(); + + if let TerminatorKind::Call { + func: terminator_func, + args: _, + destination: terminator_destination, + target: terminator_target, + unwind: _, + } = &terminator.kind + { + // Get the function signature of the terminator call. + let fn_kind = match terminator_func.ty(new_body.locals()) { + Ok(fn_ty) => fn_ty.kind(), + _ => continue, + }; + let RigidTy::FnDef(fn_def, ..) = fn_kind.rigid().unwrap() else { + continue; + }; + + // The basic blocks end with register functions are loop head blocks. + if KaniAttributes::for_def_id(tcx, fn_def.def_id()).fn_marker() + == Some(Symbol::intern("kani_register_loop_contract")) + { + contain_loop_contracts = true; + + // Replace the original loop head block + // ```ignore + // bb_idx: { + // loop_head_stmts + // _v = kani_register_loop_contract(move args) -> [return: terminator_target]; + // } + // ``` + // with + // ```ignore + // bb_idx: { + // _v = true; + // goto -> terminator_target + // } + // ``` + let new_loop_head_block_stmts: Vec = vec![Statement { + kind: StatementKind::Assign( + terminator_destination.clone(), + Rvalue::Use(Operand::Constant(ConstOperand { + span: terminator.span, + user_ty: None, + const_: MirConst::from_bool(true), + })), + ), + span: terminator.span, + }]; + + new_body.replace_statements( + &SourceInstruction::Terminator { bb: bb_idx }, + new_loop_head_block_stmts, + ); + + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: bb_idx }, + Terminator { + kind: TerminatorKind::Goto { + target: terminator_target.unwrap(), + }, + span: terminator.span, + }, + ); + } + } + + // Add successors of the current basic blocks to + // the visiting queue. + for to_visit in terminator.successors() { + if visited.contains(&to_visit) { + continue; + } + queue.push_back(to_visit); + } + } + (contain_loop_contracts, new_body.into()) + } + } + _ => { + /* static variables case */ + (false, body) + } } } diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 8a0f1aa7aec4..14d718d142b5 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -191,7 +191,7 @@ pub struct GlobalPasses { impl GlobalPasses { pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { let mut global_passes = GlobalPasses { global_passes: vec![] }; - global_passes.add_global_pass(queries, LoopContractPass::new(tcx)); + global_passes.add_global_pass(queries, LoopContractPass::new(tcx, queries)); global_passes.add_global_pass(queries, DelayedUbPass::new(CheckType::new_assert(tcx))); global_passes.add_global_pass(queries, DumpMirPass::new(tcx)); global_passes diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs index 3fe922d934d6..311be5a00e7f 100644 --- a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs +++ b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs @@ -1,4 +1,4 @@ -// Copyright Kani Contributors +// Modifications Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Z loop-contracts --enable-unstable --cbmc-args --arrays-uf-always --no-standard-checks From 7920f91cefa6fb36604a6e3f376f9d1a12daff27 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 8 Oct 2024 16:51:59 -0500 Subject: [PATCH 46/52] Fix copyright --- tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs index 311be5a00e7f..cb3c6f51240d 100644 --- a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs +++ b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs @@ -1,12 +1,15 @@ -// Modifications Copyright Kani Contributors +// Copyright rustc Contributors +// Adapted from rust std: https://github.com/rust-lang/rust/blob/master/library/core/src/str/pattern.rs#L1885 +// // SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Modifications Copyright Kani Contributors +// See GitHub history for details. // kani-flags: -Z loop-contracts --enable-unstable --cbmc-args --arrays-uf-always --no-standard-checks //! Check if loop contracts are correctly applied. The flag --no-standard-checks should be //! removed once same_object predicate is supported in loop contracts. -//! This function is originally the std function -//! https://github.com/rust-lang/rust/blob/master/library/core/src/str/pattern.rs#L1885 #![feature(stmt_expr_attributes)] #![feature(proc_macro_hygiene)] From 52f94a8d3e70f343c1866256348be171c205b089 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Thu, 10 Oct 2024 16:23:46 -0500 Subject: [PATCH 47/52] Add checkks for unspport invariants --- .../codegen_cprover_gotoc/overrides/hooks.rs | 18 +- .../src/kani_middle/transform/body.rs | 16 +- .../kani_middle/transform/loop_contracts.rs | 544 +++++++----------- .../src/kani_middle/transform/mod.rs | 2 +- 4 files changed, 209 insertions(+), 371 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 328ca698c582..8675d3fad3f3 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -573,23 +573,9 @@ impl GotocHook for LoopInvariantRegister { ) -> Stmt { let loc = gcx.codegen_span_stable(span); let func_exp = gcx.codegen_func_expr(instance, loc); - // The last basic block in the register function are statements used to update the closure. - // We first codegen for them. - let body = gcx.transformer.body(gcx.tcx, instance); - let loop_head_block = &body.blocks[body.blocks.len() - 1].statements; - let mut loop_inv_stmts: Vec = Vec::new(); - for stmt in loop_head_block { - loop_inv_stmts.push(gcx.codegen_statement(&stmt)); - } - - loop_inv_stmts - .push(func_exp.call(fargs).cast_to(Type::CInteger(CIntType::Bool)).as_stmt(loc)); - Stmt::goto(bb_label(target.unwrap()), loc).with_loop_contracts(Expr::statement_expression( - std::mem::take(&mut loop_inv_stmts), - Type::CInteger(CIntType::Bool), - loc, - )) + Stmt::goto(bb_label(target.unwrap()), loc) + .with_loop_contracts(func_exp.call(fargs).cast_to(Type::CInteger(CIntType::Bool))) } } diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 38686fe85b83..bf87d59c1036 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -59,6 +59,11 @@ impl MutableBody { self.arg_count } + #[allow(dead_code)] + pub fn var_debug_info(&self) -> &Vec { + &self.var_debug_info + } + /// Create a mutable body from the original MIR body. pub fn from(body: Body) -> Self { MutableBody { @@ -428,15 +433,6 @@ impl MutableBody { self.blocks.push(BasicBlock { statements: Vec::default(), terminator }) } - /// Replace statements from the given basic block - pub fn replace_statements( - &mut self, - source_instruction: &SourceInstruction, - new_stmts: Vec, - ) { - self.blocks.get_mut(source_instruction.bb()).unwrap().statements = new_stmts; - } - /// Replace a terminator from the given basic block pub fn replace_terminator( &mut self, @@ -568,7 +564,7 @@ pub trait MutMirVisitor { StatementKind::Assign(_, rvalue) => { self.visit_rvalue(rvalue); } - StatementKind::Intrinsic(intrisic) => match intrisic { + StatementKind::Intrinsic(intrinsic) => match intrinsic { NonDivergingIntrinsic::Assume(operand) => { self.visit_operand(operand); } diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 150e6a91fc12..2a6b57afdde7 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -5,25 +5,24 @@ //! use crate::kani_middle::KaniAttributes; +use crate::kani_middle::codegen_units::CodegenUnit; use crate::kani_middle::find_fn_def; +use crate::kani_middle::transform::TransformationType; use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction}; -use crate::kani_middle::transform::{BodyTransformation, CallGraph, TransformationResult}; use crate::kani_queries::QueryDb; use crate::stable_mir::CrateDef; use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; -use stable_mir::DefId; -use stable_mir::mir::mono::{Instance, MonoItem}; +use stable_mir::mir::mono::Instance; use stable_mir::mir::{ - BasicBlock, BasicBlockIdx, Body, ConstOperand, Operand, Rvalue, Statement, StatementKind, - Terminator, TerminatorKind, + BasicBlockIdx, Body, ConstOperand, Operand, Place, Rvalue, StatementKind, Terminator, + TerminatorKind, VarDebugInfoContents, }; use stable_mir::ty::{FnDef, MirConst, RigidTy}; -use std::collections::VecDeque; -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, HashSet, VecDeque}; use std::fmt::Debug; -use super::GlobalPass; +use super::TransformPass; /// This pass will perform the following operations: /// 1. Replace the body of `kani_register_loop_contract` by `kani::internal::run_contract_fn` @@ -63,109 +62,44 @@ use super::GlobalPass; /// /// bb_new_loop_latch: { /// loop_head_body -/// _v = kani_register_loop_contract(move args) -> [return: bb_idx]; +/// _v = kani_register_loop_contract(move args) -> [return: terminator_target]; /// } /// ``` -/// -/// 3. Move the statements `loop_head_stmts` of loop head with contracts to the body of register -/// functions for later codegen them as statement_expression in CBMC loop contracts. #[derive(Debug, Default)] pub struct LoopContractPass { /// Cache KaniRunContract function used to implement contracts. run_contract_fn: Option, - /// The bb_idx of the new loop latch block. - /// Keys are the original loop head. + /// The map from original loop head to the new loop latch. + /// We use this map to redirect all original loop latches to a new single loop latch. new_loop_latches: HashMap, - /// Statements of loop head with loop contracts. - registered_stmts: HashMap>, - /// If loop contracts is enabled. - loop_contracts_enabled: bool, } -impl GlobalPass for LoopContractPass { - fn is_enabled(&self, _query_db: &QueryDb) -> bool +impl TransformPass for LoopContractPass { + /// The type of transformation that this pass implements. + fn transformation_type() -> TransformationType where Self: Sized, { - true - } - - /// Run a transformation pass on the whole codegen unit. - fn transform( - &mut self, - tcx: TyCtxt, - _call_graph: &CallGraph, - _starting_items: &[MonoItem], - instances: Vec, - transformer: &mut BodyTransformation, - ) { - if self.loop_contracts_enabled { - // First transform functions with loop contracts. - for instance in &instances { - let body = instance.body().unwrap(); - let (modified, new_body) = self.transform_main_body(tcx, body, *instance); - if modified { - transformer.cache.entry(*instance).and_modify(|transformation_result| { - *transformation_result = TransformationResult::Modified(new_body); - }); - } - } - - // Move loop head statements with loop contracts to the corresponding register functions. - for instance in &instances { - let body = instance.body().unwrap(); - let (modified, new_body) = self.transform_register_body(tcx, body, *instance); - if modified { - transformer.cache.entry(*instance).and_modify(|transformation_result| { - *transformation_result = TransformationResult::Modified(new_body); - }); - } - } - } else { - for instance in &instances { - let body = instance.body().unwrap(); - let (modified, new_body) = - self.remove_register_function_calls(tcx, body, *instance); - if modified { - transformer.cache.entry(*instance).and_modify(|transformation_result| { - *transformation_result = TransformationResult::Modified(new_body); - }); - } - } - } + TransformationType::Stubbing } -} -impl LoopContractPass { - pub fn new(tcx: TyCtxt, query_db: &QueryDb) -> LoopContractPass { - let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); - if run_contract_fn.is_some() { - LoopContractPass { - run_contract_fn, - new_loop_latches: HashMap::new(), - registered_stmts: HashMap::new(), - loop_contracts_enabled: query_db - .args() - .unstable_features - .contains(&"loop-contracts".to_string()), - } - } else { - LoopContractPass::default() - } + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized, + { + query_db.args().unstable_features.contains(&"loop-contracts".to_string()) } - fn remove_register_function_calls( - &mut self, - tcx: TyCtxt, - body: Body, - instance: Instance, - ) -> (bool, Body) { + /// Run a transformation pass on the whole codegen unit. + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { match instance.ty().kind().rigid().unwrap() { - RigidTy::FnDef(_func, _args) => { + RigidTy::FnDef(_func, args) => { if KaniAttributes::for_instance(tcx, instance).fn_marker() == Some(Symbol::intern("kani_register_loop_contract")) { - (false, body) + // Replace the body of the register function with `run_contract_fn`'s. + let run = Instance::resolve(self.run_contract_fn.unwrap(), args).unwrap(); + (true, run.body().unwrap()) } else { let mut new_body = MutableBody::from(body); let mut contain_loop_contracts: bool = false; @@ -173,78 +107,17 @@ impl LoopContractPass { // Visit basic blocks in control flow order (BFS). let mut visited: HashSet = HashSet::new(); let mut queue: VecDeque = VecDeque::new(); + // Visit blocks in loops only when there is no blocks in queue. + let mut loop_queue: VecDeque = VecDeque::new(); queue.push_back(0); - while let Some(bb_idx) = queue.pop_front() { + while let Some(bb_idx) = queue.pop_front().or(loop_queue.pop_front()) { visited.insert(bb_idx); let terminator = new_body.blocks()[bb_idx].terminator.clone(); - if let TerminatorKind::Call { - func: terminator_func, - args: _, - destination: terminator_destination, - target: terminator_target, - unwind: _, - } = &terminator.kind - { - // Get the function signature of the terminator call. - let fn_kind = match terminator_func.ty(new_body.locals()) { - Ok(fn_ty) => fn_ty.kind(), - _ => continue, - }; - let RigidTy::FnDef(fn_def, ..) = fn_kind.rigid().unwrap() else { - continue; - }; - - // The basic blocks end with register functions are loop head blocks. - if KaniAttributes::for_def_id(tcx, fn_def.def_id()).fn_marker() - == Some(Symbol::intern("kani_register_loop_contract")) - { - contain_loop_contracts = true; - - // Replace the original loop head block - // ```ignore - // bb_idx: { - // loop_head_stmts - // _v = kani_register_loop_contract(move args) -> [return: terminator_target]; - // } - // ``` - // with - // ```ignore - // bb_idx: { - // _v = true; - // goto -> terminator_target - // } - // ``` - let new_loop_head_block_stmts: Vec = vec![Statement { - kind: StatementKind::Assign( - terminator_destination.clone(), - Rvalue::Use(Operand::Constant(ConstOperand { - span: terminator.span, - user_ty: None, - const_: MirConst::from_bool(true), - })), - ), - span: terminator.span, - }]; - - new_body.replace_statements( - &SourceInstruction::Terminator { bb: bb_idx }, - new_loop_head_block_stmts, - ); - - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: bb_idx }, - Terminator { - kind: TerminatorKind::Goto { - target: terminator_target.unwrap(), - }, - span: terminator.span, - }, - ); - } - } + let is_loop_head = self.transform_bb(tcx, &mut new_body, bb_idx); + contain_loop_contracts |= is_loop_head; // Add successors of the current basic blocks to // the visiting queue. @@ -252,7 +125,11 @@ impl LoopContractPass { if visited.contains(&to_visit) { continue; } - queue.push_back(to_visit); + if is_loop_head { + loop_queue.push_back(to_visit); + } else { + queue.push_back(to_visit); + } } } (contain_loop_contracts, new_body.into()) @@ -264,204 +141,183 @@ impl LoopContractPass { } } } +} - /// Transform bodies of loop contract register functions. - /// 1. Replace the body of the register function with `run_contract_fn`'s. - /// 2. Move statements `loop_head_stmts` of loop head with contracts to the body of register functions - /// and make them unreachable. We will later codegen them when when codegen the calls to register functions. - fn transform_register_body( - &mut self, - tcx: TyCtxt, - body: Body, - instance: Instance, - ) -> (bool, Body) { - match instance.ty().kind().rigid().unwrap() { - RigidTy::FnDef(_func, args) => { - if KaniAttributes::for_instance(tcx, instance).fn_marker() - == Some(Symbol::intern("kani_register_loop_contract")) - && self.registered_stmts.contains_key(&instance.def.def_id()) - { - // Replace the body of the register function with `run_contract_fn`'s. - let run = Instance::resolve(self.run_contract_fn.unwrap(), args).unwrap(); +impl LoopContractPass { + pub fn new(tcx: TyCtxt, unit: &CodegenUnit) -> LoopContractPass { + if let Some(_) = unit.harnesses.first() { + let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); + assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); + LoopContractPass { run_contract_fn, new_loop_latches: HashMap::new() } + } else { + // If reachability mode is PubFns or Tests, we just remove any contract logic. + // Note that in this path there is no proof harness. + LoopContractPass::default() + } + } - // Move the stmts `loop_head_stmts` of loop head with contracts to the corresponding register functions. - let mut new_body = MutableBody::from(run.body().unwrap()); - new_body.insert_bb( - BasicBlock { - statements: self.registered_stmts[&instance.def.def_id()].clone(), - terminator: Terminator { - kind: TerminatorKind::Goto { target: new_body.blocks().len() }, - span: new_body.blocks()[0].terminator.span, - }, - }, - &mut SourceInstruction::Terminator { bb: 0 }, - InsertPosition::Before, - ); - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: 0 }, - Terminator { - kind: TerminatorKind::Goto { target: new_body.blocks().len() - 2 }, - span: new_body.blocks()[0].terminator.span, + fn is_supported_argument_of_closure(&self, rv: &Rvalue, body: &MutableBody) -> bool { + let var_debug_info = &body.var_debug_info(); + matches!(rv, Rvalue::Ref(_, _, place) if + var_debug_info.iter().any(|info| + matches!(&info.value, VarDebugInfoContents::Place(debug_place) if *place == *debug_place) + )) + } + + fn transform_bb(&mut self, tcx: TyCtxt, new_body: &mut MutableBody, bb_idx: usize) -> bool { + let terminator = new_body.blocks()[bb_idx].terminator.clone(); + let mut contain_loop_contracts = false; + + // Redirect loop latches to the new latches. + if let TerminatorKind::Goto { target: terminator_target } = &terminator.kind { + if self.new_loop_latches.contains_key(terminator_target) { + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: bb_idx }, + Terminator { + kind: TerminatorKind::Goto { + target: self.new_loop_latches[terminator_target], }, - ); - (true, new_body.into()) - } else { - (false, body) - } - } - _ => { - /* static variables case */ - (false, body) + span: terminator.span, + }, + ); } } - } - /// Transform main function bodies with loop contracts. - fn transform_main_body(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { - match instance.ty().kind().rigid().unwrap() { - RigidTy::FnDef(_func, _args) => { - if KaniAttributes::for_instance(tcx, instance).fn_marker() - == Some(Symbol::intern("kani_register_loop_contract")) + // Transform loop heads with loop contracts. + if let TerminatorKind::Call { + func: terminator_func, + args: terminator_args, + destination: terminator_destination, + target: terminator_target, + unwind: terminator_unwind, + } = &terminator.kind + { + // Get the function signature of the terminator call. + let Some(RigidTy::FnDef(fn_def, ..)) = terminator_func + .ty(new_body.locals()) + .ok() + .map(|fn_ty| fn_ty.kind().rigid().unwrap().clone()) + else { + return false; + }; + + // The basic blocks end with register functions are loop head blocks. + if KaniAttributes::for_def_id(tcx, fn_def.def_id()).fn_marker() + == Some(Symbol::intern("kani_register_loop_contract")) + { + contain_loop_contracts = true; + + // Collect supported vars assigned in the block. + // And check if all arguments of the closure is supported. + let mut supported_vars: Vec = Vec::new(); + // All user variables are support + supported_vars.extend(new_body.var_debug_info().into_iter().filter_map(|info| { + match &info.value { + VarDebugInfoContents::Place(debug_place) => Some(debug_place.clone()), + _ => None, + } + })); + if let Operand::Copy(closure_place) | Operand::Move(closure_place) = + &terminator_args[0] { - // Register functions will be handled by `transform_register_body`. - (false, body) - } else { - let mut new_body = MutableBody::from(body); - let mut contain_loop_contracts: bool = false; - - // Visit basic blocks in control flow order (BFS). - let mut visited: HashSet = HashSet::new(); - let mut queue: VecDeque = VecDeque::new(); - queue.push_back(0); - - while let Some(bb_idx) = queue.pop_front() { - visited.insert(bb_idx); - - let terminator = new_body.blocks()[bb_idx].terminator.clone(); - - // Redirect loop latches to the new latches. - if let TerminatorKind::Goto { target: terminator_target } = &terminator.kind - { - if self.new_loop_latches.contains_key(terminator_target) { - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: bb_idx }, - Terminator { - kind: TerminatorKind::Goto { - target: self.new_loop_latches[terminator_target], - }, - span: terminator.span, - }, - ); + for stmt in &new_body.blocks()[bb_idx].statements { + if let StatementKind::Assign(place, rvalue) = &stmt.kind { + if place == closure_place { + if let Rvalue::Aggregate(_, closure_args) = rvalue { + if closure_args.iter().any(|arg| !matches!(arg, Operand::Copy(arg_place) | Operand::Move(arg_place) if + supported_vars.contains(arg_place))){ + unreachable!( + "The loop invariant contains unsupported variables. + Please report https://github.com/model-checking/kani/issues/new?template=bug_report.md" + ) + } + } else { + unreachable!( + "The argument of loop contracts register function must be a closure." + ) + } + } else { + if self.is_supported_argument_of_closure(rvalue, new_body) { + supported_vars.push(place.clone()) + }; } } - - if let TerminatorKind::Call { - func: terminator_func, - args: terminator_args, - destination: terminator_destination, - target: terminator_target, - unwind: terminator_unwind, - } = &terminator.kind - { - // Get the function signature of the terminator call. - let fn_kind = match terminator_func.ty(new_body.locals()) { - Ok(fn_ty) => fn_ty.kind(), - _ => continue, - }; - let RigidTy::FnDef(fn_def, ..) = fn_kind.rigid().unwrap() else { - continue; - }; - - // The basic blocks end with register functions are loop head blocks. - if KaniAttributes::for_def_id(tcx, fn_def.def_id()).fn_marker() - == Some(Symbol::intern("kani_register_loop_contract")) - { - contain_loop_contracts = true; - - // Replace the original loop head block - // ```ignore - // bb_idx: { - // loop_head_stmts - // _v = kani_register_loop_contract(move args) -> [return: terminator_target]; - // } - // ``` - // with - // ```ignore - // bb_idx: { - // _v = true; - // goto -> terminator_target - // } - // ``` - let new_loop_head_block_stmts: Vec = vec![Statement { - kind: StatementKind::Assign( - terminator_destination.clone(), - Rvalue::Use(Operand::Constant(ConstOperand { - span: terminator.span, - user_ty: None, - const_: MirConst::from_bool(true), - })), - ), - span: terminator.span, - }]; - - self.registered_stmts.insert( - fn_def.def_id(), - new_body.blocks()[bb_idx].statements.clone(), - ); - - new_body.replace_statements( - &SourceInstruction::Terminator { bb: bb_idx }, - new_loop_head_block_stmts, - ); - - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: bb_idx }, - Terminator { - kind: TerminatorKind::Goto { target: bb_idx }, - span: terminator.span, - }, - ); - - // Insert a new basic block as the loop latch block, and later redirect - // all latches to the new loop latch block. - // ----- - // bb_new_loop_latch: { - // _v = kani_register_loop_contract(move args) -> [return: bb_idx]; - // } - new_body.insert_terminator( - &mut SourceInstruction::Terminator { bb: bb_idx }, - InsertPosition::After, - Terminator { - kind: TerminatorKind::Call { - func: terminator_func.clone(), - args: terminator_args.clone(), - destination: terminator_destination.clone(), - target: *terminator_target, - unwind: *terminator_unwind, - }, - span: terminator.span, - }, - ); - self.new_loop_latches.insert(bb_idx, new_body.blocks().len() - 1); - } - } - - // Add successors of the current basic blocks to - // the visiting queue. - for to_visit in terminator.successors() { - if visited.contains(&to_visit) { - continue; - } - queue.push_back(to_visit); - } } - (contain_loop_contracts, new_body.into()) + } else { + unreachable!( + "The argument of loop contracts register function must be a closure." + ) } - } - _ => { - /* static variables case */ - (false, body) + // Replace the original loop head block + // ```ignore + // bb_idx: { + // loop_head_stmts + // _v = kani_register_loop_contract(move args) -> [return: terminator_target]; + // } + // ``` + // with + // ```ignore + // bb_idx: { + // _v = true; + // goto -> terminator_target + // } + // ``` + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: bb_idx }, + Terminator { + kind: TerminatorKind::Goto { target: bb_idx }, + span: terminator.span, + }, + ); + new_body.assign_to( + terminator_destination.clone(), + Rvalue::Use(Operand::Constant(ConstOperand { + span: terminator.span, + user_ty: None, + const_: MirConst::from_bool(true), + })), + &mut SourceInstruction::Terminator { bb: bb_idx }, + InsertPosition::Before, + ); + + // Insert a new basic block as the loop latch block, and later redirect + // all latches to the new loop latch block. + // ----- + // bb_new_loop_latch: { + // _v = kani_register_loop_contract(move args) -> [return: terminator_target]; + // } + new_body.insert_terminator( + &mut SourceInstruction::Terminator { bb: bb_idx }, + InsertPosition::After, + Terminator { + kind: TerminatorKind::Call { + func: terminator_func.clone(), + args: terminator_args.clone(), + destination: terminator_destination.clone(), + target: *terminator_target, + unwind: *terminator_unwind, + }, + span: terminator.span, + }, + ); + new_body.replace_terminator( + &mut SourceInstruction::Terminator { bb: new_body.blocks().len() - 1 }, + Terminator { + kind: TerminatorKind::Call { + func: terminator_func.clone(), + args: terminator_args.clone(), + destination: terminator_destination.clone(), + target: *terminator_target, + unwind: *terminator_unwind, + }, + span: terminator.span, + }, + ); + + // Cache the new loop latch. + self.new_loop_latches.insert(bb_idx, new_body.blocks().len() - 1); } } + contain_loop_contracts } } diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 14d718d142b5..3f324d72c39e 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -91,6 +91,7 @@ impl BodyTransformation { mem_init_fn_cache: HashMap::new(), arguments: queries.args().clone(), }); + transformer.add_pass(queries, LoopContractPass::new(tcx, &unit)); transformer } @@ -191,7 +192,6 @@ pub struct GlobalPasses { impl GlobalPasses { pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { let mut global_passes = GlobalPasses { global_passes: vec![] }; - global_passes.add_global_pass(queries, LoopContractPass::new(tcx, queries)); global_passes.add_global_pass(queries, DelayedUbPass::new(CheckType::new_assert(tcx))); global_passes.add_global_pass(queries, DumpMirPass::new(tcx)); global_passes From aa3152811ac1628e8155b208941f551664e79066 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Fri, 11 Oct 2024 10:35:23 -0500 Subject: [PATCH 48/52] Code simplification --- .../kani_middle/transform/loop_contracts.rs | 62 ++++++++----------- tests/expected/loop-contract/box.expected | 1 + tests/expected/loop-contract/box.rs | 24 +++++++ .../simple_while_loop/simple_while_loop.rs | 2 +- .../small_slice_eq/small_slice_eq.rs | 2 +- 5 files changed, 53 insertions(+), 38 deletions(-) create mode 100644 tests/expected/loop-contract/box.expected create mode 100644 tests/expected/loop-contract/box.rs diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 2a6b57afdde7..f4df55450870 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -15,8 +15,8 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; use stable_mir::mir::mono::Instance; use stable_mir::mir::{ - BasicBlockIdx, Body, ConstOperand, Operand, Place, Rvalue, StatementKind, Terminator, - TerminatorKind, VarDebugInfoContents, + AggregateKind, BasicBlockIdx, Body, ConstOperand, Operand, Place, Rvalue, StatementKind, + Terminator, TerminatorKind, VarDebugInfoContents, }; use stable_mir::ty::{FnDef, MirConst, RigidTy}; use std::collections::{HashMap, HashSet, VecDeque}; @@ -122,13 +122,10 @@ impl TransformPass for LoopContractPass { // Add successors of the current basic blocks to // the visiting queue. for to_visit in terminator.successors() { - if visited.contains(&to_visit) { - continue; - } - if is_loop_head { - loop_queue.push_back(to_visit); - } else { - queue.push_back(to_visit); + if !visited.contains(&to_visit) { + let target_queue = + if is_loop_head { &mut loop_queue } else { &mut queue }; + target_queue.push_back(to_visit); } } } @@ -145,7 +142,7 @@ impl TransformPass for LoopContractPass { impl LoopContractPass { pub fn new(tcx: TyCtxt, unit: &CodegenUnit) -> LoopContractPass { - if let Some(_) = unit.harnesses.first() { + if unit.harnesses.first().is_some() { let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); LoopContractPass { run_contract_fn, new_loop_latches: HashMap::new() } @@ -211,43 +208,36 @@ impl LoopContractPass { // And check if all arguments of the closure is supported. let mut supported_vars: Vec = Vec::new(); // All user variables are support - supported_vars.extend(new_body.var_debug_info().into_iter().filter_map(|info| { + supported_vars.extend(new_body.var_debug_info().iter().filter_map(|info| { match &info.value { VarDebugInfoContents::Place(debug_place) => Some(debug_place.clone()), _ => None, } })); - if let Operand::Copy(closure_place) | Operand::Move(closure_place) = - &terminator_args[0] - { - for stmt in &new_body.blocks()[bb_idx].statements { - if let StatementKind::Assign(place, rvalue) = &stmt.kind { - if place == closure_place { - if let Rvalue::Aggregate(_, closure_args) = rvalue { - if closure_args.iter().any(|arg| !matches!(arg, Operand::Copy(arg_place) | Operand::Move(arg_place) if - supported_vars.contains(arg_place))){ - unreachable!( - "The loop invariant contains unsupported variables. - Please report https://github.com/model-checking/kani/issues/new?template=bug_report.md" - ) - } - } else { + + // For each assignment in the loop head block, + // if it assigns to the closure place, we check if all arguments are supported; + // if it assigns to other places, we cache if the assigned places are supported. + for stmt in &new_body.blocks()[bb_idx].statements { + if let StatementKind::Assign(place, rvalue) = &stmt.kind { + match rvalue { + Rvalue::Aggregate(AggregateKind::Closure(..), closure_args) => { + if closure_args.iter().any(|arg| !matches!(arg, Operand::Copy(arg_place) | Operand::Move(arg_place) if supported_vars.contains(arg_place))) { unreachable!( - "The argument of loop contracts register function must be a closure." - ) + "The loop invariant contains unsupported variables. \ + Please report github.com/model-checking/kani/issues/new?template=bug_report.md" + ); } - } else { + } + _ => { if self.is_supported_argument_of_closure(rvalue, new_body) { - supported_vars.push(place.clone()) - }; + supported_vars.push(place.clone()); + } } } } - } else { - unreachable!( - "The argument of loop contracts register function must be a closure." - ) } + // Replace the original loop head block // ```ignore // bb_idx: { @@ -301,7 +291,7 @@ impl LoopContractPass { }, ); new_body.replace_terminator( - &mut SourceInstruction::Terminator { bb: new_body.blocks().len() - 1 }, + &SourceInstruction::Terminator { bb: new_body.blocks().len() - 1 }, Terminator { kind: TerminatorKind::Call { func: terminator_func.clone(), diff --git a/tests/expected/loop-contract/box.expected b/tests/expected/loop-contract/box.expected new file mode 100644 index 000000000000..ebb34e360be6 --- /dev/null +++ b/tests/expected/loop-contract/box.expected @@ -0,0 +1 @@ +internal error: entered unreachable code: The loop invariant contains unsupported variables.\ \ No newline at end of file diff --git a/tests/expected/loop-contract/box.rs b/tests/expected/loop-contract/box.rs new file mode 100644 index 000000000000..ca48821b2082 --- /dev/null +++ b/tests/expected/loop-contract/box.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! Check if loop contracts is correctly applied. + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +type Data = u8; + +#[kani::proof] +fn box_harness() { + let mut i: u8 = 0; + + let mut data: Box = Box::new(0); + + #[kani::loop_invariant(*data == i)] + while i < 10 { + i += 1; + data = Box::new(i); + } +} diff --git a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs index c959262373ac..e3b75ab4a8b7 100644 --- a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs +++ b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs @@ -9,7 +9,7 @@ #![feature(proc_macro_hygiene)] #[kani::proof] -fn main() { +fn simple_while_loop_harness() { let mut x: u8 = kani::any_where(|i| *i >= 2); #[kani::loop_invariant(x >= 2)] diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs index cb3c6f51240d..6f18e8c54da8 100644 --- a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs +++ b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs @@ -38,7 +38,7 @@ unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool { } #[kani::proof] -fn main() { +fn small_slice_eq_harness() { let mut a = [1; 2000]; let mut b = [1; 2000]; unsafe { From faf50e98962f0fb4f99334beda67034f660ad850 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Fri, 11 Oct 2024 16:11:27 -0500 Subject: [PATCH 49/52] Add more tests --- .../kani_middle/transform/loop_contracts.rs | 63 ++++++++++++------- library/kani_core/src/lib.rs | 12 ++++ .../src/sysroot/loop_contracts/mod.rs | 6 +- .../loop-contract/count_zero.expected | 1 + tests/expected/loop-contract/count_zero.rs | 31 +++++++++ ...count_zero_loop_contracts_disable.expected | 1 + .../count_zero_loop_contracts_disable.rs | 30 +++++++++ 7 files changed, 118 insertions(+), 26 deletions(-) create mode 100644 tests/expected/loop-contract/count_zero.expected create mode 100644 tests/expected/loop-contract/count_zero.rs create mode 100644 tests/expected/loop-contract/count_zero_loop_contracts_disable.expected create mode 100644 tests/expected/loop-contract/count_zero_loop_contracts_disable.rs diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index f4df55450870..dfaba80e3dd1 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -15,10 +15,10 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; use stable_mir::mir::mono::Instance; use stable_mir::mir::{ - AggregateKind, BasicBlockIdx, Body, ConstOperand, Operand, Place, Rvalue, StatementKind, - Terminator, TerminatorKind, VarDebugInfoContents, + AggregateKind, BasicBlock, BasicBlockIdx, Body, ConstOperand, Operand, Place, Rvalue, + Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, }; -use stable_mir::ty::{FnDef, MirConst, RigidTy}; +use stable_mir::ty::{FnDef, MirConst, RigidTy, UintTy}; use std::collections::{HashMap, HashSet, VecDeque}; use std::fmt::Debug; @@ -47,6 +47,7 @@ use super::TransformPass; /// to blocks /// ```ignore /// bb_idx: { +/// loop_head_stmts /// _v = true /// goto -> terminator_target /// } @@ -142,8 +143,8 @@ impl TransformPass for LoopContractPass { impl LoopContractPass { pub fn new(tcx: TyCtxt, unit: &CodegenUnit) -> LoopContractPass { - if unit.harnesses.first().is_some() { - let run_contract_fn = find_fn_def(tcx, "KaniRunContract"); + if !unit.harnesses.is_empty() { + let run_contract_fn = find_fn_def(tcx, "KaniRunLoopContract"); assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); LoopContractPass { run_contract_fn, new_loop_latches: HashMap::new() } } else { @@ -153,6 +154,20 @@ impl LoopContractPass { } } + /// Generate the body of loop head block by dropping all statements + /// except for `StorageLive` and `StorageDead`. + fn get_loop_head_block(&self, block: &BasicBlock) -> BasicBlock { + let new_stmts: Vec = block + .statements + .iter() + .filter(|stmt| { + matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_)) + }) + .map(|stmt| stmt.clone()) + .collect(); + return BasicBlock { statements: new_stmts, terminator: block.terminator.clone() }; + } + fn is_supported_argument_of_closure(&self, rv: &Rvalue, body: &MutableBody) -> bool { let var_debug_info = &body.var_debug_info(); matches!(rv, Rvalue::Ref(_, _, place) if @@ -201,6 +216,7 @@ impl LoopContractPass { // The basic blocks end with register functions are loop head blocks. if KaniAttributes::for_def_id(tcx, fn_def.def_id()).fn_marker() == Some(Symbol::intern("kani_register_loop_contract")) + && matches!(&terminator_args[1], Operand::Constant(op) if op.const_.eval_target_usize().unwrap() == 0) { contain_loop_contracts = true; @@ -248,17 +264,11 @@ impl LoopContractPass { // with // ```ignore // bb_idx: { + // loop_head_stmts // _v = true; // goto -> terminator_target // } // ``` - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: bb_idx }, - Terminator { - kind: TerminatorKind::Goto { target: bb_idx }, - span: terminator.span, - }, - ); new_body.assign_to( terminator_destination.clone(), Rvalue::Use(Operand::Constant(ConstOperand { @@ -269,6 +279,7 @@ impl LoopContractPass { &mut SourceInstruction::Terminator { bb: bb_idx }, InsertPosition::Before, ); + let new_latch_block = self.get_loop_head_block(&new_body.blocks()[bb_idx]); // Insert a new basic block as the loop latch block, and later redirect // all latches to the new loop latch block. @@ -276,13 +287,26 @@ impl LoopContractPass { // bb_new_loop_latch: { // _v = kani_register_loop_contract(move args) -> [return: terminator_target]; // } - new_body.insert_terminator( + new_body.insert_bb( + new_latch_block, &mut SourceInstruction::Terminator { bb: bb_idx }, InsertPosition::After, + ); + // Update the argument `transformed` to 1 to avoid double transformation. + let new_args = vec![ + terminator_args[0].clone(), + Operand::Constant(ConstOperand { + span: terminator.span, + user_ty: None, + const_: MirConst::try_from_uint(1, UintTy::Usize).unwrap(), + }), + ]; + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: new_body.blocks().len() - 1 }, Terminator { kind: TerminatorKind::Call { func: terminator_func.clone(), - args: terminator_args.clone(), + args: new_args, destination: terminator_destination.clone(), target: *terminator_target, unwind: *terminator_unwind, @@ -291,19 +315,12 @@ impl LoopContractPass { }, ); new_body.replace_terminator( - &SourceInstruction::Terminator { bb: new_body.blocks().len() - 1 }, + &SourceInstruction::Terminator { bb: bb_idx }, Terminator { - kind: TerminatorKind::Call { - func: terminator_func.clone(), - args: terminator_args.clone(), - destination: terminator_destination.clone(), - target: *terminator_target, - unwind: *terminator_unwind, - }, + kind: TerminatorKind::Goto { target: terminator_target.unwrap() }, span: terminator.span, }, ); - // Cache the new loop latch. self.new_loop_latches.insert(bb_idx, new_body.blocks().len() - 1); } diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 839313f084c2..ee0092554e56 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -435,6 +435,18 @@ macro_rules! kani_intrinsics { func() } + /// Function that calls a closure used to implement loop contracts. + /// + /// In contracts, we cannot invoke the generated closures directly, instead, we call register + /// contract. This function is a no-op. However, in the reality, we do want to call the closure, + /// so we swap the register body by this function body. + #[doc(hidden)] + #[allow(dead_code)] + #[rustc_diagnostic_item = "KaniRunLoopContract"] + fn run_loop_contract_fn bool>(func: &F, _transformed: usize) -> bool { + func() + } + /// This is used by contracts to select which version of the contract to use during codegen. #[doc(hidden)] pub type Mode = u8; diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 786ee58daa80..01166af3d988 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -53,7 +53,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { Expr::While(ref mut ew) => { let new_cond: Expr = syn::parse( quote!( - #register_ident(||->bool{#inv_expr})) + #register_ident(&||->bool{#inv_expr}, 0)) .into(), ) .unwrap(); @@ -81,8 +81,8 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { // This function gets replaced by `kani::internal::call_closure`. #[inline(never)] #[kanitool::fn_marker = "kani_register_loop_contract"] - const fn #register_ident T>(f: F) -> T { - unreachable!() + const fn #register_ident bool>(_f: &F, _transformed: usize) -> bool { + true } #loop_stmt}) .into() diff --git a/tests/expected/loop-contract/count_zero.expected b/tests/expected/loop-contract/count_zero.expected new file mode 100644 index 000000000000..880f00714b32 --- /dev/null +++ b/tests/expected/loop-contract/count_zero.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL \ No newline at end of file diff --git a/tests/expected/loop-contract/count_zero.rs b/tests/expected/loop-contract/count_zero.rs new file mode 100644 index 000000000000..616edef7ba2b --- /dev/null +++ b/tests/expected/loop-contract/count_zero.rs @@ -0,0 +1,31 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! Check if loop contracts is correctly applied. + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +pub const BASE: usize = count_zero(&[]); + +#[kani::proof] +pub fn check_counter() { + assert_eq!(count_zero(&[1, 2, 3]), 0) +} + +const fn count_zero(slice: &[u8]) -> usize { + let mut counter: usize = 0; + let mut index: usize = 0; + + #[kani::loop_invariant(index <= slice.len() && counter <= slice.len() && counter == 0 )] + while index < slice.len() { + if slice[index] == 0 { + counter += 1; + } + index += 1; + } + + counter +} diff --git a/tests/expected/loop-contract/count_zero_loop_contracts_disable.expected b/tests/expected/loop-contract/count_zero_loop_contracts_disable.expected new file mode 100644 index 000000000000..880f00714b32 --- /dev/null +++ b/tests/expected/loop-contract/count_zero_loop_contracts_disable.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL \ No newline at end of file diff --git a/tests/expected/loop-contract/count_zero_loop_contracts_disable.rs b/tests/expected/loop-contract/count_zero_loop_contracts_disable.rs new file mode 100644 index 000000000000..b64fbadeb4a4 --- /dev/null +++ b/tests/expected/loop-contract/count_zero_loop_contracts_disable.rs @@ -0,0 +1,30 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check if harness work loop contracts macro can be proved correctly +//! without loop contracts enable. + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +pub const BASE: usize = count_zero(&[]); + +#[kani::proof] +pub fn check_counter() { + assert_eq!(count_zero(&[1, 2, 3]), 0) +} + +const fn count_zero(slice: &[u8]) -> usize { + let mut counter: usize = 0; + let mut index: usize = 0; + + #[kani::loop_invariant(index <= slice.len() && counter <= slice.len() && counter == 0 )] + while index < slice.len() { + if slice[index] == 0 { + counter += 1; + } + index += 1; + } + + counter +} From 3c1a64a114f7216bce1684fa4cfa68be95c7ba98 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Mon, 14 Oct 2024 01:28:42 -0500 Subject: [PATCH 50/52] Use DFCC --- .../src/kani_middle/transform/body.rs | 9 + .../kani_middle/transform/loop_contracts.rs | 159 +++++++++++++----- kani-driver/src/call_goto_instrument.rs | 7 +- tests/expected/loop-contract/box.expected | 2 +- .../loop-contract/count_zero.expected | 2 +- tests/expected/loop-contract/count_zero.rs | 14 +- ...count_zero_loop_contracts_disable.expected | 2 +- .../loop-contract/{box.rs => fixme_box.rs} | 3 +- .../loop-contract/memchar_naive.expected | 1 + tests/expected/loop-contract/memchar_naive.rs | 36 ++++ .../loop-contract/multiple_loops.expected | 1 + .../expected/loop-contract/multiple_loops.rs | 53 ++++++ .../simple_while_loop/simple_while_loop.rs | 5 + .../small_slice_eq/small_slice_eq.rs | 6 +- 14 files changed, 244 insertions(+), 56 deletions(-) rename tests/expected/loop-contract/{box.rs => fixme_box.rs} (73%) create mode 100644 tests/expected/loop-contract/memchar_naive.expected create mode 100644 tests/expected/loop-contract/memchar_naive.rs create mode 100644 tests/expected/loop-contract/multiple_loops.expected create mode 100644 tests/expected/loop-contract/multiple_loops.rs diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index bf87d59c1036..6cb104c7470a 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -433,6 +433,15 @@ impl MutableBody { self.blocks.push(BasicBlock { statements: Vec::default(), terminator }) } + /// Replace statements from the given basic block + pub fn replace_statements( + &mut self, + source_instruction: &SourceInstruction, + new_stmts: Vec, + ) { + self.blocks.get_mut(source_instruction.bb()).unwrap().statements = new_stmts; + } + /// Replace a terminator from the given basic block pub fn replace_terminator( &mut self, diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index dfaba80e3dd1..4a69f63aac4f 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -24,48 +24,6 @@ use std::fmt::Debug; use super::TransformPass; -/// This pass will perform the following operations: -/// 1. Replace the body of `kani_register_loop_contract` by `kani::internal::run_contract_fn` -/// to invoke the closure. -/// -/// 2. Transform loops with contracts from -/// ```ignore -/// bb_idx: { -/// loop_head_stmts -/// _v = kani_register_loop_contract(move args) -> [return: terminator_target]; -/// } -/// -/// ... -/// loop_body_blocks -/// ... -/// -/// loop_latch_block: { -/// loop_latch_stmts -/// goto -> bb_idx; -/// } -/// ``` -/// to blocks -/// ```ignore -/// bb_idx: { -/// loop_head_stmts -/// _v = true -/// goto -> terminator_target -/// } -/// -/// ... -/// loop_body_blocks -/// ... -/// -/// loop_latch_block: { -/// loop_latch_stmts -/// goto -> bb_new_loop_latch; -/// } -/// -/// bb_new_loop_latch: { -/// loop_head_body -/// _v = kani_register_loop_contract(move args) -> [return: terminator_target]; -/// } -/// ``` #[derive(Debug, Default)] pub struct LoopContractPass { /// Cache KaniRunContract function used to implement contracts. @@ -92,7 +50,51 @@ impl TransformPass for LoopContractPass { } /// Run a transformation pass on the whole codegen unit. + /// + /// This pass will perform the following operations: + /// 1. Replace the body of `kani_register_loop_contract` by `kani::internal::run_contract_fn` + /// to invoke the closure. + /// + /// 2. Transform loops with contracts from + /// ```ignore + /// bb_idx: { + /// loop_head_stmts + /// _v = kani_register_loop_contract(move args) -> [return: terminator_target]; + /// } + /// + /// ... + /// loop_body_blocks + /// ... + /// + /// loop_latch_block: { + /// loop_latch_stmts + /// goto -> bb_idx; + /// } + /// ``` + /// to blocks + /// ```ignore + /// bb_idx: { + /// loop_head_stmts + /// _v = true + /// goto -> terminator_target + /// } + /// + /// ... + /// loop_body_blocks + /// ... + /// + /// loop_latch_block: { + /// loop_latch_stmts + /// goto -> bb_new_loop_latch; + /// } + /// + /// bb_new_loop_latch: { + /// loop_head_body + /// _v = kani_register_loop_contract(move args) -> [return: terminator_target]; + /// } + /// ``` fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + self.new_loop_latches = HashMap::new(); match instance.ty().kind().rigid().unwrap() { RigidTy::FnDef(_func, args) => { if KaniAttributes::for_instance(tcx, instance).fn_marker() @@ -163,11 +165,25 @@ impl LoopContractPass { .filter(|stmt| { matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_)) }) - .map(|stmt| stmt.clone()) + .cloned() .collect(); - return BasicBlock { statements: new_stmts, terminator: block.terminator.clone() }; + BasicBlock { statements: new_stmts, terminator: block.terminator.clone() } } + /// Remove `StorageDead closure_var` to avoid invariant closure becoming dead. + fn make_invariant_closure_alive(&self, body: &mut MutableBody, bb_idx: usize) { + let mut stmts = body.blocks()[bb_idx].statements.clone(); + if stmts.len() == 0 || !matches!(stmts[0].kind, StatementKind::StorageDead(_)) { + unreachable!( + "The assumptions for loop-contracts transformation are violated. \ + Please report github.com/model-checking/kani/issues/new?template=bug_report.md" + ); + } + stmts.remove(0); + body.replace_statements(&SourceInstruction::Terminator { bb: bb_idx }, stmts); + } + + /// We only support closure arguments that are either `copy`` or `move`` of reference of user variables. fn is_supported_argument_of_closure(&self, rv: &Rvalue, body: &MutableBody) -> bool { let var_debug_info = &body.var_debug_info(); matches!(rv, Rvalue::Ref(_, _, place) if @@ -176,6 +192,44 @@ impl LoopContractPass { )) } + /// Transform loops with contracts from + /// ```ignore + /// bb_idx: { + /// loop_head_stmts + /// _v = kani_register_loop_contract(move args) -> [return: terminator_target]; + /// } + /// + /// ... + /// loop_body_blocks + /// ... + /// + /// loop_latch_block: { + /// loop_latch_stmts + /// goto -> bb_idx; + /// } + /// ``` + /// to blocks + /// ```ignore + /// bb_idx: { + /// loop_head_stmts + /// _v = true + /// goto -> terminator_target + /// } + /// + /// ... + /// loop_body_blocks + /// ... + /// + /// loop_latch_block: { + /// loop_latch_stmts + /// goto -> bb_new_loop_latch; + /// } + /// + /// bb_new_loop_latch: { + /// loop_head_body + /// _v = kani_register_loop_contract(move args) -> [return: terminator_target]; + /// } + /// ``` fn transform_bb(&mut self, tcx: TyCtxt, new_body: &mut MutableBody, bb_idx: usize) -> bool { let terminator = new_body.blocks()[bb_idx].terminator.clone(); let mut contain_loop_contracts = false; @@ -218,6 +272,23 @@ impl LoopContractPass { == Some(Symbol::intern("kani_register_loop_contract")) && matches!(&terminator_args[1], Operand::Constant(op) if op.const_.eval_target_usize().unwrap() == 0) { + // Check if the MIR satisfy the assumptions of this transformation. + if new_body.blocks()[terminator_target.unwrap()].statements.len() != 0 + || !matches!( + new_body.blocks()[terminator_target.unwrap()].terminator.kind, + TerminatorKind::SwitchInt { .. } + ) + { + unreachable!( + "The assumptions for loop-contracts transformation are violated. \ + Please report github.com/model-checking/kani/issues/new?template=bug_report.md" + ); + } + + let ori_condition_bb_idx = + new_body.blocks()[terminator_target.unwrap()].terminator.successors()[1]; + self.make_invariant_closure_alive(new_body, ori_condition_bb_idx); + contain_loop_contracts = true; // Collect supported vars assigned in the block. diff --git a/kani-driver/src/call_goto_instrument.rs b/kani-driver/src/call_goto_instrument.rs index f98f48128c75..a77e3c675aec 100644 --- a/kani-driver/src/call_goto_instrument.rs +++ b/kani-driver/src/call_goto_instrument.rs @@ -45,7 +45,7 @@ impl KaniSession { .unstable_features .contains(kani_metadata::UnstableFeature::LoopContracts) { - self.instrument_loop_contracts(output)?; + self.instrument_loop_contracts(harness, output)?; } if self.args.checks.undefined_function_on() { @@ -193,10 +193,13 @@ impl KaniSession { } /// Apply annotated loop contracts. - pub fn instrument_loop_contracts(&self, file: &Path) -> Result<()> { + pub fn instrument_loop_contracts(&self, harness: &HarnessMetadata, file: &Path) -> Result<()> { let args: Vec = vec![ + "--dfcc".into(), + (&harness.mangled_name).into(), "--apply-loop-contracts".into(), "--loop-contracts-no-unwind".into(), + "--no-malloc-may-fail".into(), // Because loop contracts now are wrapped in a closure which will be a side-effect expression in CBMC even they // may not contain side-effect. So we disable the side-effect check for now and will implement a better check // instead of simply rejecting function calls and statement expressions. diff --git a/tests/expected/loop-contract/box.expected b/tests/expected/loop-contract/box.expected index ebb34e360be6..3f49c30d1dd7 100644 --- a/tests/expected/loop-contract/box.expected +++ b/tests/expected/loop-contract/box.expected @@ -1 +1 @@ -internal error: entered unreachable code: The loop invariant contains unsupported variables.\ \ No newline at end of file +internal error: entered unreachable code: The loop invariant contains unsupported variables.\ diff --git a/tests/expected/loop-contract/count_zero.expected b/tests/expected/loop-contract/count_zero.expected index 880f00714b32..34c886c358cb 100644 --- a/tests/expected/loop-contract/count_zero.expected +++ b/tests/expected/loop-contract/count_zero.expected @@ -1 +1 @@ -VERIFICATION:- SUCCESSFUL \ No newline at end of file +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/count_zero.rs b/tests/expected/loop-contract/count_zero.rs index 616edef7ba2b..1b6cee925c97 100644 --- a/tests/expected/loop-contract/count_zero.rs +++ b/tests/expected/loop-contract/count_zero.rs @@ -10,11 +10,6 @@ pub const BASE: usize = count_zero(&[]); -#[kani::proof] -pub fn check_counter() { - assert_eq!(count_zero(&[1, 2, 3]), 0) -} - const fn count_zero(slice: &[u8]) -> usize { let mut counter: usize = 0; let mut index: usize = 0; @@ -29,3 +24,12 @@ const fn count_zero(slice: &[u8]) -> usize { counter } + +#[kani::proof] +pub fn check_counter() { + // Needed to avoid having `free` be removed as unused function. This is + // because DFCC contract enforcement assumes that a definition for `free` + // exists. + let _ = Box::new(10); + assert_eq!(count_zero(&[1, 2, 3]), 0) +} diff --git a/tests/expected/loop-contract/count_zero_loop_contracts_disable.expected b/tests/expected/loop-contract/count_zero_loop_contracts_disable.expected index 880f00714b32..34c886c358cb 100644 --- a/tests/expected/loop-contract/count_zero_loop_contracts_disable.expected +++ b/tests/expected/loop-contract/count_zero_loop_contracts_disable.expected @@ -1 +1 @@ -VERIFICATION:- SUCCESSFUL \ No newline at end of file +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/box.rs b/tests/expected/loop-contract/fixme_box.rs similarity index 73% rename from tests/expected/loop-contract/box.rs rename to tests/expected/loop-contract/fixme_box.rs index ca48821b2082..35dc0210d971 100644 --- a/tests/expected/loop-contract/box.rs +++ b/tests/expected/loop-contract/fixme_box.rs @@ -3,7 +3,8 @@ // kani-flags: -Z loop-contracts -//! Check if loop contracts is correctly applied. +//! Loop contracts in CBMC backend doesn't support malloc or free in loop bodies. +//! Tracked in: https://github.com/model-checking/kani/issues/3587 #![feature(stmt_expr_attributes)] #![feature(proc_macro_hygiene)] diff --git a/tests/expected/loop-contract/memchar_naive.expected b/tests/expected/loop-contract/memchar_naive.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/loop-contract/memchar_naive.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/memchar_naive.rs b/tests/expected/loop-contract/memchar_naive.rs new file mode 100644 index 000000000000..140d9f6342ed --- /dev/null +++ b/tests/expected/loop-contract/memchar_naive.rs @@ -0,0 +1,36 @@ +// Copyright rustc Contributors +// Adapted from rust std: https://github.com/rust-lang/rust/blob/master/library/core/src/slice/memchr.rs#L38 +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Modifications Copyright Kani Contributors +// See GitHub history for details. + +// kani-flags: -Z loop-contracts + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +#[kani::proof] +fn memchar_naive_harness() { + // Needed to avoid having `free` be removed as unused function. This is + // because DFCC contract enforcement assumes that a definition for `free` + // exists. + let _ = Box::new(10); + let text = [1, 2, 3, 4, 5]; + let x = 5; + let mut i = 0; + let mut r: Option = None; + + #[kani::loop_invariant(i <= text.len() && ((i == 0) || text[i-1]!= x))] + while i < text.len() { + if text[i] == x { + r = Some(i); + break; + } + + i += 1; + } + + assert_eq!(r, Some(4)); +} diff --git a/tests/expected/loop-contract/multiple_loops.expected b/tests/expected/loop-contract/multiple_loops.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/loop-contract/multiple_loops.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/multiple_loops.rs b/tests/expected/loop-contract/multiple_loops.rs new file mode 100644 index 000000000000..5fa2d6b9889a --- /dev/null +++ b/tests/expected/loop-contract/multiple_loops.rs @@ -0,0 +1,53 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts --enable-unstable --cbmc-args --object-bits 8 + +//! Check if loop contracts is correctly applied. + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +fn multiple_loops() { + let mut x: u8 = kani::any_where(|i| *i >= 10); + + #[kani::loop_invariant(x >= 5)] + while x > 5 { + x = x - 1; + } + + assert!(x == 5); + + #[kani::loop_invariant(x >= 2)] + while x > 2 { + x = x - 1; + } + + assert!(x == 2); +} + +fn simple_while_loops() { + let mut x: u8 = kani::any_where(|i| *i >= 10); + let mut y: u8 = kani::any_where(|i| *i >= 10); + + #[kani::loop_invariant(x >= 2)] + while x > 2 { + x = x - 1; + #[kani::loop_invariant(y >= 2)] + while y > 2 { + y = y - 1; + } + } + + assert!(x == 2); +} + +#[kani::proof] +fn multiple_loops_harness() { + // Needed to avoid having `free` be removed as unused function. This is + // because DFCC contract enforcement assumes that a definition for `free` + // exists. + let _ = Box::new(10); + multiple_loops(); + simple_while_loops(); +} diff --git a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs index e3b75ab4a8b7..6880d86d673a 100644 --- a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs +++ b/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs @@ -9,7 +9,12 @@ #![feature(proc_macro_hygiene)] #[kani::proof] +#[kani::solver(kissat)] fn simple_while_loop_harness() { + // Needed to avoid having `free` be removed as unused function. This is + // because DFCC contract enforcement assumes that a definition for `free` + // exists. + let _ = Box::new(10); let mut x: u8 = kani::any_where(|i| *i >= 2); #[kani::loop_invariant(x >= 2)] diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs index 6f18e8c54da8..a59436122da5 100644 --- a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs +++ b/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs @@ -6,7 +6,7 @@ // Modifications Copyright Kani Contributors // See GitHub history for details. -// kani-flags: -Z loop-contracts --enable-unstable --cbmc-args --arrays-uf-always --no-standard-checks +// kani-flags: -Z loop-contracts --enable-unstable --cbmc-args --arrays-uf-always --no-standard-checks --object-bits 8 //! Check if loop contracts are correctly applied. The flag --no-standard-checks should be //! removed once same_object predicate is supported in loop contracts. @@ -39,6 +39,10 @@ unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool { #[kani::proof] fn small_slice_eq_harness() { + // Needed to avoid having `free` be removed as unused function. This is + // because DFCC contract enforcement assumes that a definition for `free` + // exists. + let _ = Box::new(10); let mut a = [1; 2000]; let mut b = [1; 2000]; unsafe { From f00822312786cb863802da8ad9eefa7464bf7002 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Mon, 14 Oct 2024 15:05:23 -0500 Subject: [PATCH 51/52] Fix format --- .../src/kani_middle/transform/loop_contracts.rs | 10 +++++----- tests/expected/loop-contract/box.expected | 1 - tests/expected/loop-contract/fixme_box.expected | 1 + .../loop-contract}/simple_while_loop.rs | 0 .../loop-contract}/small_slice_eq.rs | 0 tests/ui/loop_contracts/simple_while_loop/expected | 2 -- tests/ui/loop_contracts/small_slice_eq/expected | 2 -- 7 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 tests/expected/loop-contract/box.expected create mode 100644 tests/expected/loop-contract/fixme_box.expected rename tests/{ui/loop_contracts/simple_while_loop => expected/loop-contract}/simple_while_loop.rs (100%) rename tests/{ui/loop_contracts/small_slice_eq => expected/loop-contract}/small_slice_eq.rs (100%) delete mode 100644 tests/ui/loop_contracts/simple_while_loop/expected delete mode 100644 tests/ui/loop_contracts/small_slice_eq/expected diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 4a69f63aac4f..8038ef784970 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -173,9 +173,9 @@ impl LoopContractPass { /// Remove `StorageDead closure_var` to avoid invariant closure becoming dead. fn make_invariant_closure_alive(&self, body: &mut MutableBody, bb_idx: usize) { let mut stmts = body.blocks()[bb_idx].statements.clone(); - if stmts.len() == 0 || !matches!(stmts[0].kind, StatementKind::StorageDead(_)) { + if stmts.is_empty() || !matches!(stmts[0].kind, StatementKind::StorageDead(_)) { unreachable!( - "The assumptions for loop-contracts transformation are violated. \ + "The assumptions for loop-contracts transformation are violated by some other transformation. \ Please report github.com/model-checking/kani/issues/new?template=bug_report.md" ); } @@ -273,14 +273,14 @@ impl LoopContractPass { && matches!(&terminator_args[1], Operand::Constant(op) if op.const_.eval_target_usize().unwrap() == 0) { // Check if the MIR satisfy the assumptions of this transformation. - if new_body.blocks()[terminator_target.unwrap()].statements.len() != 0 + if !new_body.blocks()[terminator_target.unwrap()].statements.is_empty() || !matches!( new_body.blocks()[terminator_target.unwrap()].terminator.kind, TerminatorKind::SwitchInt { .. } ) { unreachable!( - "The assumptions for loop-contracts transformation are violated. \ + "The assumptions for loop-contracts transformation are violated by some other transformation. \ Please report github.com/model-checking/kani/issues/new?template=bug_report.md" ); } @@ -311,7 +311,7 @@ impl LoopContractPass { Rvalue::Aggregate(AggregateKind::Closure(..), closure_args) => { if closure_args.iter().any(|arg| !matches!(arg, Operand::Copy(arg_place) | Operand::Move(arg_place) if supported_vars.contains(arg_place))) { unreachable!( - "The loop invariant contains unsupported variables. \ + "The loop invariant support only reference of user variables. The provided invariants contain unsupported dereference. \ Please report github.com/model-checking/kani/issues/new?template=bug_report.md" ); } diff --git a/tests/expected/loop-contract/box.expected b/tests/expected/loop-contract/box.expected deleted file mode 100644 index 3f49c30d1dd7..000000000000 --- a/tests/expected/loop-contract/box.expected +++ /dev/null @@ -1 +0,0 @@ -internal error: entered unreachable code: The loop invariant contains unsupported variables.\ diff --git a/tests/expected/loop-contract/fixme_box.expected b/tests/expected/loop-contract/fixme_box.expected new file mode 100644 index 000000000000..59982b8b57a6 --- /dev/null +++ b/tests/expected/loop-contract/fixme_box.expected @@ -0,0 +1 @@ +internal error: entered unreachable code: The loop invariant support only reference of user variables. The provided invariants contain unsupported dereference.\ \ No newline at end of file diff --git a/tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs b/tests/expected/loop-contract/simple_while_loop.rs similarity index 100% rename from tests/ui/loop_contracts/simple_while_loop/simple_while_loop.rs rename to tests/expected/loop-contract/simple_while_loop.rs diff --git a/tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs b/tests/expected/loop-contract/small_slice_eq.rs similarity index 100% rename from tests/ui/loop_contracts/small_slice_eq/small_slice_eq.rs rename to tests/expected/loop-contract/small_slice_eq.rs diff --git a/tests/ui/loop_contracts/simple_while_loop/expected b/tests/ui/loop_contracts/simple_while_loop/expected deleted file mode 100644 index 000dba6ada5c..000000000000 --- a/tests/ui/loop_contracts/simple_while_loop/expected +++ /dev/null @@ -1,2 +0,0 @@ -- Description: "Check that loop invariant is preserved" -VERIFICATION:- SUCCESSFUL diff --git a/tests/ui/loop_contracts/small_slice_eq/expected b/tests/ui/loop_contracts/small_slice_eq/expected deleted file mode 100644 index 000dba6ada5c..000000000000 --- a/tests/ui/loop_contracts/small_slice_eq/expected +++ /dev/null @@ -1,2 +0,0 @@ -- Description: "Check that loop invariant is preserved" -VERIFICATION:- SUCCESSFUL From b1ef46d871d00f6baff29a4d64287316f5c1c577 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Mon, 14 Oct 2024 16:10:12 -0500 Subject: [PATCH 52/52] Add expected --- tests/expected/loop-contract/simple_while_loop.expected | 1 + tests/expected/loop-contract/small_slice_eq.expected | 1 + 2 files changed, 2 insertions(+) create mode 100644 tests/expected/loop-contract/simple_while_loop.expected create mode 100644 tests/expected/loop-contract/small_slice_eq.expected diff --git a/tests/expected/loop-contract/simple_while_loop.expected b/tests/expected/loop-contract/simple_while_loop.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/loop-contract/simple_while_loop.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/small_slice_eq.expected b/tests/expected/loop-contract/small_slice_eq.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/loop-contract/small_slice_eq.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL