├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── ruslic ├── Cargo.toml ├── src │ ├── cargo.rs │ ├── constant.rs │ ├── contract_translator.rs │ ├── fnsig_regions.rs │ ├── hir_translator.rs │ ├── interner.rs │ ├── lib.rs │ ├── main.rs │ ├── ruslik_pure.rs │ ├── ruslik_pure_helpers.rs │ ├── ruslik_ssl.rs │ ├── ruslik_types.rs │ ├── src_replace.rs │ ├── subst_generics.rs │ ├── suslik.rs │ ├── suslik_normalize.rs │ ├── suslik_translate.rs │ └── trait_bounds.rs └── tests │ ├── all │ ├── creusot-expected.mlcfg │ ├── creusot.rs │ └── russol.rs │ ├── ci-expected-full.txt │ ├── ci-expected.txt │ ├── ci.rs │ ├── crates-expected-full.txt │ ├── crates-expected.txt │ ├── synth │ ├── other │ │ ├── ci │ │ │ ├── copy.rs │ │ │ ├── many_choices.rs │ │ │ └── strange_args.rs │ │ ├── presentation │ │ │ └── stack.rs │ │ └── suslik_other │ │ │ └── avl.rs │ └── paper │ │ ├── rust │ │ ├── a-sll_tutorial │ │ │ └── stack.rs │ │ ├── b-stackoverflow │ │ │ ├── nested_refs.rs │ │ │ ├── reborrow.rs │ │ │ ├── replace_1.rs │ │ │ ├── replace_2.rs │ │ │ └── swap_enum.rs │ │ └── c-custom │ │ │ ├── futures │ │ │ ├── futures.rs │ │ │ └── reborrow.rs │ │ │ ├── general │ │ │ ├── clone.rs │ │ │ ├── copy_out.rs │ │ │ ├── cyclic_then_reborrow.rs │ │ │ ├── enum.rs │ │ │ ├── find_false.rs │ │ │ ├── integer_construct.rs │ │ │ ├── list.rs │ │ │ ├── lstset_awkward.rs │ │ │ ├── rebrrw_choice.rs │ │ │ ├── test.rs │ │ │ ├── tree.rs │ │ │ ├── tree_bst.rs │ │ │ ├── tree_to_list.rs │ │ │ └── zeroing.rs │ │ │ └── list_ex │ │ │ ├── list_paper.creusot │ │ │ ├── list_paper.rs │ │ │ └── list_paper_alt.rs │ │ ├── suslik │ │ ├── a-integers │ │ │ └── integers.rs │ │ ├── b-singly_linked_list │ │ │ ├── sll.rs │ │ │ └── sll_srtl.creusot │ │ ├── c-sorted_list │ │ │ └── srtl.rs │ │ ├── d-list_of_lists │ │ │ └── multi-list.rs │ │ ├── e-binary_tree │ │ │ └── tree.rs │ │ └── f-rose_tree │ │ │ └── rose-tree.rs │ │ └── verifier │ │ ├── a-prusti │ │ ├── account.rs │ │ ├── box-specification.rs │ │ ├── bst_generics_paper.rs │ │ ├── fields-spec.rs │ │ ├── generics-basic-5.rs │ │ ├── generics-basic-6.rs │ │ ├── match-expr-variants.rs │ │ ├── match-expr.rs │ │ ├── move.rs │ │ ├── ownership2.rs │ │ ├── shared.rs │ │ ├── sign_mix.rs │ │ ├── signed.rs │ │ ├── unsigned.rs │ │ └── wand-identity2.rs │ │ └── b-creusot │ │ ├── inc_some_2_list.rs │ │ ├── swap_borrows.rs │ │ ├── take_max.rs │ │ ├── unnest.rs │ │ └── wrapping.rs │ ├── top_100_crates │ ├── aho-corasick-0.7.20.crate │ ├── ansi_term-0.12.1.crate │ ├── anyhow-1.0.70.crate │ ├── arrayvec-0.7.2.crate │ ├── atty-0.2.14.crate │ ├── autocfg-1.1.0.crate │ ├── base64-0.21.0.crate │ ├── bitflags-2.0.2.crate │ ├── block-buffer-0.10.4.crate │ ├── byteorder-1.4.3.crate │ ├── bytes-1.4.0.crate │ ├── cc-1.0.79.crate │ ├── cfg-if-1.0.0.crate │ ├── chrono-0.4.24.crate │ ├── clap-4.2.1.crate │ ├── crossbeam-channel-0.5.7.crate │ ├── crossbeam-epoch-0.9.14.crate │ ├── crossbeam-utils-0.8.15.crate │ ├── digest-0.10.6.crate │ ├── either-1.8.1.crate │ ├── env_logger-0.10.0.crate │ ├── fnv-1.0.7.crate │ ├── futures-0.3.28.crate │ ├── futures-channel-0.3.28.crate │ ├── futures-core-0.3.28.crate │ ├── futures-io-0.3.28.crate │ ├── futures-sink-0.3.28.crate │ ├── futures-task-0.3.28.crate │ ├── futures-util-0.3.28.crate │ ├── generic-array-0.14.7.crate │ ├── getrandom-0.2.8.crate │ ├── hashbrown-0.13.2.crate │ ├── heck-0.4.1.crate │ ├── http-0.2.9.crate │ ├── hyper-0.14.25.crate │ ├── idna-0.3.0.crate │ ├── indexmap-1.9.3.crate │ ├── itertools-0.10.5.crate │ ├── itoa-1.0.6.crate │ ├── lazy_static-1.4.0.crate │ ├── libc-0.2.140.crate │ ├── lock_api-0.4.9.crate │ ├── log-0.4.17.crate │ ├── matches-0.1.10.crate │ ├── memchr-2.5.0.crate │ ├── memoffset-0.8.0.crate │ ├── miniz_oxide-0.7.1.crate │ ├── mio-0.8.6.crate │ ├── nom-7.1.3.crate │ ├── num-integer-0.1.45.crate │ ├── num-traits-0.2.15.crate │ ├── num_cpus-1.15.0.crate │ ├── once_cell-1.17.1.crate │ ├── parking_lot-0.12.1.crate │ ├── parking_lot_core-0.9.7.crate │ ├── percent-encoding-2.2.0.crate │ ├── pin-project-lite-0.2.9.crate │ ├── pin-utils-0.1.0.crate │ ├── pkg-config-0.3.26.crate │ ├── ppv-lite86-0.2.17.crate │ ├── proc-macro2-1.0.54.crate │ ├── quote-1.0.26.crate │ ├── rand-0.8.5.crate │ ├── rand_chacha-0.3.1.crate │ ├── rand_core-0.6.4.crate │ ├── regex-1.7.3.crate │ ├── regex-syntax-0.6.29.crate │ ├── rustc_version-0.4.0.crate │ ├── ryu-1.0.13.crate │ ├── scopeguard-1.1.0.crate │ ├── semver-1.0.17.crate │ ├── serde-1.0.159.crate │ ├── serde_derive-1.0.159.crate │ ├── serde_json-1.0.95.crate │ ├── sha2-0.10.6.crate │ ├── slab-0.4.8.crate │ ├── smallvec-1.10.0.crate │ ├── socket2-0.5.1.crate │ ├── strsim-0.10.0.crate │ ├── syn-2.0.12.crate │ ├── termcolor-1.2.0.crate │ ├── textwrap-0.16.0.crate │ ├── thiserror-1.0.40.crate │ ├── thiserror-impl-1.0.40.crate │ ├── thread_local-1.1.7.crate │ ├── time-0.3.20.crate │ ├── tinyvec-1.6.0.crate │ ├── tokio-1.27.0.crate │ ├── tokio-util-0.7.7.crate │ ├── toml-0.7.3.crate │ ├── tracing-0.1.37.crate │ ├── tracing-core-0.1.30.crate │ ├── typenum-1.16.0.crate │ ├── unicode-bidi-0.3.13.crate │ ├── unicode-normalization-0.1.22.crate │ ├── unicode-width-0.1.10.crate │ ├── unicode-xid-0.2.4.crate │ ├── url-2.3.1.crate │ ├── version_check-0.9.4.crate │ └── winapi-0.3.9.crate │ ├── top_crates.rs │ └── unsupported │ ├── generics_unsound.rs │ ├── privacy.rs │ ├── reborrows.rs │ └── sorted.rs ├── russol-contracts ├── Cargo.toml └── src │ └── lib.rs ├── russol-macros ├── Cargo.toml └── src │ ├── lib.rs │ └── rewriter.rs └── rust-toolchain.toml /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Cargo Build & Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build_and_test: 12 | name: Test on local files 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | # > git clone --recursive https://github.com/JonasAlaif/russol-alpha.git 17 | with: 18 | submodules: 'recursive' 19 | - name: Install Z3 20 | # Ensure that `z3` is in your path 21 | uses: pavpanchekha/setup-z3@v0.3.0 22 | - run: z3 --version 23 | - name: Setup JDK 24 | # Ensure that java (e.g. https://adoptium.net/temurin/releases) and sbt (https://www.scala-sbt.org/) work 25 | uses: actions/setup-java@v3.6.0 26 | with: 27 | distribution: temurin 28 | java-version: 8 29 | cache: sbt 30 | - name: Cache cargo/rustc 31 | uses: Swatinem/rust-cache@v2.0.0 32 | - name: Build 33 | run: cargo build --verbose 34 | - name: Run tests 35 | # Install rust (https://www.rust-lang.org/tools/install), the correct version will automatically be downloaded 36 | # To run on a single file use: `cargo run /path/to/file.rs` 37 | run: cargo test --package ruslic --test ci -- all_tests --exact --nocapture 38 | 39 | test_crates: 40 | # if: ${{ false }} # disable for now 41 | strategy: 42 | matrix: 43 | version: [0, 1, 2, 3] 44 | fail-fast: false 45 | name: Test on crates ${{ matrix.version }} 46 | runs-on: ubuntu-latest 47 | env: 48 | RUSLIC_TIMEOUT: 150000 49 | steps: 50 | - uses: actions/checkout@v3 51 | with: 52 | submodules: 'recursive' 53 | - name: Install Z3 54 | uses: pavpanchekha/setup-z3@v0.3.0 55 | - run: z3 --version 56 | - name: Setup JDK 57 | uses: actions/setup-java@v3.6.0 58 | with: 59 | distribution: temurin 60 | java-version: 8 61 | cache: sbt 62 | - name: Cache cargo/rustc 63 | uses: Swatinem/rust-cache@v2.0.0 64 | - name: Build 65 | run: cargo build --verbose 66 | - name: Run tests 67 | run: cargo test --package ruslic --test top_crates -- top_crates_${{ matrix.version }} --exact --nocapture 68 | - name: Upload failing cases 69 | uses: actions/upload-artifact@v3 70 | with: 71 | name: failing-fns-${{ matrix.version }} 72 | path: suslik/tmp-*/ 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | # Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | /.vscode/ 13 | /.metals/ 14 | 15 | /ruslic/tests/top_100_crates/* 16 | !/ruslic/tests/top_100_crates/*.crate 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "suslik"] 2 | path = suslik 3 | url = https://github.com/JonasAlaif/suslik.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "ruslic", 4 | "russol-contracts", 5 | "russol-macros", 6 | ] 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RusSOL 2 | 3 | This is the implementation of the tool described in the paper [Leveraging Rust Types for Program Synthesis](https://dl.acm.org/doi/abs/10.1145/3591278). 4 | 5 | For setup and use, follow the steps that the [CI](https://github.com/JonasAlaif/russol-alpha/blob/main/.github/workflows/ci.yml) takes. Execute with `cargo run /path/to/file.rs`. 6 | 7 | Test files can be found [here](https://github.com/JonasAlaif/russol-alpha/tree/main/ruslic/tests), the ones under `synth` work (tested with CI), there are also some under `unsupported` due to known limitations of the search. 8 | -------------------------------------------------------------------------------- /ruslic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ruslic" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "ruslic" 8 | path = "src/main.rs" 9 | 10 | [[bin]] 11 | name = "cargo-russol" 12 | path = "src/cargo.rs" 13 | 14 | [dependencies] 15 | itertools = "0.10.3" 16 | wait-timeout = "^0.2.0" 17 | serde = { version = "^1.0", features = ["derive"] } 18 | serde_json = "^1.0" 19 | toml = "0.5" 20 | rand = "0.8" 21 | 22 | [dev-dependencies] 23 | walkdir = "2.3.2" 24 | 25 | reqwest = { version = "^0.11", features = ["blocking"] } 26 | serde = "^1.0" 27 | serde_derive = "^1.0" 28 | serde_json = "^1.0" 29 | 30 | [package.metadata.rust-analyzer] 31 | rustc_private=true 32 | -------------------------------------------------------------------------------- /ruslic/src/cargo.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | 3 | fn main() { 4 | if let Err(code) = process(std::env::args().skip(1)) { 5 | std::process::exit(code); 6 | } 7 | } 8 | 9 | fn process(args: I) -> Result<(), i32> 10 | where 11 | I: Iterator, 12 | { 13 | let mut russol_rustc_path = std::env::current_exe() 14 | .expect("current executable path invalid") 15 | .with_file_name("ruslic"); 16 | if cfg!(windows) { 17 | russol_rustc_path.set_extension("exe"); 18 | } 19 | 20 | // Remove the "russol" argument when `cargo-russol` is invoked as 21 | // `cargo --cflag russol` (note the space in `cargo russol` rather than a `-`) 22 | let args: Vec<_> = args.skip_while(|arg| arg == "russol").collect(); 23 | 24 | let exit_status = std::process::Command::new("cargo") 25 | .arg("check") 26 | .args(&args) 27 | // Otherwise `rust-analyzer` might run `cargo check` and so this one would do nothing (cached result) 28 | .env("CARGO_INCREMENTAL", "false") 29 | .env("RUST_TOOLCHAIN", get_rust_toolchain_channel()) 30 | .env("RUSTUP_TOOLCHAIN", get_rust_toolchain_channel()) 31 | .env("RUSTC_WRAPPER", russol_rustc_path) 32 | .env("RUSLIC_OPTIMISTICALLY_ALLOW_PRIVATE_TYPES", "true") 33 | .env("RUSLIC_SUBST_RESULT", "true") 34 | .env("RUSLIC_SUMMARISE", "true") 35 | .status() 36 | .expect("could not run cargo"); 37 | 38 | if !exit_status.success() { 39 | return Err(exit_status.code().unwrap_or(-1)); 40 | } 41 | 42 | let path = args.iter().find(|arg| arg.starts_with("--manifest-path=")); 43 | // Run fmt after `RUSLIC_SUBST_RESULT` 44 | let exit_status = std::process::Command::new("cargo") 45 | .arg("fmt") 46 | .args(path) 47 | .status() 48 | .expect("could not run cargo"); 49 | if !exit_status.success() { 50 | Ok(()) 51 | } else { 52 | Err(exit_status.code().unwrap_or(-1)) 53 | } 54 | } 55 | 56 | pub fn get_rust_toolchain_channel() -> String { 57 | #[derive(Deserialize)] 58 | struct RustToolchainFile { 59 | toolchain: RustToolchain, 60 | } 61 | 62 | #[derive(Deserialize)] 63 | struct RustToolchain { 64 | channel: String, 65 | #[allow(dead_code)] 66 | components: Option>, 67 | } 68 | 69 | let content = include_str!("../../rust-toolchain.toml"); 70 | // Be ready to accept TOML format 71 | // See: https://github.com/rust-lang/rustup/pull/2438 72 | if content.starts_with("[toolchain]") { 73 | let rust_toolchain: RustToolchainFile = 74 | toml::from_str(content).expect("failed to parse rust-toolchain file"); 75 | rust_toolchain.toolchain.channel 76 | } else { 77 | content.trim().to_string() 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ruslic/src/constant.rs: -------------------------------------------------------------------------------- 1 | use rustc_hir::def_id::DefId; 2 | use rustc_middle::{ 3 | mir::ConstantKind, 4 | ty::{self, Const, ConstKind, ParamEnv, SubstsRef, Ty, TyCtxt}, 5 | }; 6 | 7 | use crate::ruslik_pure::PureExpression; 8 | 9 | pub(crate) fn translate_constant<'tcx>( 10 | tcx: TyCtxt<'tcx>, 11 | def_id: DefId, 12 | subst: SubstsRef<'tcx>, 13 | ty: Ty<'tcx>, 14 | ) -> PureExpression<'tcx> { 15 | let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), subst); 16 | let constant = tcx.mk_const(ty::ConstS { 17 | kind: ty::ConstKind::Unevaluated(uneval), 18 | ty, 19 | }); 20 | 21 | from_ty_const(tcx, constant, tcx.param_env(def_id)) 22 | } 23 | 24 | fn from_ty_const<'tcx>( 25 | tcx: TyCtxt<'tcx>, 26 | c: Const<'tcx>, 27 | env: ParamEnv<'tcx>, 28 | ) -> PureExpression<'tcx> { 29 | if let ConstKind::Param(_) = c.kind() { 30 | todo!("const generic parameters are not yet supported"); 31 | } 32 | try_to_bits(tcx, env, c.ty(), c) 33 | } 34 | 35 | pub(crate) fn try_to_bits<'tcx, C: ToBits<'tcx>>( 36 | tcx: TyCtxt<'tcx>, 37 | env: ParamEnv<'tcx>, 38 | ty: Ty<'tcx>, 39 | c: C, 40 | ) -> PureExpression<'tcx> { 41 | use rustc_middle::ty::{FloatTy, IntTy, UintTy}; 42 | use rustc_type_ir::sty::TyKind::{Bool, Float, Int, Uint}; 43 | match ty.kind() { 44 | Int(ity) => { 45 | let bits = c.get_bits(tcx, env, ty).unwrap(); 46 | let bits: i128 = match *ity { 47 | IntTy::I128 => bits as i128, 48 | IntTy::Isize => bits as i64 as i128, 49 | IntTy::I8 => bits as i8 as i128, 50 | IntTy::I16 => bits as i16 as i128, 51 | IntTy::I32 => bits as i32 as i128, 52 | IntTy::I64 => bits as i64 as i128, 53 | }; 54 | let expr = PureExpression::from_u128(bits.unsigned_abs(), ty); 55 | if bits < 0 { 56 | -expr 57 | } else { 58 | expr 59 | } 60 | } 61 | Uint(uty) => { 62 | let bits = c.get_bits(tcx, env, ty).unwrap(); 63 | let bits: u128 = match *uty { 64 | UintTy::U128 => bits as u128, 65 | UintTy::Usize => bits as u64 as u128, 66 | UintTy::U8 => bits as u8 as u128, 67 | UintTy::U16 => bits as u16 as u128, 68 | UintTy::U32 => bits as u32 as u128, 69 | UintTy::U64 => bits as u64 as u128, 70 | }; 71 | PureExpression::from_u128(bits, ty) 72 | } 73 | Bool => PureExpression::from_bool(c.get_bits(tcx, env, ty) == Some(1), tcx), 74 | Float(FloatTy::F32) => { 75 | let bits = c.get_bits(tcx, env, ty); 76 | let float = f32::from_bits(bits.unwrap() as u32); 77 | todo!("Floats are not yet supported ({float})") 78 | } 79 | Float(FloatTy::F64) => { 80 | let bits = c.get_bits(tcx, env, ty); 81 | let float = f64::from_bits(bits.unwrap() as u64); 82 | todo!("Floats are not yet supported ({float})") 83 | } 84 | _ => todo!("Unsupported constant of type {ty}"), 85 | } 86 | } 87 | 88 | pub(crate) trait ToBits<'tcx> { 89 | fn get_bits(&self, tcx: TyCtxt<'tcx>, env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option; 90 | } 91 | 92 | impl<'tcx> ToBits<'tcx> for Const<'tcx> { 93 | fn get_bits(&self, tcx: TyCtxt<'tcx>, env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option { 94 | self.try_eval_bits(tcx, env, ty) 95 | } 96 | } 97 | impl<'tcx> ToBits<'tcx> for ConstantKind<'tcx> { 98 | fn get_bits(&self, tcx: TyCtxt<'tcx>, env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option { 99 | self.try_eval_bits(tcx, env, ty) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /ruslic/src/fnsig_regions.rs: -------------------------------------------------------------------------------- 1 | use rustc_ast::Mutability; 2 | use rustc_middle::ty::{TyKind, Region, Ty, GenericArgKind, TyCtxt}; 3 | use rustc_data_structures::fx::FxHashSet; 4 | 5 | pub fn collect_blocking_lfts<'tcx>(args: Vec>, ret: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> (FxHashSet>, FxHashSet>) { 6 | let args_mutrefs = args.iter().flat_map(|ty| TypeMutRefWalker::new(*ty, tcx).into_iter()); 7 | let (sources, tys): (FxHashSet<_>, Vec<_>) = args_mutrefs.unzip(); 8 | let dests = ret.walk().chain(tys.into_iter().flat_map(Ty::walk)).filter_map(|ga| 9 | match ga.unpack() { 10 | GenericArgKind::Lifetime(r) => Some(r), 11 | GenericArgKind::Type(_) => None, 12 | GenericArgKind::Const(_) => None, 13 | } 14 | ).collect(); 15 | (sources, dests) 16 | } 17 | 18 | struct TypeMutRefWalker<'tcx> { 19 | stack: Vec<(&'tcx TyKind<'tcx>, usize, usize)>, 20 | visited: FxHashSet>, 21 | tcx: TyCtxt<'tcx>, 22 | } 23 | 24 | impl<'tcx> TypeMutRefWalker<'tcx> { 25 | pub fn new(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self { 26 | let stack = vec![(ty.kind(), 0, 0)]; 27 | Self { stack, visited: FxHashSet::default(), tcx } 28 | } 29 | fn push_ty(&mut self, ty: Ty<'tcx>) { 30 | if !self.visited.contains(&ty) { 31 | self.visited.insert(ty); 32 | self.stack.push((ty.kind(), 0, 0)); 33 | } 34 | } 35 | } 36 | impl<'tcx> Iterator for TypeMutRefWalker<'tcx> { 37 | type Item = (Region<'tcx>, Ty<'tcx>); 38 | 39 | fn next(&mut self) -> Option { 40 | loop { 41 | match self.stack.pop()? { 42 | (TyKind::Bool, _, _) | 43 | (TyKind::Char, _, _) | 44 | (TyKind::Int(_), _, _) | 45 | (TyKind::Uint(_), _, _) | 46 | (TyKind::Float(_), _, _) | 47 | (TyKind::Param(_), _, _) | 48 | // Cannot go through immut ref without loosing mutability (itself or nothing inside can be blocked or blocking) 49 | (TyKind::Ref(_, _, Mutability::Not), _, _) | 50 | // Cannot go through raw pointer without unsafe 51 | (TyKind::RawPtr(_), _, _) => (), 52 | (kind@TyKind::Tuple(tys), idx, _) => if idx < tys.len() { 53 | self.stack.push((kind, idx+1, 0)); 54 | self.push_ty(tys[idx]); 55 | }, 56 | (kind@TyKind::Adt(adt, substs), vidx, fidx) => { 57 | if adt.is_phantom_data() { 58 | // Special rule: since PhantomData owns T 59 | self.push_ty(substs.type_at(0)); 60 | continue; 61 | } 62 | if let Some(v) = adt.variants().iter().skip(vidx).next() { 63 | if fidx < v.fields.len() { 64 | self.stack.push((kind, vidx, fidx+1)); 65 | self.push_ty(v.fields[fidx].ty(self.tcx, substs)); 66 | } else { 67 | self.stack.push((kind, vidx+1, 0)); 68 | } 69 | } 70 | } 71 | (TyKind::Ref(r, ty, Mutability::Mut), _, _) => return Some((*r, *ty)), 72 | (kind, _, _) => todo!("Unsupported ty {:?}", kind), 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ruslic/src/hir_translator.rs: -------------------------------------------------------------------------------- 1 | use rustc_ast::{AttrItem, AttrKind, MacArgs, MacArgsEq, NormalAttr}; 2 | use rustc_data_structures::fx::FxHashMap; 3 | use rustc_hir::{ 4 | def::DefKind, 5 | def_id::{DefId, LocalDefId}, 6 | }; 7 | use rustc_middle::{ 8 | thir::{ClosureExpr, Expr, ExprId, ExprKind, Stmt, StmtKind, Thir}, 9 | ty::{DefIdTree, TyCtxt, TyKind, WithOptConstParam}, 10 | }; 11 | 12 | use crate::ruslik_ssl::Var; 13 | use crate::{contract_translator::to_expr, ruslik_pure::PureExpression, ruslik_types::RuslikFnSig}; 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct PureFn<'tcx> { 17 | pub def_id: DefId, 18 | pub arg_names: Vec, 19 | pub expr: PureExpression<'tcx>, 20 | pub pure_post: PureExpression<'tcx>, 21 | pub executable: bool, 22 | pub ast_nodes: usize, 23 | } 24 | 25 | pub type PureFnMap<'tcx> = FxHashMap>; 26 | 27 | #[derive(Clone, Copy, Eq, PartialEq)] 28 | enum SpecKind { 29 | Requires, 30 | Ensures, 31 | TrustedEnsures, 32 | } 33 | 34 | pub struct HirTranslator<'tcx> { 35 | tcx: TyCtxt<'tcx>, 36 | pub pure_fns: PureFnMap<'tcx>, 37 | pub extern_fns: Vec>, 38 | pub impure_fns: Vec<(bool, RuslikFnSig<'tcx>)>, 39 | // Type predicates 40 | // types: FxHashMap>, 41 | // basic_types: FxHashSet>, 42 | } 43 | impl<'tcx> HirTranslator<'tcx> { 44 | pub fn new(tcx: TyCtxt<'tcx>) -> Self { 45 | Self { 46 | tcx, 47 | pure_fns: FxHashMap::default(), 48 | extern_fns: Vec::new(), 49 | impure_fns: Vec::new(), 50 | // types: FxHashMap::default(), 51 | // basic_types: FxHashSet::default(), 52 | } 53 | } 54 | 55 | pub fn translate(&mut self, def_id: DefId) -> Option<()> { 56 | match self.tcx.def_kind(def_id) { 57 | DefKind::AssocFn | DefKind::Fn => { 58 | // Don't try to synth derived fns 59 | let span: rustc_span::Span = self.tcx.def_span(def_id); 60 | if span.from_expansion() { 61 | return None; 62 | } 63 | // Don't try to synth default trait fns 64 | let parent = self 65 | .tcx 66 | .opt_parent(def_id) 67 | .map(|parent| self.tcx.def_kind(parent)); 68 | if matches!( 69 | parent, 70 | Some(DefKind::Trait) | Some(DefKind::Fn) | Some(DefKind::AssocFn) 71 | ) { 72 | return None; 73 | } 74 | 75 | let (mut is_pure, mut is_extern, mut is_synth, mut params) = 76 | (false, false, false, String::new()); 77 | for a in self.tcx.get_attrs_unchecked(def_id) { 78 | if let AttrKind::Normal(p) = &a.kind { 79 | let NormalAttr { 80 | item: AttrItem { path, args, .. }, 81 | .. 82 | } = &**p; 83 | match path.segments.last().unwrap().ident.as_str() { 84 | "ruslik_helper" => return None, 85 | "ruslik_pure" => is_pure = true, 86 | "ruslik_extern_spec" => is_extern = true, 87 | "ruslik_synth" => is_synth = true, 88 | "ruslik_params" => { 89 | if let MacArgs::Eq(_, MacArgsEq::Hir(lit)) = args { 90 | params = params + " " + lit.token_lit.symbol.as_str(); 91 | } else { 92 | unreachable!() 93 | } 94 | } 95 | _ => (), 96 | } 97 | } 98 | } 99 | let (pure_pre, pure_post, ast_nodes) = self.collect_contracts(def_id, is_pure)?; 100 | if is_pure { 101 | assert!( 102 | !is_extern, 103 | "Cannot have `#[pure]` and `#[extern_spec]` on the same function!" 104 | ); 105 | assert!( 106 | params.is_empty(), 107 | "Cannot have params on `#[pure]`: \"{}\"", 108 | params 109 | ); 110 | let (expr, pure_nodes) = to_expr(self.tcx, def_id.expect_local()); 111 | let arg_names = self 112 | .tcx 113 | .fn_arg_names(def_id) 114 | .iter() 115 | .map(|id| Var::arg(id.name)) 116 | .collect(); 117 | // Get return value 118 | let gen_sig = self.tcx.fn_sig(def_id); 119 | let sig: rustc_middle::ty::FnSig = 120 | self.tcx.liberate_late_bound_regions(def_id, gen_sig); 121 | let executable = sig.inputs().len() == 1 122 | && !sig.inputs()[0] 123 | .to_string() 124 | .starts_with("russol_contracts::") 125 | && !sig.output().to_string().starts_with("russol_contracts::"); 126 | let pure_fn = PureFn { 127 | def_id, 128 | arg_names, 129 | expr, 130 | pure_post, 131 | executable, 132 | ast_nodes: ast_nodes + pure_nodes, 133 | }; 134 | self.pure_fns.insert(def_id, pure_fn); 135 | } else { 136 | if is_extern { 137 | assert!( 138 | params.is_empty(), 139 | "Cannot have params on `#[extern_spec]`: \"{}\"", 140 | params 141 | ); 142 | } 143 | self.tcx.ensure().mir_borrowck(def_id.expect_local()); 144 | let sig = 145 | RuslikFnSig::new(def_id, self.tcx, pure_pre, pure_post, params, ast_nodes); 146 | if is_extern { 147 | self.extern_fns.push(sig); 148 | } else { 149 | self.impure_fns.push((is_synth, sig)); 150 | } 151 | } 152 | } 153 | DefKind::Closure 154 | | DefKind::AnonConst 155 | | DefKind::Const 156 | | DefKind::Static(_) 157 | | DefKind::AssocConst => (), 158 | other => println!("Skipping {:?}", other), 159 | } 160 | Some(()) 161 | } 162 | 163 | fn parse_attr_count(&self, def_id: DefId) -> Option { 164 | self.tcx.get_attrs_unchecked(def_id).iter().find_map(|a| { 165 | if let AttrKind::Normal(p) = &a.kind { 166 | if let NormalAttr { 167 | item: 168 | AttrItem { 169 | path, 170 | args: MacArgs::Eq(_, MacArgsEq::Hir(l)), 171 | .. 172 | }, 173 | .. 174 | } = &**p 175 | { 176 | if path.segments.last().unwrap().ident.to_string() == "ruslik_spec_count" { 177 | Some(l.token_lit.symbol.as_str().parse::().ok()?) 178 | } else { 179 | None 180 | } 181 | } else { 182 | None 183 | } 184 | } else { 185 | None 186 | } 187 | }) 188 | } 189 | 190 | fn collect_contracts( 191 | &self, 192 | def_id: DefId, 193 | is_pure: bool, 194 | ) -> Option<(PureExpression<'tcx>, PureExpression<'tcx>, usize)> { 195 | let (thir, _) = self 196 | .tcx 197 | .thir_body(WithOptConstParam::unknown(def_id.expect_local())) 198 | .unwrap(); 199 | if format!("{thir:?}") == "Steal { value: RwLock(RefCell { value: None }) }" { 200 | // let span: rustc_span::Span = self.tcx.def_span(def_id); 201 | // (span, "stolen body!") 202 | return None; 203 | } 204 | let thir = &thir.borrow(); 205 | let mut contracts_len = 0; 206 | let contract = thir 207 | .stmts 208 | .iter() 209 | .map_while(|stmt| self.parse_spec_stmt(stmt, thir)) 210 | .fold( 211 | ( 212 | PureExpression::from_bool(true, self.tcx), 213 | PureExpression::from_bool(true, self.tcx), 214 | 0, 215 | ), 216 | |acc, (kind, cid)| { 217 | assert_eq!(is_pure, kind == SpecKind::TrustedEnsures); 218 | let (mut acc_pre, mut acc_post, ast_nodes) = acc; 219 | let (expr, new_nodes) = to_expr(self.tcx, cid); 220 | contracts_len += 1; 221 | if kind == SpecKind::Requires { 222 | acc_pre = acc_pre & expr; 223 | } else { 224 | acc_post = acc_post & expr; 225 | } 226 | (acc_pre, acc_post, ast_nodes + new_nodes) 227 | }, 228 | ); 229 | 230 | if let Some(attrs) = self.parse_attr_count(def_id) { 231 | assert_eq!( 232 | contracts_len, attrs, 233 | "Unexpected THIR layout! Could not find all specs." 234 | ); 235 | } else { 236 | assert_eq!( 237 | contracts_len, 0, 238 | "Could not find ruslik_spec_count attribute, even though specs are present!" 239 | ); 240 | // if !is_pure { println!("Found no contract for {}. Unconstrained synthesis is generally uninteresting!", self.tcx.item_name(def_id)); } 241 | } 242 | Some(contract) 243 | } 244 | fn parse_spec_stmt(&self, stmt: &Stmt, thir: &Thir) -> Option<(SpecKind, LocalDefId)> { 245 | if let StmtKind::Expr { expr, .. } = stmt.kind { 246 | if let ExprKind::Call { 247 | fun, 248 | args: box [arg], 249 | .. 250 | } = Self::descope_expr(expr, thir)?.kind 251 | { 252 | let fun: &Expr = Self::descope_expr(fun, thir)?; 253 | if let TyKind::FnDef(def_id, _) = fun.ty.kind() 254 | && let ExprKind::Closure(box ClosureExpr { closure_id, .. }) = Self::descope_expr(arg, thir)?.kind 255 | && self.tcx.crate_name(def_id.krate).to_string() == "russol_contracts" 256 | { 257 | match self.tcx.item_name(*def_id).to_string().as_str() { 258 | "requires" => Some((SpecKind::Requires, closure_id)), 259 | "ensures" => Some((SpecKind::Ensures, closure_id)), 260 | "trusted_ensures" => Some((SpecKind::TrustedEnsures, closure_id)), 261 | _ => None, 262 | } 263 | // println!("crate_name: {}", self.tcx.crate_name(def_id.krate)); 264 | // println!("item_name: {}", self.tcx.item_name(*def_id)); 265 | // println!("fun: {:#?}", fun.ty.kind()); 266 | // println!("arg: {:#?}", Self::descope_expr(arg, &thir)); 267 | } else { None } 268 | } else { 269 | None 270 | } 271 | } else { 272 | None 273 | } 274 | } 275 | /// Ok to return None (rather than panic), since we later check that we have parsed the correct amount of specs 276 | fn descope_expr<'thir, 'b>(expr: ExprId, thir: &'b Thir<'thir>) -> Option<&'b Expr<'thir>> { 277 | if let ExprKind::Scope { value, .. } = thir.exprs[expr].kind { 278 | Some(&thir.exprs[value]) 279 | } else { 280 | None 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /ruslic/src/interner.rs: -------------------------------------------------------------------------------- 1 | use rustc_data_structures::fx::FxHashMap; 2 | use rustc_hir::def_id::DefId; 3 | use rustc_middle::ty::TyCtxt; 4 | 5 | use crate::{ 6 | hir_translator::HirTranslator, 7 | src_replace::replace_with_sln, 8 | suslik::{SuslikProgram, SynthesisResult}, 9 | }; 10 | 11 | pub fn intern(tcx: TyCtxt, timeout: u64) -> Option> { 12 | rustc_typeck::check_crate(tcx).ok()?; 13 | tcx.hir() 14 | .par_body_owners(|def_id| tcx.ensure().check_match(def_id.to_def_id())); 15 | tcx.hir().par_for_each_module(|module| { 16 | tcx.ensure().check_mod_privacy(module); 17 | }); 18 | if tcx.sess.has_errors().is_some() { 19 | return None; 20 | } 21 | 22 | let mut translator = HirTranslator::new(tcx); 23 | for def_id in tcx.hir().body_owners() { 24 | let def_id = def_id.to_def_id(); 25 | tcx.ensure().check_match(def_id); 26 | if tcx.sess.has_errors().is_some() { 27 | return None; 28 | } 29 | // println!("Translating {:?}", def_id); 30 | translator.translate(def_id); 31 | } 32 | 33 | let multithreaded = std::env::var("RUSLIC_THREAD_COUNT") 34 | .map(|v| v.parse::().unwrap()) 35 | .unwrap_or(8); 36 | if multithreaded > 1 { 37 | solve_multithreaded(tcx, timeout, translator, multithreaded) 38 | } else { 39 | solve(tcx, timeout, translator) 40 | } 41 | } 42 | 43 | pub fn solve<'tcx>( 44 | tcx: TyCtxt<'tcx>, 45 | timeout: u64, 46 | translator: HirTranslator<'tcx>, 47 | ) -> Option> { 48 | let multifn = translator.impure_fns.len() > 1; 49 | let mut times = FxHashMap::default(); 50 | let only_synth = translator.impure_fns.iter().any(|(s, _)| *s); 51 | for (synth, sig) in translator.impure_fns.into_iter() { 52 | if only_synth && !synth { 53 | continue; 54 | } 55 | let def_id = sig.def_id; 56 | let name = tcx.def_path_str(def_id); 57 | let result = SuslikProgram::solve( 58 | tcx, 59 | sig, 60 | &translator.pure_fns, 61 | &translator 62 | .extern_fns 63 | .iter() 64 | .map(|ef| (*ef).clone()) 65 | .collect(), 66 | timeout, 67 | )?; 68 | handle_result(result, &mut times, tcx, def_id, name, multifn); 69 | } 70 | 71 | Some(times) 72 | } 73 | 74 | pub fn solve_multithreaded<'tcx>( 75 | tcx: TyCtxt<'tcx>, 76 | timeout: u64, 77 | translator: HirTranslator<'tcx>, 78 | thread_count: usize, 79 | ) -> Option> { 80 | let multifn = translator.impure_fns.len() > 1; 81 | let only_synth = translator.impure_fns.iter().any(|(s, _)| *s); 82 | let impure_fns = if only_synth { 83 | translator 84 | .impure_fns 85 | .into_iter() 86 | .filter(|(s, _)| *s) 87 | .collect() 88 | } else { 89 | translator.impure_fns 90 | }; 91 | let mut results: Vec<(DefId, Option)> = Vec::new(); 92 | let (tx, rx) = std::sync::mpsc::channel(); 93 | for (_, sig) in impure_fns.into_iter() { 94 | results.push((sig.def_id, None)); 95 | if results.len() > thread_count { 96 | let (idx, result) = rx.recv().unwrap(); 97 | let results: &mut (_, _) = &mut results[idx]; 98 | results.1 = result; 99 | } 100 | SuslikProgram::solve_in_thread( 101 | tx.clone(), 102 | results.len() - 1, 103 | tcx, 104 | sig, 105 | &translator.pure_fns, 106 | &translator 107 | .extern_fns 108 | .iter() 109 | .map(|ef| (*ef).clone()) 110 | .collect(), 111 | timeout, 112 | ); 113 | } 114 | for _ in 0..std::cmp::min(results.len(), thread_count) { 115 | let (idx, result) = rx.recv().unwrap(); 116 | results[idx].1 = result; 117 | } 118 | 119 | let mut times = FxHashMap::default(); 120 | for (def_id, result) in results.into_iter() { 121 | let name = tcx.def_path_str(def_id); 122 | let result = result.unwrap(); 123 | handle_result(result, &mut times, tcx, def_id, name, multifn); 124 | } 125 | 126 | Some(times) 127 | } 128 | 129 | pub fn handle_result( 130 | result: SynthesisResult, 131 | times: &mut FxHashMap, 132 | tcx: TyCtxt, 133 | def_id: DefId, 134 | name: String, 135 | multifn: bool, 136 | ) { 137 | if let Some(sln) = result.get_solved() { 138 | sln.print(); 139 | } 140 | 141 | // eprintln!("Synth for {:?} result: {:?}", def_id, result); 142 | let subst_result = std::env::var("RUSLIC_SUBST_RESULT") 143 | .map(|v| v.parse::().unwrap()) 144 | .unwrap_or(false); 145 | if let Some(sln) = result.get_solved() && subst_result { 146 | let sln_lines = sln.slns[0].loc; 147 | let sln = sln.slns[0].code.lines().skip(1).take(sln_lines).fold("\n".to_string(), |acc, line| acc + line + "\n"); 148 | replace_with_sln(tcx, def_id, sln, multifn); 149 | } 150 | times.insert(name, result); 151 | } 152 | -------------------------------------------------------------------------------- /ruslic/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![feature(iter_intersperse)] 3 | #![feature(box_patterns)] 4 | #![feature(if_let_guard)] 5 | #![feature(let_chains)] 6 | #![feature(assert_matches)] 7 | #![allow(clippy::needless_lifetimes)] 8 | 9 | extern crate rustc_ast; 10 | extern crate rustc_ast_pretty; 11 | extern crate rustc_attr; 12 | extern crate rustc_const_eval; 13 | extern crate rustc_data_structures; 14 | extern crate rustc_driver; 15 | extern crate rustc_errors; 16 | extern crate rustc_hir; 17 | extern crate rustc_hir_pretty; 18 | extern crate rustc_infer; 19 | extern crate rustc_interface; 20 | extern crate rustc_middle; 21 | extern crate rustc_session; 22 | extern crate rustc_span; 23 | extern crate rustc_target; 24 | extern crate rustc_type_ir; 25 | extern crate rustc_typeck; 26 | 27 | mod constant; 28 | mod contract_translator; 29 | mod hir_translator; 30 | mod interner; 31 | mod ruslik_pure; 32 | mod ruslik_pure_helpers; 33 | mod ruslik_ssl; 34 | mod ruslik_types; 35 | mod src_replace; 36 | mod subst_generics; 37 | pub mod suslik; 38 | mod suslik_normalize; 39 | mod suslik_translate; 40 | mod trait_bounds; 41 | // mod fnsig_regions; 42 | 43 | use rustc_data_structures::fx::FxHashMap; 44 | use rustc_driver::{Callbacks, Compilation, RunCompiler}; 45 | use rustc_interface::{interface::Compiler, Queries}; 46 | use suslik::SynthesisResult; 47 | 48 | struct CompilerCallbacks { 49 | is_cargo: bool, 50 | timeout: u64, 51 | timings: FxHashMap, 52 | } 53 | impl Callbacks for CompilerCallbacks { 54 | fn after_expansion<'tcx>( 55 | &mut self, 56 | compiler: &Compiler, 57 | queries: &'tcx Queries<'tcx>, 58 | ) -> Compilation { 59 | compiler.session().abort_if_errors(); 60 | 61 | unsafe { 62 | crate::ruslik_ssl::VARS = Some(rustc_data_structures::fx::FxHashSet::default()); 63 | crate::ruslik_ssl::UNIFS = Some(rustc_data_structures::fx::FxHashSet::default()); 64 | } 65 | if false { 66 | let (krate, _resolver, _lint_store) = &mut *queries.expansion().unwrap().peek_mut(); 67 | rustc_driver::pretty::print_after_parsing( 68 | compiler.session(), 69 | compiler.input(), 70 | krate, 71 | rustc_session::config::PpMode::Source(rustc_session::config::PpSourceMode::Normal), 72 | None, 73 | ); 74 | } 75 | 76 | queries.prepare_outputs().unwrap(); 77 | 78 | queries.global_ctxt().unwrap().peek_mut().enter(|tcx| { 79 | match crate::interner::intern(tcx, self.timeout) { 80 | Some(times) => self.timings = times, 81 | None => { 82 | panic!("error"); 83 | // tcx.sess.err(e.0); 84 | } 85 | } 86 | }); 87 | if self.is_cargo { 88 | Compilation::Continue 89 | } else { 90 | Compilation::Stop 91 | } 92 | } 93 | } 94 | 95 | /// Adds the correct --sysroot option. 96 | fn sys_root() -> Vec { 97 | let home = option_env!("RUSTUP_HOME"); 98 | let toolchain = option_env!("RUSTUP_TOOLCHAIN"); 99 | let sysroot = format!("{}/toolchains/{}", home.unwrap(), toolchain.unwrap()); 100 | vec!["--sysroot".into(), sysroot] 101 | } 102 | 103 | /// Pass rustc arguments in args (namely, path to rust file). Timeout is per function to be synthesized. 104 | pub fn run_on_file( 105 | mut args: Vec, 106 | timeout: u64, 107 | is_cargo: bool, 108 | ) -> Result, rustc_errors::ErrorGuaranteed> { 109 | let current_dir = std::env::current_exe().unwrap(); 110 | let current_dir = current_dir.parent().unwrap(); 111 | // Back out once more if running CI 112 | let current_dir = if current_dir.ends_with("deps") { 113 | current_dir.parent().unwrap() 114 | } else { 115 | current_dir 116 | }; 117 | 118 | if !is_cargo { 119 | args.push("--crate-type=lib".into()); 120 | } 121 | args.extend(["-A".into(), "dead_code".into()]); 122 | args.extend(["-A".into(), "unused_variables".into()]); 123 | args.extend(sys_root()); 124 | if !is_cargo { 125 | args.push("--edition=2021".into()); 126 | } 127 | std::env::set_var( 128 | "LD_LIBRARY_PATH", 129 | std::env::current_exe().unwrap().as_os_str(), 130 | ); 131 | std::env::set_var( 132 | "DYLD_LIBRARY_PATH", 133 | std::env::current_exe().unwrap().as_os_str(), 134 | ); 135 | let russol_contracts = current_dir.join("librussol_contracts.rlib"); 136 | args.extend([ 137 | "--extern".into(), 138 | format!( 139 | "russol_contracts={}", 140 | russol_contracts.as_os_str().to_str().unwrap() 141 | ), 142 | ]); 143 | 144 | args.push("-L".into()); 145 | args.push(format!( 146 | "dependency={}", 147 | current_dir.join("deps").as_os_str().to_str().unwrap() 148 | )); 149 | 150 | // println!("Running with args: {:?}", args); 151 | let mut cc = CompilerCallbacks { 152 | is_cargo, 153 | timeout, 154 | timings: FxHashMap::default(), 155 | }; 156 | RunCompiler::new(&args, &mut cc).run()?; 157 | Ok(cc.timings) 158 | } 159 | -------------------------------------------------------------------------------- /ruslic/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | extern crate rustc_driver; 3 | extern crate rustc_errors; 4 | 5 | use ruslic::suslik::{SynthesisResult, SynthesisResultKind}; 6 | use rustc_errors::ErrorGuaranteed; 7 | 8 | fn main() -> Result<(), ErrorGuaranteed> { 9 | rustc_driver::catch_fatal_errors(|| { 10 | match filter_args() { 11 | // Skip synth 12 | (args, _, true) => { 13 | let status = std::process::Command::new("rustc") 14 | .args(args.into_iter().skip(1)) 15 | .status() 16 | .unwrap(); 17 | assert!(status.success()); 18 | } 19 | // Do synth 20 | (args, is_cargo, false) => { 21 | let timeout = std::env::var("RUSLIC_TIMEOUT") 22 | .map(|v| v.parse::().unwrap()) 23 | .unwrap_or(1_000_000); 24 | if let Ok(res) = ruslic::run_on_file(args, timeout, is_cargo) { 25 | summarise(res.values().collect()); 26 | } 27 | } 28 | } 29 | }) 30 | } 31 | 32 | fn filter_args() -> (Vec, bool, bool) { 33 | let mut is_cargo = false; 34 | let mut crate_name = false; 35 | let mut is_build_script = false; 36 | let args = std::env::args() 37 | .filter(|arg| { 38 | if crate_name { 39 | assert!(!is_build_script); 40 | is_build_script = arg == "build_script_main" || arg == "build_script_build"; 41 | } 42 | crate_name = arg == "--crate-name"; 43 | 44 | is_cargo = is_cargo || arg == "rustc"; 45 | arg != "rustc" 46 | }) 47 | .collect(); 48 | let skip_synth = 49 | (std::env::var("CARGO_PRIMARY_PACKAGE").is_err() && is_cargo) || is_build_script; 50 | (args, is_cargo, skip_synth) 51 | } 52 | 53 | fn summarise(res: Vec<&SynthesisResult>) { 54 | if !std::env::var("RUSLIC_SUMMARISE") 55 | .map(|v| v.parse().unwrap()) 56 | .unwrap_or(false) 57 | { 58 | return; 59 | } 60 | let (mut unsupported, mut unsolvable, mut timeout, mut solved) = (0, 0, 0, Vec::new()); 61 | for res in res.iter() { 62 | match &res.kind { 63 | SynthesisResultKind::Unsupported(_) => unsupported += 1, 64 | SynthesisResultKind::Unsolvable(_) => unsolvable += 1, 65 | SynthesisResultKind::Timeout => timeout += 1, 66 | SynthesisResultKind::Solved(s) => { 67 | for (idx, sln) in s.slns.iter().enumerate() { 68 | if solved.len() <= idx { 69 | solved.push((0, 0, 0)); 70 | } 71 | solved[idx].0 += 1; 72 | solved[idx].1 += sln.loc; 73 | solved[idx].2 += sln.synth_time; 74 | } 75 | } 76 | } 77 | } 78 | println!("Unsupported: {unsupported}\nUnsolvable: {unsolvable}\nTimeout: {timeout}"); 79 | print!("Solved: "); 80 | for (solved, lines, time) in solved.iter() { 81 | print!(" {solved} (loc {lines}, time {time}),"); 82 | } 83 | if solved.is_empty() { 84 | println!("0"); 85 | } else { 86 | println!(); 87 | } 88 | let json = std::env::var("RUSLIC_SUMMARISE_JSON") 89 | .map(|v| v.parse::().unwrap()) 90 | .unwrap_or(false); 91 | if json { 92 | let serialized = serde_json::to_string(&res).unwrap(); 93 | assert!(!serialized.contains('\n')); 94 | println!("###### SUMMARY @@@@@@{serialized}"); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ruslic/src/ruslik_pure.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, ops::Neg}; 2 | 3 | use rustc_hir::def_id::DefId; 4 | use rustc_middle::{ 5 | mir::Field, 6 | ty::{Ty, TyCtxt}, 7 | }; 8 | use rustc_middle::{ 9 | mir::UnOp, 10 | ty::{subst::SubstsRef, util::Discr}, 11 | }; 12 | use rustc_target::abi::VariantIdx; 13 | 14 | use crate::ruslik_types::{field_to_name, DISC, FUT, OLD}; 15 | use crate::{ruslik_ssl::Var, ruslik_types::AdtIdent}; 16 | 17 | #[derive(Debug, Copy, Clone)] 18 | pub enum UnOpKind { 19 | Snap, 20 | UnOp(UnOp), 21 | } 22 | #[derive(Debug, Clone, Copy)] 23 | pub enum CallInfo<'tcx> { 24 | Pure(DefId, SubstsRef<'tcx>), 25 | Builtin(BuiltinCallKind), 26 | } 27 | #[derive(Debug, Clone, Copy)] 28 | pub enum BuiltinCallKind { 29 | SetConstruct, 30 | SetContains, 31 | } 32 | type BinOp = rustc_hir::BinOpKind; 33 | type Lit = rustc_ast::ast::LitKind; 34 | 35 | #[derive(Debug, Clone)] 36 | pub struct PureExpression<'tcx> { 37 | ty: Ty<'tcx>, 38 | kind: ExprKind<'tcx>, 39 | } 40 | 41 | #[derive(Debug, Clone)] 42 | pub enum ExprKind<'tcx> { 43 | Never, 44 | Var(Var), 45 | // Debrujin(usize), 46 | Lit(Lit), 47 | BinOp(BinOp, Box>, Box>), 48 | UnOp(UnOpKind, Box>), 49 | // Constructor: 50 | Constructor(AdtIdent, VariantIdx, Vec<(Field, PureExpression<'tcx>)>), 51 | // Destructor: 52 | Field(Box>, VariantIdx, Field), 53 | // Match(Box>, Vec), 54 | IfElse( 55 | Box>, 56 | Box>, 57 | Box>, 58 | ), 59 | Call(CallInfo<'tcx>, Vec>), 60 | } 61 | impl<'tcx> ExprKind<'tcx> { 62 | pub fn with_ty(self, ty: Ty<'tcx>) -> PureExpression<'tcx> { 63 | PureExpression { ty, kind: self } 64 | } 65 | } 66 | impl<'tcx> PureExpression<'tcx> { 67 | pub fn ty(&self) -> Ty<'tcx> { 68 | self.ty 69 | } 70 | pub fn kind(&self) -> &ExprKind<'tcx> { 71 | &self.kind 72 | } 73 | pub fn ty_mut(&mut self) -> &mut Ty<'tcx> { 74 | &mut self.ty 75 | } 76 | pub fn kind_mut(&mut self) -> &mut ExprKind<'tcx> { 77 | &mut self.kind 78 | } 79 | pub fn get_kind(self) -> ExprKind<'tcx> { 80 | self.kind 81 | } 82 | pub fn is_result(&self) -> bool { 83 | matches!(&self.kind, ExprKind::Var(v) if v.uuid() == "result") 84 | } 85 | pub fn is_true(&self) -> bool { 86 | matches!(&self.kind, ExprKind::Lit(Lit::Bool(true))) 87 | } 88 | pub fn from_u32(item: u32, tcx: TyCtxt<'tcx>) -> Self { 89 | ExprKind::Lit(Lit::Int( 90 | item.into(), 91 | rustc_ast::ast::LitIntType::Unsuffixed, 92 | )) 93 | .with_ty(tcx.types.u32) 94 | } 95 | pub fn from_u64(item: u64, tcx: TyCtxt<'tcx>) -> Self { 96 | ExprKind::Lit(Lit::Int( 97 | item.into(), 98 | rustc_ast::ast::LitIntType::Unsuffixed, 99 | )) 100 | .with_ty(tcx.types.u64) 101 | } 102 | pub fn from_u128(item: u128, ty: Ty<'tcx>) -> Self { 103 | ExprKind::Lit(Lit::Int(item, rustc_ast::ast::LitIntType::Unsuffixed)).with_ty(ty) 104 | } 105 | pub fn from_i64(item: i64, tcx: TyCtxt<'tcx>) -> Self { 106 | // TODO: fix ty 107 | let lit = Self::from_u64(item.abs().try_into().unwrap(), tcx) 108 | .kind 109 | .with_ty(tcx.types.i32); 110 | if item < 0 { 111 | -lit 112 | } else { 113 | lit 114 | } 115 | } 116 | pub fn from_bool(item: bool, tcx: TyCtxt<'tcx>) -> Self { 117 | ExprKind::Lit(Lit::Bool(item)).with_ty(tcx.types.bool) 118 | } 119 | pub fn _eq(self, other: Self, tcx: TyCtxt<'tcx>) -> Self { 120 | assert!(self.ty == other.ty); 121 | ExprKind::BinOp(BinOp::Eq, Box::new(self), Box::new(other)).with_ty(tcx.types.bool) 122 | } 123 | pub fn _ge(self, other: Self, tcx: TyCtxt<'tcx>) -> Self { 124 | assert!(self.ty == other.ty); 125 | ExprKind::BinOp(BinOp::Ge, Box::new(self), Box::new(other)).with_ty(tcx.types.bool) 126 | } 127 | pub fn borrow(self, ty: Ty<'tcx>) -> Self { 128 | let (f, v) = OLD; 129 | ExprKind::Constructor(AdtIdent::intern("&"), v, vec![(f, self)]).with_ty(ty) 130 | } 131 | pub fn deref(mut self, fut: bool) -> Self { 132 | // Check safety and get inner type 133 | if self.ty.is_box() { 134 | assert!(!fut, "Cannot use `^` on Box type expression `{self}`!") 135 | } 136 | let ty = if let Some(ty_mut) = self.ty.builtin_deref(false) { 137 | ty_mut.ty 138 | } else { 139 | panic!("Cannot deref expr `{self}`!") 140 | }; 141 | // Get variant and field indicies 142 | let (f, v) = if fut { FUT } else { OLD }; 143 | if let ExprKind::Constructor(adt_id, _, fields) = &mut self.kind && adt_id.as_str() == "&" { 144 | let expr = fields.pop().unwrap().1; 145 | assert!(!fut, "Encountered disallowed `^&...` in expression: ^&{}", expr); 146 | expr 147 | } else { 148 | ExprKind::Field(Box::new(self), v, f).with_ty(ty) 149 | } 150 | } 151 | pub fn field(self, v: VariantIdx, f: Field, ty: Ty<'tcx>) -> Self { 152 | let new_self = self; 153 | if let ExprKind::Constructor(_, vid, fields) = new_self.kind { 154 | assert!(v == vid); 155 | return fields.into_iter().find(|(fd, _)| *fd == f).unwrap().1; 156 | } 157 | ExprKind::Field(Box::new(new_self), v, f).with_ty(ty) 158 | } 159 | pub fn disc(self, disc: Discr<'tcx>, tcx: TyCtxt<'tcx>) -> Self { 160 | let variant = Self::from_u128(disc.val, disc.ty); 161 | let (f, v) = DISC; 162 | self.field(v, f, variant.ty())._eq(variant, tcx) 163 | } 164 | } 165 | impl<'tcx> fmt::Display for PureExpression<'tcx> { 166 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 167 | match &self.kind { 168 | ExprKind::Never => todo!(), 169 | ExprKind::Var(v) => write!(f, "{}", v.rname()), 170 | // Self::Debrujin(d) => write!(f, "!{}", d), 171 | ExprKind::Lit(Lit::Bool(b)) => write!(f, "{}", b), 172 | ExprKind::Lit(Lit::Int(i, rustc_ast::ast::LitIntType::Unsuffixed)) => { 173 | write!(f, "{}", i) 174 | } 175 | ExprKind::Lit(_) => todo!("{:?}", self), 176 | ExprKind::UnOp(UnOpKind::UnOp(UnOp::Not), e) => write!(f, "(! {})", e), 177 | ExprKind::UnOp(UnOpKind::UnOp(UnOp::Neg), e) => write!(f, "(- {})", e), 178 | ExprKind::UnOp(UnOpKind::Snap, e) => write!(f, "(@ {})", e), 179 | ExprKind::BinOp(op, box l, box r) => write!(f, "({} {} {})", l, op.as_str(), r), 180 | ExprKind::Constructor(id, _, fds) if id.as_str().starts_with("tuple_") => write!( 181 | f, 182 | "{}({})", 183 | id, 184 | fds.iter() 185 | .map(|(_, expr)| expr.to_string()) 186 | .intersperse(", ".to_string()) 187 | .collect::() 188 | ), 189 | ExprKind::Constructor(id, v, fds) => write!( 190 | f, 191 | "{}_{}{{ {} }}", 192 | id, 193 | v.as_u32(), 194 | fds.iter() 195 | .map(|(fd, expr)| field_to_name(*fd, *v, self.ty()) + ": " + &expr.to_string()) 196 | .intersperse(", ".to_string()) 197 | .collect::() 198 | ), 199 | ExprKind::Field(pe, v, field) => { 200 | write!(f, "{}.{}", pe, field_to_name(*field, *v, pe.ty)) 201 | } 202 | ExprKind::IfElse(cond, te, fe) => { 203 | write!(f, "if {} {{ {} }} else {{ {} }}", cond, te, fe) 204 | } 205 | ExprKind::Call(ci, args) => write!( 206 | f, 207 | "[{:?}]({})", 208 | ci, 209 | args.iter() 210 | .map(|arg| arg.to_string()) 211 | .intersperse(", ".to_string()) 212 | .collect::() 213 | ), 214 | } 215 | } 216 | } 217 | 218 | impl<'tcx> Neg for PureExpression<'tcx> { 219 | type Output = Self; 220 | fn neg(self) -> Self::Output { 221 | let ty = self.ty; 222 | ExprKind::UnOp(UnOpKind::UnOp(UnOp::Neg), Box::new(self)).with_ty(ty) 223 | } 224 | } 225 | impl<'tcx> std::ops::BitAnd> for PureExpression<'tcx> { 226 | type Output = Self; 227 | 228 | fn bitand(self, rhs: Self) -> Self { 229 | assert!(self.ty == rhs.ty); 230 | let ty = self.ty; 231 | // TODO: check types and behave differently for boolean and int types 232 | match (&self.kind, &rhs.kind) { 233 | // Optimizations: 234 | (ExprKind::Lit(Lit::Bool(true)), _) | (_, ExprKind::Lit(Lit::Bool(false))) => rhs, 235 | (_, ExprKind::Lit(Lit::Bool(true))) | (ExprKind::Lit(Lit::Bool(false)), _) => self, 236 | // Constructor: 237 | _ => ExprKind::BinOp(BinOp::And, Box::new(self), Box::new(rhs)).with_ty(ty), 238 | } 239 | } 240 | } 241 | impl<'tcx> std::ops::BitOr> for PureExpression<'tcx> { 242 | type Output = Self; 243 | 244 | fn bitor(self, rhs: Self) -> Self { 245 | assert!(self.ty == rhs.ty); 246 | let ty = self.ty; 247 | // TODO: check types and behave differently for boolean and int types 248 | match (&self.kind, &rhs.kind) { 249 | // Optimizations: 250 | (ExprKind::Lit(Lit::Bool(true)), _) | (_, ExprKind::Lit(Lit::Bool(false))) => self, 251 | (_, ExprKind::Lit(Lit::Bool(true))) | (ExprKind::Lit(Lit::Bool(false)), _) => rhs, 252 | // Constructor: 253 | _ => ExprKind::BinOp(BinOp::Or, Box::new(self), Box::new(rhs)).with_ty(ty), 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /ruslic/src/ruslik_pure_helpers.rs: -------------------------------------------------------------------------------- 1 | use rustc_middle::ty::{TypeFoldable, TypeFolder}; 2 | 3 | use crate::ruslik_pure::{CallInfo, ExprKind, PureExpression}; 4 | 5 | impl<'tcx> PureExpression<'tcx> { 6 | pub fn walk_mut>(&mut self, walker: &mut T) { 7 | walker.walk_kind_mut(self.kind_mut()); 8 | } 9 | } 10 | impl<'tcx> ExprKind<'tcx> { 11 | pub fn walk_mut>(&mut self, walker: &mut T) { 12 | match self { 13 | ExprKind::Never | ExprKind::Var(_) | ExprKind::Lit(_) => (), 14 | ExprKind::BinOp(_, l, r) => { 15 | walker.walk_expr_mut(l); 16 | walker.walk_expr_mut(r); 17 | } 18 | ExprKind::UnOp(_, e) | ExprKind::Field(e, _, _) => walker.walk_expr_mut(e), 19 | ExprKind::Constructor(_, _, es) => { 20 | for e in es { 21 | walker.walk_expr_mut(&mut e.1) 22 | } 23 | } 24 | ExprKind::IfElse(c, t, e) => { 25 | walker.walk_expr_mut(c); 26 | walker.walk_expr_mut(t); 27 | walker.walk_expr_mut(e); 28 | } 29 | ExprKind::Call(_, es) => { 30 | for e in es { 31 | walker.walk_expr_mut(e) 32 | } 33 | } 34 | } 35 | } 36 | } 37 | pub trait PureExpressionWalker<'tcx>: Sized { 38 | fn walk_expr_mut(&mut self, e: &mut PureExpression<'tcx>) { 39 | e.walk_mut(self); 40 | } 41 | fn walk_kind_mut(&mut self, k: &mut ExprKind<'tcx>) { 42 | match k { 43 | ExprKind::Never | ExprKind::Var(_) | ExprKind::Lit(_) => (), 44 | ExprKind::BinOp(_, l, r) => { 45 | self.walk_expr_mut(l); 46 | self.walk_expr_mut(r); 47 | } 48 | ExprKind::UnOp(_, e) | ExprKind::Field(e, _, _) => self.walk_expr_mut(e), 49 | ExprKind::Constructor(_, _, es) => { 50 | for e in es { 51 | self.walk_expr_mut(&mut e.1) 52 | } 53 | } 54 | ExprKind::IfElse(c, t, e) => { 55 | self.walk_expr_mut(c); 56 | self.walk_expr_mut(t); 57 | self.walk_expr_mut(e); 58 | } 59 | ExprKind::Call(CallInfo::Pure(_, _), _) => todo!(), 60 | ExprKind::Call(CallInfo::Builtin(_), es) => { 61 | for e in es { 62 | self.walk_expr_mut(e) 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | impl<'tcx, F: TypeFolder<'tcx>> PureExpressionWalker<'tcx> for F { 70 | fn walk_expr_mut(&mut self, e: &mut PureExpression<'tcx>) { 71 | *e.ty_mut() = self.fold_ty(e.ty()); 72 | e.walk_mut(self); 73 | } 74 | fn walk_kind_mut(&mut self, k: &mut ExprKind<'tcx>) { 75 | if let ExprKind::Call(CallInfo::Pure(_, gas), _) = k { 76 | *gas = gas.fold_with(self); 77 | } 78 | k.walk_mut(self); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ruslic/src/ruslik_ssl.rs: -------------------------------------------------------------------------------- 1 | use rustc_data_structures::fx::FxHashSet; 2 | use rustc_span::Symbol; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 5 | pub struct Var { 6 | id: Symbol, 7 | } 8 | pub static mut VARS: Option> = None; 9 | pub static mut UNIFS: Option> = None; 10 | 11 | impl Var { 12 | pub fn arg(id: Symbol) -> Self { 13 | Self { id } 14 | } 15 | pub fn new(name: &str) -> Self { 16 | let id = Symbol::intern(name); 17 | Self { id } 18 | } 19 | pub fn extend(&self, field: &str) -> Self { 20 | Self::new(&(self.uuid() + "." + field)) 21 | } 22 | pub fn uuid(&self) -> String { 23 | self.id.as_str().to_string() 24 | } 25 | pub fn rname(&self) -> String { 26 | self.uuid() 27 | .chars() 28 | .map(|x| match x { 29 | '.' => '_', 30 | '*' => 'v', 31 | '^' => 'f', 32 | _ => x, 33 | }) 34 | .collect() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ruslic/src/ruslik_types.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use rustc_infer::infer::outlives::env::OutlivesEnvironment; 4 | use rustc_middle::ty::{Ty, TyCtxt, TyKind}; 5 | use rustc_span::def_id::DefId; 6 | use rustc_target::abi::VariantIdx; 7 | 8 | use crate::{ 9 | ruslik_pure, 10 | ruslik_ssl::{self, Var}, 11 | }; 12 | 13 | pub(crate) type AdtIdent = rustc_span::symbol::Symbol; 14 | pub(crate) type FieldIdent = rustc_middle::mir::Field; 15 | 16 | pub(crate) const DISC: (FieldIdent, VariantIdx) = (FieldIdent::MAX, VariantIdx::MAX); 17 | pub(crate) const FUT: (FieldIdent, VariantIdx) = ( 18 | FieldIdent::from_u32(FieldIdent::MAX_AS_U32 - 1), 19 | VariantIdx::from_u32(0), 20 | ); 21 | pub(crate) const OLD: (FieldIdent, VariantIdx) = (FieldIdent::MAX, VariantIdx::from_u32(0)); 22 | 23 | pub(crate) fn field_to_name(f: FieldIdent, v: VariantIdx, ty: Ty) -> String { 24 | // println!(" [TY: {}] ", ty); 25 | match (f, v) { 26 | DISC => "disc".to_string(), 27 | OLD => "*".to_string(), 28 | FUT => "^".to_string(), 29 | _ => match ty.kind() { 30 | TyKind::Adt(adt, _) => adt.variant(v).fields[f.index()].name.as_str().to_string(), 31 | TyKind::Tuple(_) => format!("_{}", f.as_u32()), 32 | _ => todo!(), 33 | }, 34 | } 35 | } 36 | 37 | #[derive(Clone)] 38 | pub struct RuslikFnSig<'tcx> { 39 | pub args: Vec<(ruslik_ssl::Var, Ty<'tcx>)>, 40 | pub ret: Ty<'tcx>, 41 | pub pure_pre: ruslik_pure::PureExpression<'tcx>, 42 | pub pure_post: ruslik_pure::PureExpression<'tcx>, 43 | pub def_id: DefId, 44 | pub outlives: Rc>, 45 | pub params: String, 46 | pub ast_nodes: usize, 47 | } 48 | impl<'tcx> RuslikFnSig<'tcx> { 49 | pub(crate) fn new( 50 | def_id: DefId, 51 | tcx: TyCtxt<'tcx>, 52 | pure_pre: ruslik_pure::PureExpression<'tcx>, 53 | pure_post: ruslik_pure::PureExpression<'tcx>, 54 | params: String, 55 | ast_nodes: usize, 56 | ) -> Self { 57 | // println!("{:?}", self.tcx.named_region_map(def_id.expect_local())); 58 | // println!("{:?}", def_id.expect_local()); 59 | if let Some(local) = def_id.as_local() { 60 | let owner = rustc_hir::HirId::make_owner(local); 61 | let _sig = tcx.hir().fn_sig_by_hir_id(owner).unwrap(); 62 | // println!("{:?}", sig.decl.inputs.into_iter().map(|p| p).collect::>()); 63 | } 64 | 65 | let gen_sig = tcx.fn_sig(def_id); 66 | // println!("{:?}", gen_sig.inputs_and_output()); 67 | 68 | let outlives = Rc::new(OutlivesEnvironment::new(tcx.param_env(def_id))); 69 | // println!("{:?}", outlives.free_region_map()); 70 | // let sig = self 71 | // .tcx 72 | // .normalize_erasing_late_bound_regions(self.tcx.param_env(def_id), gen_sig); 73 | let sig: rustc_middle::ty::FnSig = tcx.liberate_late_bound_regions(def_id, gen_sig); 74 | // println!("Input and output types of {:?}: {:?}", def_id, sig.inputs_and_output.iter().map(|ty| ty).collect::>()); 75 | let args = tcx 76 | .fn_arg_names(def_id) 77 | .iter() 78 | .map(|id| Var::arg(id.name)) 79 | .zip(sig.inputs().iter().copied()) 80 | .collect(); 81 | RuslikFnSig { 82 | args, 83 | ret: sig.output(), 84 | pure_pre, 85 | pure_post, 86 | def_id, 87 | outlives, 88 | params, 89 | ast_nodes, 90 | } 91 | } 92 | 93 | pub fn is_trivial(&self) -> bool { 94 | let return_trivial = self.ret.is_unit() || self.ret.is_primitive(); 95 | self.pure_post.is_true() && return_trivial 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /ruslic/src/src_replace.rs: -------------------------------------------------------------------------------- 1 | use rustc_hir::def_id::DefId; 2 | use rustc_middle::ty::TyCtxt; 3 | 4 | pub fn replace_with_sln<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, sln: String, keep_newlines: bool) { 5 | let span: rustc_span::Span = tcx.source_span(def_id.expect_local()); 6 | let sm = tcx.sess.source_map(); 7 | let flines = sm.span_to_lines(span).unwrap(); 8 | let fname = if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(fname)) = 9 | &flines.file.name 10 | { 11 | fname 12 | } else { 13 | panic!() 14 | }; 15 | let src = std::fs::read_to_string(fname).unwrap(); 16 | let (first, last) = (flines.lines.first().unwrap(), flines.lines.last().unwrap()); 17 | let (fline, lline) = (first.line_index, last.line_index + 1); 18 | let mut method = src.split('\n').into_iter().skip(fline).take(lline - fline); 19 | // let debug = src.split('\n').into_iter().skip(fline).take(lline - fline).fold(String::new(), |acc, line| acc + line + "\n"); 20 | // eprintln!("Fn ({:?} {:?}):\n{debug}", first, last); 21 | let line1 = method.next().unwrap(); 22 | // let mut last_line_len = line1.len(); 23 | // line1[first.start_col.0..].to_string() 24 | let method = method.fold(line1.to_string(), |acc, line| { 25 | // last_line_len = line.len(); 26 | acc + "\n" + line 27 | }); 28 | // eprintln!("{last_line_len} vs {}", last.end_col.0); 29 | // let to_cut_off = last_line_len-last.end_col.0; 30 | // let method = &method[..method.len()-to_cut_off]; 31 | // let fn_sig = format!("fn {}", tcx.item_name(def_id).as_str()); 32 | // eprintln!("Fn sig: {fn_sig} and method:\n{method}"); 33 | // let mut split = method.split(&fn_sig); 34 | // let body = if let (_, Some(body), None) = (split.next(), split.next(), split.next()) { 35 | // body 36 | // } else { panic!() }; 37 | let mut braces = 0; 38 | let mut curly_braces = 0; 39 | let mut in_body = 0; 40 | let mut in_comment = 0; 41 | let body: String = method 42 | .chars() 43 | .into_iter() 44 | .filter(|c| { 45 | if in_comment == 1 { 46 | if *c == '/' { 47 | in_comment = 2 48 | } else { 49 | in_comment = 0 50 | } 51 | } else if *c == '\n' { 52 | in_comment = 0 53 | } 54 | if in_comment == 0 { 55 | match c { 56 | '/' => in_comment = 1, 57 | '(' => braces += 1, 58 | ')' => braces -= 1, 59 | '{' => { 60 | if braces == 0 && curly_braces == 0 { 61 | in_body += 1; 62 | } 63 | curly_braces += 1 64 | } 65 | '}' => { 66 | curly_braces -= 1; 67 | if braces == 0 && curly_braces == 0 { 68 | in_body += 1; 69 | return true; 70 | } 71 | } 72 | _ => (), 73 | } 74 | }; 75 | in_body == 1 76 | }) 77 | .collect(); 78 | assert_eq!( 79 | in_body, 2, 80 | "Couldn't find {{ body }} in ({span:?})\n{method}" 81 | ); 82 | assert_eq!( 83 | method.matches(&body).count(), 84 | 1, 85 | "Looking for {body} in\n{method}" 86 | ); 87 | let sln = sln.replace('\n', &format!("\n{}", " ".repeat(first.start_col.0))); 88 | let mut new_method = method.replace(&body, &format!("{{{}}}", sln)); 89 | if keep_newlines { 90 | let (new_method_lines, method_lines) = ( 91 | new_method.matches('\n').count(), 92 | method.matches('\n').count(), 93 | ); 94 | match new_method_lines.cmp(&method_lines) { 95 | std::cmp::Ordering::Less => { 96 | for _ in 0..(method_lines - new_method_lines) { 97 | new_method += "\n"; 98 | } 99 | } 100 | std::cmp::Ordering::Equal => (), 101 | std::cmp::Ordering::Greater => { 102 | new_method = new_method.replacen('\n', "", new_method_lines - method_lines); 103 | } 104 | } 105 | assert_eq!( 106 | new_method.matches('\n').count(), 107 | method.matches('\n').count() 108 | ); 109 | } 110 | 111 | // let new_src = src.replace(&method, &new_method); 112 | let pre = src.split('\n').take(fline); 113 | let post = src.split('\n').skip(lline); 114 | let mut total = pre.chain(new_method.split('\n')).chain(post); 115 | let init = total.next().unwrap().to_string(); 116 | let new_src = total.fold(init, |acc, line| acc + "\n" + line); 117 | // println!("{src}\nVS\n{new_src}"); 118 | if keep_newlines { 119 | assert_eq!( 120 | src.matches('\n').count(), 121 | new_src.matches('\n').count(), 122 | "{fname:?} ({:?} {:?})\n{src}\n-- VS --\n{new_src}", 123 | first, 124 | last 125 | ); 126 | } 127 | std::fs::write(fname, &new_src).unwrap(); 128 | } 129 | -------------------------------------------------------------------------------- /ruslic/src/subst_generics.rs: -------------------------------------------------------------------------------- 1 | use std::ops::ControlFlow; 2 | 3 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; 4 | use rustc_middle::ty::{ 5 | GenericParamDef, ParamTy, SubstsRef, Ty, TyCtxt, TyKind, TypeFolder, TypeSuperFoldable, 6 | TypeVisitable, TypeVisitor, 7 | }; 8 | 9 | use crate::{ 10 | ruslik_pure::PureExpression, ruslik_pure_helpers::PureExpressionWalker, 11 | ruslik_types::RuslikFnSig, 12 | }; 13 | 14 | pub struct SubstFolder<'a, 'tcx> { 15 | pub tcx: TyCtxt<'tcx>, 16 | pub subst: Box Ty<'tcx> + 'a>, 17 | } 18 | impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { 19 | fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { 20 | self.tcx 21 | } 22 | fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { 23 | if !t.needs_subst() { 24 | t 25 | } else { 26 | match *t.kind() { 27 | TyKind::Param(p) => (self.subst)(p), 28 | _ => t.super_fold_with(self), 29 | } 30 | } 31 | } 32 | } 33 | impl<'a, 'tcx: 'a> SubstFolder<'a, 'tcx> { 34 | pub fn from_gens(tcx: TyCtxt<'tcx>, substs: &'a FxHashMap>) -> Self { 35 | SubstFolder { 36 | tcx, 37 | subst: Box::new(|p| substs[&p.index]), 38 | } 39 | } 40 | pub fn from_substs_ref(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>) -> Self { 41 | SubstFolder { 42 | tcx, 43 | subst: Box::new(|p| substs.type_at(p.index as usize)), 44 | } 45 | } 46 | } 47 | 48 | pub trait TyFoldable<'tcx> { 49 | fn subst>(&mut self, folder: &mut F); 50 | } 51 | 52 | impl<'tcx> TyFoldable<'tcx> for Ty<'tcx> { 53 | fn subst>(&mut self, folder: &mut F) { 54 | *self = folder.fold_ty(*self) 55 | } 56 | } 57 | 58 | impl<'tcx> TyFoldable<'tcx> for PureExpression<'tcx> { 59 | fn subst>(&mut self, folder: &mut F) { 60 | folder.walk_expr_mut(self); 61 | } 62 | } 63 | 64 | impl<'tcx> TyFoldable<'tcx> for RuslikFnSig<'tcx> { 65 | fn subst>(&mut self, folder: &mut F) { 66 | for (_, rt) in &mut self.args { 67 | *rt = folder.fold_ty(*rt); 68 | } 69 | self.ret = folder.fold_ty(self.ret); 70 | folder.walk_expr_mut(&mut self.pure_pre); 71 | folder.walk_expr_mut(&mut self.pure_post); 72 | } 73 | } 74 | 75 | pub struct SubstFinder<'tcx> { 76 | pub param_subs: FxHashMap>, 77 | } 78 | impl<'tcx> SubstFinder<'tcx> { 79 | fn new() -> Self { 80 | Self { 81 | param_subs: FxHashMap::default(), 82 | } 83 | } 84 | fn visit_ty_tuple(&mut self, l: Ty<'tcx>, r: Ty<'tcx>) -> Result<(), ()> { 85 | match (l.kind(), r.kind()) { 86 | (TyKind::Adt(l, ls), TyKind::Adt(r, rs)) if l == r => { 87 | assert_eq!(ls.len(), rs.len()); 88 | // TODO check and subst regions? 89 | for (l, r) in ls.types().zip(rs.types()) { 90 | self.visit_ty_tuple(l, r)?; 91 | } 92 | Ok(()) 93 | } 94 | (TyKind::Ref(_, l, _), TyKind::Ref(_, r, _)) => self.visit_ty_tuple(*l, *r), 95 | (TyKind::Tuple(l), TyKind::Tuple(r)) if l.len() == r.len() => { 96 | for (l, r) in l.iter().zip(r.iter()) { 97 | self.visit_ty_tuple(l, r)?; 98 | } 99 | Ok(()) 100 | } 101 | (TyKind::Param(p), _) => { 102 | if let Some(ty) = self.param_subs.insert(p.index, r) { 103 | if ty != r { 104 | return Err(()); 105 | } 106 | } 107 | Ok(()) 108 | } 109 | (a, b) => { 110 | if a == b { 111 | Ok(()) 112 | } else { 113 | Err(()) 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | #[derive(Debug)] 121 | pub struct VecIter { 122 | curr: Vec, 123 | started: bool, 124 | start: usize, 125 | end: usize, 126 | } 127 | impl VecIter { 128 | pub fn new(start: usize, end: usize, width: usize) -> Self { 129 | VecIter { 130 | curr: vec![end; width], 131 | started: false, 132 | start, 133 | end, 134 | } 135 | } 136 | pub fn next<'a>(&'a mut self) -> Option<&'a Vec> { 137 | for i in &mut self.curr { 138 | if *i + 1 >= self.end { 139 | *i = self.start; 140 | } else { 141 | *i += 1; 142 | return Some(&self.curr); 143 | } 144 | } 145 | if self.started { 146 | self.end = self.start; 147 | None 148 | } else { 149 | self.started = true; 150 | Some(&self.curr) 151 | } 152 | } 153 | pub fn total_elems(&self) -> usize { 154 | let res = (self.end - self.start) * self.curr.len(); 155 | if res == 0 { 156 | 1 157 | } else { 158 | res 159 | } 160 | } 161 | } 162 | #[derive(Debug)] 163 | pub struct VecIter2 { 164 | curr: Vec, 165 | max: Vec, 166 | started: bool, 167 | } 168 | impl VecIter2 { 169 | pub fn new(max: Vec) -> Self { 170 | if max.iter().any(|v| *v == 0) { 171 | VecIter2 { 172 | curr: Vec::new(), 173 | max: Vec::new(), 174 | started: true, 175 | } 176 | } else { 177 | VecIter2 { 178 | curr: vec![0; max.len()], 179 | max, 180 | started: false, 181 | } 182 | } 183 | } 184 | pub fn next<'a>(&'a mut self) -> Option<&'a Vec> { 185 | for (i, max) in &mut self.curr.iter_mut().zip(self.max.iter()) { 186 | if !self.started { 187 | self.started = true; 188 | return Some(&self.curr); 189 | } 190 | if *i + 1 >= *max { 191 | *i = 0; 192 | } else { 193 | *i += 1; 194 | return Some(&self.curr); 195 | } 196 | } 197 | if !self.started { 198 | self.started = true; 199 | Some(&self.curr) 200 | } else { 201 | None 202 | } 203 | } 204 | pub fn total_elems(&self) -> usize { 205 | if self.max.is_empty() { 206 | 0 207 | } else { 208 | self.max.iter().product() 209 | } 210 | } 211 | } 212 | 213 | pub struct SGenericsCollector<'tcx> { 214 | pub tcx: TyCtxt<'tcx>, 215 | pub synth_tys: FxHashSet>, 216 | } 217 | impl<'tcx> SGenericsCollector<'tcx> { 218 | pub fn find_subs_for_ext_fns( 219 | &self, 220 | extern_fn: &RuslikFnSig<'tcx>, 221 | ) -> Vec<(String, RuslikFnSig<'tcx>)> { 222 | // Collect generics 223 | let mut generics_of = self.tcx.generics_of(extern_fn.def_id); 224 | let param_count = generics_of.parent_count + generics_of.params.len(); 225 | let mut params: Vec<&GenericParamDef> = generics_of.params.iter().collect(); 226 | while let Some(parent) = generics_of.parent { 227 | generics_of = self.tcx.generics_of(parent); 228 | params.extend(&generics_of.params); 229 | } 230 | assert_eq!(param_count, params.len()); 231 | 232 | // 233 | // let param_to_idx: FxHashMap<_, _> = params.iter().filter(|p| matches!(p.kind, GenericParamDefKind::Type { .. })).enumerate().map(|(i, p)| (p.index, i)).collect(); 234 | let efn_tys: Vec<_> = extern_fn 235 | .args 236 | .iter() 237 | .map(|(_, ty)| *ty) 238 | .chain(std::iter::once(extern_fn.ret)) 239 | .collect(); 240 | 241 | let mut possible_subs = FxHashMap::default(); 242 | for efn_ty in &efn_tys { 243 | struct ParamCollector<'tcx> { 244 | params: FxHashMap>>, 245 | } 246 | impl<'tcx> TypeVisitor<'tcx> for ParamCollector<'tcx> { 247 | fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { 248 | use rustc_middle::ty::TypeSuperVisitable; 249 | t.super_visit_with(self); 250 | if let TyKind::Param(p) = t.kind() { 251 | self.params.insert(p.index, FxHashSet::default()); 252 | } 253 | ControlFlow::Continue(()) 254 | } 255 | } 256 | let mut pc = ParamCollector { 257 | params: FxHashMap::default(), 258 | }; 259 | pc.visit_ty(*efn_ty); 260 | // TODO: decide on this flag 261 | let all_possible_subs = false; 262 | if !all_possible_subs { 263 | for synth_ty in &self.synth_tys { 264 | let mut sf = SubstFinder::new(); 265 | // Peel refs for fn arg since these can be created (just borrow to create arg) 266 | let (mut efn_ty, mut synth_ty) = (*efn_ty, *synth_ty); 267 | while let TyKind::Ref(_, inner_ty, _) = efn_ty.kind() { 268 | efn_ty = *inner_ty; 269 | if let TyKind::Ref(_, inner_ty, _) = synth_ty.kind() { 270 | synth_ty = *inner_ty; 271 | } 272 | } 273 | if let Ok(()) = sf.visit_ty_tuple(efn_ty, synth_ty) { 274 | for (param, ty) in sf.param_subs { 275 | pc.params.get_mut(¶m).unwrap().insert(ty); 276 | } 277 | } 278 | } 279 | } 280 | for (param, tys) in pc.params { 281 | if all_possible_subs { 282 | possible_subs 283 | .entry(param) 284 | .or_insert_with(|| self.synth_tys.clone()); 285 | } else { 286 | possible_subs 287 | .entry(param) 288 | .and_modify(|old_tys| { 289 | *old_tys = tys.intersection(old_tys).copied().collect() 290 | }) 291 | .or_insert(tys); 292 | } 293 | } 294 | } 295 | let (possible_subs, maxs): (FxHashMap<_, _>, Vec<_>) = possible_subs 296 | .into_iter() 297 | .enumerate() 298 | .map(|(idx, (gp, tys))| { 299 | ( 300 | (gp, (idx, tys.iter().copied().collect::>())), 301 | tys.len(), 302 | ) 303 | }) 304 | .unzip(); 305 | // print!("Possible subst for {:?}: ", extern_fn.def_id); 306 | let mut perms = VecIter2::new(maxs); 307 | let mut possible_perms: Vec<(String, RuslikFnSig<'tcx>)> = 308 | Vec::with_capacity(perms.total_elems()); 309 | while let Some(perm) = perms.next() { 310 | let substs: FxHashMap<_, _> = possible_subs 311 | .iter() 312 | .map(|(gp, (idx, tys))| (*gp, tys[perm[*idx]])) 313 | .collect(); 314 | // print!("{:?}, ", substs); 315 | let substs_name = substs 316 | .values() 317 | .map(|ty| crate::suslik_translate::sanitize(&ty.to_string())) 318 | .intersperse("_".to_string()) 319 | .collect(); 320 | let mut efn = (*extern_fn).clone(); 321 | efn.subst(&mut SubstFolder::from_gens(self.tcx, &substs)); 322 | possible_perms.push((substs_name, efn)); 323 | } 324 | // println!(""); 325 | possible_perms 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /ruslic/src/trait_bounds.rs: -------------------------------------------------------------------------------- 1 | use rustc_data_structures::fx::FxHashSet; 2 | use rustc_hir::def_id::DefId; 3 | use rustc_middle::ty::{ 4 | AssocItems, AssocKind, EarlyBinder, GenericPredicates, PredicateKind, Subst, Ty, TyCtxt, 5 | }; 6 | 7 | use crate::{ruslik_pure::PureExpression, ruslik_types::RuslikFnSig}; 8 | 9 | pub(crate) fn find_trait_fns<'tcx>( 10 | tcx: TyCtxt<'tcx>, 11 | def_id: DefId, 12 | tys: &FxHashSet>, 13 | ) -> Vec> { 14 | let mut gp: Vec = vec![tcx.predicates_of(def_id)]; 15 | while let Some(parent) = gp.last().unwrap().parent { 16 | gp.push(tcx.predicates_of(parent)); 17 | } 18 | gp.into_iter() 19 | .flat_map(|gp| gp.predicates.iter()) 20 | .flat_map(|(pred, _)| { 21 | let pred_kind = if let Some(pred_kind) = pred.kind().no_bound_vars() { 22 | pred_kind 23 | } else { 24 | // eprintln!("Binder has vars: {:?} ({:?})", pred.kind(), def_id); 25 | return Vec::new(); 26 | }; 27 | match pred_kind { 28 | PredicateKind::Trait(t) => { 29 | if !tys.contains(&t.self_ty()) { 30 | // eprintln!("Skipping predicate {t:?} since self {} isn't translated.", t.self_ty()); 31 | return Vec::new(); 32 | } 33 | let items: &AssocItems = tcx.associated_items(t.trait_ref.def_id); 34 | items 35 | .in_definition_order() 36 | .filter(|i| i.kind == AssocKind::Fn) 37 | .flat_map(|method| { 38 | // Ignore trait fns with generics 39 | if !tcx.generics_of(method.def_id).params.is_empty() { 40 | return None; 41 | } 42 | let pre = PureExpression::from_bool(true, tcx); 43 | let post = PureExpression::from_bool(true, tcx); 44 | let mut rfs = 45 | RuslikFnSig::new(method.def_id, tcx, pre, post, String::new(), 0); 46 | // Ignore fns with args like "(_: i32, Struct { f }: Struct)" since we don't support them yet 47 | if rfs.args.iter().any(|(v, _)| v.uuid().is_empty()) { 48 | return None; 49 | } 50 | rfs.args = rfs 51 | .args 52 | .into_iter() 53 | .map(|(arg, ty)| { 54 | (arg, EarlyBinder(ty).subst(tcx, t.trait_ref.substs)) 55 | }) 56 | .collect(); 57 | // println!("Substituting at {:?} with ty {} and gens {:?}", method.def_id, rfs.ret.ty, t.trait_ref.substs); 58 | rfs.ret = EarlyBinder(rfs.ret).subst(tcx, t.trait_ref.substs); 59 | Some(rfs) 60 | }) 61 | .collect() 62 | } 63 | PredicateKind::RegionOutlives(_) | PredicateKind::TypeOutlives(_) => Vec::new(), 64 | PredicateKind::Projection(_) 65 | | PredicateKind::WellFormed(_) 66 | | PredicateKind::ObjectSafe(_) 67 | | PredicateKind::ClosureKind(_, _, _) 68 | | PredicateKind::Subtype(_) 69 | | PredicateKind::Coerce(_) 70 | | PredicateKind::ConstEvaluatable(_) 71 | | PredicateKind::ConstEquate(_, _) 72 | | PredicateKind::TypeWellFormedFromEnv(_) => { 73 | // eprintln!("Skipping Predicate: {:?}", pred_kind); 74 | Vec::new() 75 | } 76 | } 77 | }) 78 | .collect() 79 | } 80 | -------------------------------------------------------------------------------- /ruslic/tests/ci-expected.txt: -------------------------------------------------------------------------------- 1 | ### Measured timings (max 15_463ms) ### 2 | paper Solved 115 Time 1.0s SOL rules 26.49 Rust LOC 5.36 Code/Spec 1.37 | Overhead data: Sln nodes 16.65 Ann nodes 11.26 Non-exec pure fn nodes 0.90 3 | rust Solved 50 Time 1.3s SOL rules 32.98 Rust LOC 6.16 Code/Spec 1.27 | Overhead data: Sln nodes 19.20 Ann nodes 14.42 Non-exec pure fn nodes 0.72 4 | a-sll_tutorial Solved 7 Time 1.0s SOL rules 36.71 Rust LOC 6.29 Code/Spec 1.09 | Overhead data: Sln nodes 20.57 Ann nodes 18.86 Non-exec pure fn nodes 0.00 5 | b-stackoverflow Solved 4 Time 0.3s SOL rules 28.50 Rust LOC 4.75 Code/Spec 2.00 | Overhead data: Sln nodes 17.00 Ann nodes 8.50 Non-exec pure fn nodes 0.00 6 | c-custom Solved 39 Time 1.5s SOL rules 32.77 Rust LOC 6.28 Code/Spec 1.27 | Overhead data: Sln nodes 19.18 Ann nodes 14.23 Non-exec pure fn nodes 0.92 7 | suslik Solved 18 Time 2.1s SOL rules 49.28 Rust LOC 12.78 Code/Spec 2.84 | Overhead data: Sln nodes 36.44 Ann nodes 7.06 Non-exec pure fn nodes 5.78 8 | a-integers Solved 1 Time 0.2s SOL rules 19.00 Rust LOC 4.00 Code/Spec 2.25 | Overhead data: Sln nodes 18.00 Ann nodes 8.00 Non-exec pure fn nodes 0.00 9 | b-singly_linked_list Solved 7 Time 0.8s SOL rules 41.43 Rust LOC 10.14 Code/Spec 2.90 | Overhead data: Sln nodes 28.57 Ann nodes 7.57 Non-exec pure fn nodes 2.29 10 | c-sorted_list Solved 2 Time 0.3s SOL rules 26.50 Rust LOC 5.00 Code/Spec 0.82 | Overhead data: Sln nodes 16.00 Ann nodes 11.50 Non-exec pure fn nodes 8.00 11 | d-list_of_lists Solved 1 Time 1.3s SOL rules 50.00 Rust LOC 15.00 Code/Spec 1.02 | Overhead data: Sln nodes 41.00 Ann nodes 5.00 Non-exec pure fn nodes 35.00 12 | e-binary_tree Solved 5 Time 2.0s SOL rules 58.60 Rust LOC 16.00 Code/Spec 3.50 | Overhead data: Sln nodes 44.80 Ann nodes 5.60 Non-exec pure fn nodes 7.20 13 | f-rose_tree Solved 2 Time 10.4s SOL rules 91.00 Rust LOC 25.00 Code/Spec 2.31 | Overhead data: Sln nodes 70.50 Ann nodes 5.00 Non-exec pure fn nodes 25.50 14 | verifier Solved 47 Time 0.1s SOL rules 10.85 Rust LOC 1.66 Code/Spec 0.67 | Overhead data: Sln nodes 6.36 Ann nodes 9.51 Non-exec pure fn nodes 0.00 15 | a-prusti Solved 41 Time 0.1s SOL rules 9.39 Rust LOC 1.44 Code/Spec 0.63 | Overhead data: Sln nodes 5.46 Ann nodes 8.66 Non-exec pure fn nodes 0.00 16 | b-creusot Solved 6 Time 0.2s SOL rules 20.83 Rust LOC 3.17 Code/Spec 0.82 | Overhead data: Sln nodes 12.50 Ann nodes 15.33 Non-exec pure fn nodes 0.00 17 | ####################################### 18 | -------------------------------------------------------------------------------- /ruslic/tests/ci.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, path::PathBuf}; 2 | 3 | use ruslic::suslik::{MeanStats, Solved, SynthesisResult}; 4 | 5 | struct Category { 6 | dir: String, 7 | results: Vec<(String, SynthesisResult)>, 8 | children: Vec, 9 | depth: u32, 10 | } 11 | impl Category { 12 | fn run_tests_in_dir(dir: PathBuf, timeout: u64, depth: u32) -> Self { 13 | let mut cat = Self { 14 | dir: dir.file_name().unwrap().to_string_lossy().to_string(), 15 | results: Vec::new(), 16 | children: Vec::new(), 17 | depth, 18 | }; 19 | let mut paths: Vec<_> = std::fs::read_dir(dir) 20 | .unwrap() 21 | .map(|r| r.unwrap()) 22 | .collect(); 23 | paths.sort_by_key(|dir| dir.path()); 24 | 25 | for path in paths { 26 | if path.file_type().unwrap().is_file() { 27 | let filename = path.file_name(); 28 | let filename = filename.to_string_lossy(); 29 | if filename.ends_with(".rs") { 30 | println!( 31 | "Attempting synthesis for: {}", 32 | path.path().to_string_lossy() 33 | ); 34 | if let Ok(res) = ruslic::run_on_file( 35 | vec![ 36 | "/name/of/binary".to_string(), 37 | path.path().to_string_lossy().to_string(), 38 | ], 39 | timeout, 40 | false, 41 | ) { 42 | cat.results.extend( 43 | res.into_iter() 44 | .map(|(k, v)| (filename.to_string() + "::" + &k, v)), 45 | ); 46 | println!(); 47 | } else { 48 | panic!( 49 | "### Error when executing: {} ###", 50 | path.path().to_string_lossy() 51 | ); 52 | } 53 | } 54 | } else { 55 | let results = Self::run_tests_in_dir(path.path(), timeout, depth + 1); 56 | cat.children.push(results) 57 | } 58 | } 59 | // cat.solutions.extend(cat.results.iter().filter_map(|(_, res)| res.get_solved()).cloned()); 60 | // cat.solutions.extend(cat.children.iter().flat_map(|(_, res)| &res.solutions).cloned()); 61 | cat 62 | } 63 | fn solutions(&self) -> Box + '_> { 64 | Box::new( 65 | self.results 66 | .iter() 67 | .chain(self.children.iter().flat_map(|res| res.solutions())), 68 | ) 69 | } 70 | fn solved(&self) -> impl Iterator + '_ { 71 | self.solutions().flat_map(|sln| sln.1.get_solved()) 72 | } 73 | fn errors(&self) -> impl Iterator + '_ { 74 | self.solutions().filter(|sln| sln.1.get_solved().is_none()) 75 | } 76 | fn max_ms(&self) -> u64 { 77 | self.solved() 78 | .map(|sln| sln.slns.first().unwrap().synth_time) 79 | .max() 80 | .unwrap_or(0) 81 | } 82 | } 83 | 84 | impl Display for Category { 85 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 86 | let is_eval = std::env::var("RUSLIC_EVAL") 87 | .ok() 88 | .map(|s| s.parse::().unwrap()) 89 | .unwrap_or(false); 90 | if is_eval && self.depth > 2 { 91 | return Ok(()); 92 | } 93 | let solved = self.solved().count(); 94 | write!(f, "\n{: <22} Solved {solved}", self.dir)?; 95 | if solved > 0 { 96 | let (pure_fns, mean_stats) = MeanStats::calculate(self.solved()); 97 | let mean_stats = &mean_stats[0]; 98 | let pure_fn_nodes = pure_fns 99 | .values() 100 | .filter(|(exec, _)| !exec) 101 | .fold(0, |acc, (_, n)| acc + n); 102 | let pure_fn_nodes = pure_fn_nodes as f64 / solved as f64; 103 | let ann_overhead = mean_stats.ast_nodes / (mean_stats.spec_ast + pure_fn_nodes); 104 | write!( 105 | f, 106 | " \t Time {:.1}s \t SOL rules {:.2} \t Rust LOC {:.2} \t Code/Spec {:.2} {} Sln nodes {:.2} \t Ann nodes {:.2} \t Non-exec pure fn nodes {:.2}", 107 | mean_stats.synth_time / 1000., 108 | mean_stats.rule_apps, 109 | mean_stats.loc, 110 | ann_overhead, 111 | if is_eval { "| Overhead data:" } else { "\t" }, 112 | mean_stats.ast_nodes, 113 | mean_stats.spec_ast, 114 | pure_fn_nodes, 115 | // mean_stats.ast_nodes_unsimp, 116 | )?; 117 | if !is_eval { 118 | // let pure_fns: std::collections::HashMap<_, _> = pure_fns.iter().filter(|(_, (exec, _))| !exec).collect(); 119 | if !pure_fns.is_empty() { 120 | write!( 121 | f, 122 | " \t | \t Pure functions (\"name\": (executable, ast_nodes)): {pure_fns:?}" 123 | )?; 124 | } 125 | } 126 | } 127 | if !is_eval { 128 | for (r, v) in &self.results { 129 | write!(f, "\n {} - ", r)?; 130 | if let Some(sln) = v.get_solved() { 131 | let first = sln.slns.first().unwrap(); 132 | write!( 133 | f, 134 | "{} [{}/{}/{}/{}]", 135 | format_ms(first.synth_time), 136 | first.loc, 137 | first.ast_nodes, 138 | first.ast_nodes_unsimp, 139 | first.rule_apps 140 | )?; 141 | for sln in sln.slns.iter().skip(1) { 142 | write!( 143 | f, 144 | ", {} [{}/{}/{}/{}]", 145 | format_ms(sln.synth_time), 146 | sln.loc, 147 | sln.ast_nodes, 148 | sln.ast_nodes_unsimp, 149 | sln.rule_apps 150 | )?; 151 | } 152 | write!( 153 | f, 154 | " | spec_ast: {}, pfn_ast: {:?}", 155 | sln.synth_ast, sln.pure_fn_ast 156 | )?; 157 | } else { 158 | write!(f, "{:?}", v)?; 159 | } 160 | } 161 | } 162 | for child in &self.children { 163 | let child = child.to_string(); 164 | write!(f, "{}", child.replace('\n', "\n "))?; 165 | } 166 | Ok(()) 167 | } 168 | } 169 | 170 | #[test] 171 | fn all_tests() { 172 | let timeout = std::env::var("RUSLIC_TIMEOUT") 173 | .ok() 174 | .and_then(|t| t.parse().ok()) 175 | .unwrap_or(300_000); 176 | let is_eval = std::env::var("RUSLIC_EVAL") 177 | .ok() 178 | .map(|s| s.parse::().unwrap()) 179 | .unwrap_or(false); 180 | 181 | let results = if is_eval { 182 | all_tests_eval(timeout) 183 | } else { 184 | Category::run_tests_in_dir(PathBuf::from("./tests/synth/"), timeout, 0) 185 | }; 186 | let max_ms = format_ms(results.max_ms()); 187 | let results_str = format!("### Measured timings (max {max_ms}) ###{results}\n#######################################\n"); 188 | print!("{results_str}"); 189 | std::fs::write("./tests/ci-results.txt", results_str).expect("Unable to results to file!"); 190 | // Make sure this gets printed in the correct order in GitHub: 191 | std::thread::sleep(std::time::Duration::from_millis(1000)); 192 | if results.errors().count() > 0 { 193 | let err: Vec<_> = results.errors().map(|err| &err.0).collect(); 194 | panic!("Tests {:?} errored or exceeded timeout of {timeout}!", err); 195 | } 196 | } 197 | 198 | fn all_tests_eval(timeout: u64) -> Category { 199 | Category::run_tests_in_dir(PathBuf::from("./tests/synth/paper"), timeout, 0) 200 | } 201 | 202 | fn format_ms(ms: u64) -> String { 203 | format!("{}_{:03}ms", ms / 1000, ms % 1000) 204 | } 205 | -------------------------------------------------------------------------------- /ruslic/tests/crates-expected.txt: -------------------------------------------------------------------------------- 1 | ALL 100 | Unsupported 11460 (triv 2572, non-triv 8888) Failed 307 (triv 0, non-triv 307) Solved 2364 (triv 1036, non-triv 1328) | Trivial: Time 0.30s LOC 1.01 Rules 18.45 sln nodes 1.09 sln unsimp nodes 5.30 / Non-trivial: Time 0.79s LOC 1.41 Rules 20.81 sln nodes 5.08 sln unsimp nodes 11.31 | Falied due to: Timeout 9 (triv 0, non-triv 9), Unsolvable 298 (triv 0, non-triv 298) | Unsupported due to: ArraySlice 3066, CharFloat 883, UnnamedArgs 331, Closure 58, RequiresFlag 262, OtherTy 1486, Unsafe 5141, LateBoundRegion 7, Other 9, NonExhaustive 217, 2 | -------------------------------------------------------------------------------- /ruslic/tests/synth/other/ci/copy.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | // Using privacy to illustrate the point to avoid deconstructing and force copy 4 | mod m { 5 | #[derive(Clone, Copy)] 6 | pub struct Priv(i32); 7 | } 8 | 9 | // Required to copy-out from behind ref 10 | #[ensures(result === c)] 11 | fn foo(c: &m::Priv) -> m::Priv { 12 | *c 13 | } 14 | -------------------------------------------------------------------------------- /ruslic/tests/synth/other/ci/many_choices.rs: -------------------------------------------------------------------------------- 1 | // Taken from chrono 2 | pub struct Parsed { 3 | pub year: Option, 4 | pub year_div_100: Option, 5 | pub year_mod_100: Option, 6 | pub isoyear: Option, 7 | pub isoyear_div_100: Option, 8 | pub isoyear_mod_100: Option, 9 | pub month: Option, 10 | pub week_from_sun: Option, 11 | pub week_from_mon: Option, 12 | pub isoweek: Option, 13 | // pub weekday: Option, 14 | pub ordinal: Option, 15 | pub day: Option, 16 | pub hour_div_12: Option, 17 | pub hour_mod_12: Option, 18 | pub minute: Option, 19 | pub second: Option, 20 | pub nanosecond: Option, 21 | pub timestamp: Option, 22 | pub offset: Option, 23 | } 24 | 25 | fn owned(x: Parsed) -> (bool, bool) { 26 | (true, true) 27 | } 28 | fn borrowed(x: &mut Parsed) -> (bool, bool) { 29 | (true, true) 30 | } 31 | -------------------------------------------------------------------------------- /ruslic/tests/synth/other/ci/strange_args.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | struct Tuple { a: T, b: T } 3 | // Unsupported: 4 | // #[ensures(result.0 === a && result.1 === b)] 5 | // fn to_tuple(Tuple { a, b }: Tuple) -> (T, T) { 6 | // (a, b) 7 | // } 8 | #[ensures(result.0 === f.a && result.1 === f.b)] 9 | fn to_tuple(f: Tuple) -> (T, T) { 10 | (f.a, f.b) 11 | } 12 | -------------------------------------------------------------------------------- /ruslic/tests/synth/other/presentation/stack.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | pub struct ListP { 4 | head: Link, 5 | } 6 | 7 | type Link = Option>>; 8 | 9 | struct NodeP { 10 | elem: T, 11 | next: Link, 12 | } 13 | 14 | #[extern_spec] 15 | #[ensures(result === *x)] 16 | #[ensures(matches!(^x, None))] 17 | fn take(x: &mut Option) -> Option { ruslik!() } 18 | 19 | impl NodeP { 20 | #[pure] 21 | pub fn len(&self) -> i32 { 22 | match &self.next { 23 | None => 1, 24 | Some(n) => 1 + n.len(), 25 | } 26 | } 27 | #[pure] 28 | pub fn elems(&self) -> Set { 29 | match &self.next { 30 | None => set!{&self.elem}, 31 | Some(n) => n.elems() + set!{&self.elem}, 32 | } 33 | } 34 | } 35 | 36 | impl ListP { 37 | /// 38 | /// Example 1 [Simple owned] 39 | /// 40 | #[ensures(matches!(result.head, Some(_)))] 41 | pub fn single(elem: T) -> Self { 42 | let bx = NodeP { elem, next: ::std::option::Option::None }; 43 | let _0 = Box::new(bx); 44 | let head = ::std::option::Option::Some(_0); 45 | ListP { head } 46 | } 47 | 48 | /// 49 | /// Example 2 [Borrows/Measures] 50 | /// 51 | #[ensures((^self).len() == self.len() + tail.len())] 52 | pub fn append(&mut self, tail: Self) { 53 | Self::append_8(&mut self.head, tail.head) 54 | } 55 | #[helper] fn append_8(head_self: &mut std::option::Option>>, head_tail: std::option::Option>>) { 56 | match head_self { 57 | ::std::option::Option::None => *head_self = head_tail, 58 | ::std::option::Option::Some(_0) => Self::append_8(&mut _0.next, head_tail), 59 | } 60 | } 61 | 62 | #[pure] 63 | pub fn len(&self) -> i32 { 64 | match &self.head { 65 | None => 0, 66 | Some(n) => n.len(), 67 | } 68 | } 69 | #[pure] 70 | pub fn elems(&self) -> Set { 71 | match &self.head { 72 | None => set!{}, 73 | Some(n) => n.elems(), 74 | } 75 | } 76 | 77 | /// 78 | /// Example 3 [External fns] 79 | /// 80 | #[ensures((^self).len() == self.len() + 1)] 81 | #[ensures(match &(^self).head { 82 | Some(node) => node.elem === &elem, 83 | None => false, 84 | })] 85 | pub fn push(&mut self, elem: T) { 86 | let result = take(&mut self.head); 87 | let bx = NodeP { elem, next: result }; 88 | let _0 = Box::new(bx); 89 | let new = ::std::option::Option::Some(_0); 90 | self.head = new 91 | } 92 | 93 | /// 94 | /// Example 4 [Reborrowing] 95 | /// 96 | #[ensures(match (&self.head, result, &(^self).head) { 97 | (Some(node), Some(v), Some(fut)) => 98 | node.elem === *v && ^v === fut.elem, 99 | (None, None, None) => true, 100 | _ => false, 101 | })] 102 | pub fn peek_mut(&mut self) -> Option<&mut T> { 103 | match &mut self.head { 104 | ::std::option::Option::None => ::std::option::Option::None, 105 | ::std::option::Option::Some(_0) => ::std::option::Option::Some(&mut _0.elem), 106 | } 107 | } 108 | 109 | #[ensures((^x).elems() >= x.elems())] 110 | pub fn peek_last<'a>(x: &'a mut &mut Self) -> &'a mut Link { 111 | let result = take(&mut x.head); 112 | x.head = result; 113 | &mut x.head 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /ruslic/tests/synth/other/suslik_other/avl.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | enum Avl { 4 | Leaf, 5 | Node { f: T, left: Box>, right: Box> }, 6 | } 7 | 8 | impl Avl { 9 | #[pure] 10 | fn size(&self) -> u16 { 11 | match self { 12 | Avl::Leaf => 0, 13 | Avl::Node { left, right, .. } => 1 + left.size() + right.size(), 14 | } 15 | } 16 | #[pure] 17 | fn height(&self) -> u16 { 18 | match self { 19 | Avl::Leaf => 0, 20 | Avl::Node { left, right, .. } => { 21 | let (lh, rh) = (left.height(), right.height()); 22 | 1 + if lh >= rh { lh } else { rh } 23 | } 24 | } 25 | } 26 | #[pure] 27 | fn is_avl(&self) -> bool { 28 | match self { 29 | Avl::Leaf => true, 30 | Avl::Node { left, right, .. } => { 31 | let (lh, rh) = (left.height(), right.height()); 32 | lh <= rh+1 && rh <= lh+1 && left.is_avl() && right.is_avl() 33 | } 34 | } 35 | } 36 | } 37 | 38 | #[requires(x.is_avl())] 39 | #[ensures(result.size() == x.size())] 40 | #[ensures(result.height() == x.height())] 41 | #[ensures(result.is_avl())] 42 | fn avl_copy(x: &Avl) -> Avl { 43 | match x { 44 | Avl::Leaf => Avl::Leaf, 45 | Avl::Node { f, left, right } => { 46 | let de = *f; 47 | let result_1 = avl_copy(&**left); 48 | let result_2 = avl_copy(&**right); 49 | let right = Box::new(result_2); 50 | let left = Box::new(result_1); 51 | Avl::Node { f: de as i32, left, right } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/a-sll_tutorial/stack.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | pub struct List { 4 | head: Link, 5 | } 6 | 7 | type Link = Option>>; 8 | 9 | struct Node { 10 | elem: T, 11 | next: Link, 12 | } 13 | 14 | #[extern_spec] 15 | #[ensures(matches!(^x, None))] 16 | #[ensures(*x === result)] 17 | fn take(x: &mut Option) -> Option { ruslik!() } 18 | 19 | #[pure] 20 | fn is_some(opt: &Option) -> bool { matches!(opt, Some(_)) } 21 | 22 | impl Node { 23 | #[pure] 24 | #[trusted_ensures(result >= 1)] 25 | pub fn len_gt(&self) -> u16 { 26 | match &self.next { 27 | None => 1, 28 | Some(n) => 1 + n.len_gt(), 29 | } 30 | } 31 | } 32 | 33 | impl List { 34 | #[ensures(result.len() == 0)] 35 | pub fn new() -> Self { 36 | List { head: ::std::option::Option::None } 37 | } 38 | 39 | #[pure] 40 | #[trusted_ensures(result >= 0)] 41 | pub fn len(&self) -> u16 { 42 | match &self.head { 43 | None => 0, 44 | Some(n) => n.len_gt(), 45 | } 46 | } 47 | 48 | // #[ensures((^self).len() == self.len() + 1)] 49 | #[ensures(match &(^self).head { 50 | // Some(node) => node.elem === elem, 51 | Some(node) => node.next === self.head, 52 | None => false, 53 | })] 54 | pub fn push(&mut self, elem: T) { 55 | let result = take(&mut self.head); 56 | let bx = Node { elem, next: result }; 57 | let _0 = Box::new(bx); 58 | let new = ::std::option::Option::Some(_0); 59 | self.head = new 60 | } 61 | 62 | // #[ensures(match (&result, &self.head) { 63 | // (Some(v), Some(node)) => (^self).head === node.next && v === node.elem, 64 | // (None, None) => true, 65 | // _ => false, 66 | // })] 67 | #[ensures(match &self.head { 68 | Some(node) => (^self).head === node.next, 69 | None => true, 70 | })] 71 | pub fn pop(&mut self) -> Option { 72 | let result = take(&mut self.head); 73 | match result { 74 | ::std::option::Option::None => ::std::option::Option::None, 75 | ::std::option::Option::Some(_0) => { 76 | let result = ::std::option::Option::Some(_0.elem); 77 | self.head = _0.next; 78 | result 79 | } 80 | } 81 | } 82 | 83 | // #[ensures(match (result, &self.head) { 84 | // (Some(v), Some(node)) => v === node.elem, 85 | // (None, None) => true, 86 | // _ => false, 87 | // })] 88 | #[ensures(is_some(&self.head) == is_some(&result))] 89 | pub fn peek(&self) -> Option<&T> { 90 | match &self.head { 91 | ::std::option::Option::None => ::std::option::Option::None, 92 | ::std::option::Option::Some(_0) => ::std::option::Option::Some(&_0.elem), 93 | } 94 | } 95 | 96 | 97 | #[ensures(match (&(*self).head, result, &(^self).head) { 98 | (Some(node), Some(v), Some(fut)) => 99 | node.elem === *v && ^v === fut.elem, 100 | (None, None, None) => true, 101 | _ => false, 102 | })] 103 | pub fn peek_mut(&mut self) -> Option<&mut T> { 104 | match &mut self.head { 105 | ::std::option::Option::None => ::std::option::Option::None, 106 | ::std::option::Option::Some(_0) => ::std::option::Option::Some(&mut _0.elem), 107 | } 108 | } 109 | } 110 | 111 | // impl Drop for List { 112 | // fn drop(&mut self) { 113 | // let mut cur_link = self.head.take(); 114 | // while let Some(mut boxed_node) = cur_link { 115 | // cur_link = boxed_node.next.take(); 116 | // } 117 | // } 118 | // } 119 | 120 | pub struct Iter<'a, T> { 121 | next: Option<&'a Node>, 122 | } 123 | 124 | impl<'a, T> Iter<'a, T> { 125 | #[pure] 126 | #[trusted_ensures(result >= 0)] 127 | pub fn len(&self) -> u16 { 128 | match &self.next { 129 | None => 0, 130 | Some(n) => n.len_gt(), 131 | } 132 | } 133 | } 134 | 135 | impl<'a, T> Iterator for Iter<'a, T> { 136 | type Item = &'a T; 137 | #[ensures(match self.len() { 138 | 0 => (^self).len() == 0 && matches!(result, None), 139 | v => (^self).len() == v-1 && matches!(result, Some(_)), 140 | })] 141 | fn next(&mut self) -> Option<&'a T> { 142 | let (new, result) = match &mut self.next { 143 | ::std::option::Option::None => (::std::option::Option::None, ::std::option::Option::None), 144 | ::std::option::Option::Some(_0) => match &_0.next { 145 | ::std::option::Option::None => { 146 | let result = ::std::option::Option::Some(&_0.elem); 147 | (::std::option::Option::None, result) 148 | } 149 | ::std::option::Option::Some(_0_next_de) => { 150 | let result = ::std::option::Option::Some(&_0.elem); 151 | let new = ::std::option::Option::Some(&**_0_next_de); 152 | (new, result) 153 | } 154 | }, 155 | }; 156 | self.next = new; 157 | result 158 | } 159 | } 160 | 161 | // Should be solvable, but very slow: 162 | 163 | pub struct IterMut<'a, T> { 164 | next: Option<&'a mut Node>, 165 | } 166 | 167 | impl<'a, T> IterMut<'a, T> { 168 | #[pure] 169 | #[trusted_ensures(result >= 0)] 170 | pub fn len(&self) -> u16 { 171 | match &self.next { 172 | None => 0, 173 | Some(n) => n.len_gt(), 174 | } 175 | } 176 | } 177 | 178 | impl<'a, T> Iterator for IterMut<'a, T> { 179 | type Item = &'a mut T; 180 | 181 | #[ensures(match (&self.next, result) { 182 | (None, None) => true, 183 | (Some(node), Some(result)) => (^*node).elem === ^result, 184 | _ => false, 185 | })] 186 | // #[ensures(match self.len() { 187 | // 0 => (^self).len() == 0, 188 | // n => (^self).len() == n-1, 189 | // })] 190 | fn next(&mut self) -> Option<&'a mut T> { 191 | let result = take(&mut self.next); 192 | match result { 193 | ::std::option::Option::None => ::std::option::Option::None, 194 | ::std::option::Option::Some(_0) => ::std::option::Option::Some(&mut _0.elem), 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/b-stackoverflow/nested_refs.rs: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/32165917/why-does-linking-lifetimes-matter-only-with-mutable-references 2 | use russol_contracts::*; 3 | 4 | fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) -> private::Token { 5 | private::use_same_ref_ref(reference) 6 | } 7 | 8 | mod private { 9 | use russol_contracts::*; 10 | pub struct Token(()); 11 | 12 | #[extern_spec] 13 | pub fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) -> Token { 14 | Token(()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/b-stackoverflow/reborrow.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[pure] 4 | fn is_some(x: &Option) -> bool { 5 | matches!(x, Some(_)) 6 | } 7 | #[pure] 8 | fn is_ok(x: &Result) -> bool { 9 | matches!(x, Ok(_)) 10 | } 11 | 12 | struct Bar { data: Option> } 13 | impl Bar { 14 | // User code: (https://stackoverflow.com/questions/22282117/how-do-i-borrow-a-reference-to-what-is-inside-an-optiont) 15 | #[ensures(is_some(&self.data) == is_ok(&result))] 16 | fn borrow(&mut self) -> Result<&Box, ()> { 17 | match &mut self.data { 18 | ::std::option::Option::None => ::std::result::Result::Err(()), 19 | ::std::option::Option::Some(_0) => ::std::result::Result::Ok(_0), 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/b-stackoverflow/replace_1.rs: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/52031002/how-do-i-move-out-of-a-struct-field-that-is-an-option 2 | use russol_contracts::*; 3 | 4 | struct SomeStruct { 5 | attrib: T, 6 | next_attrib: Option, 7 | } 8 | 9 | impl SomeStruct { 10 | #[ensures(if let Some(attr) = &self.next_attrib { (^self).attrib === attr } else { true })] 11 | // #[ensures(is_none(&(^self).next_attrib))] 12 | pub fn apply_changes(&mut self) { 13 | let result = replace(&mut self.next_attrib, ::std::option::Option::None); 14 | let new = match result { 15 | ::std::option::Option::None => ::std::option::Option::None, 16 | ::std::option::Option::Some(_0) => { 17 | let result = replace(&mut self.attrib, _0); 18 | ::std::option::Option::Some(result) 19 | } 20 | }; 21 | self.next_attrib = new 22 | } 23 | } 24 | 25 | #[pure] 26 | fn is_none(opt: &Option) -> bool { matches!(opt, None) } 27 | 28 | #[extern_spec] 29 | #[ensures(*dest === result)] 30 | #[ensures(^dest === src)] 31 | fn replace(dest: &mut T, src: T) -> T { std::mem::replace(dest, src) } 32 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/b-stackoverflow/replace_2.rs: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/28258548/cannot-move-out-of-borrowed-content-when-trying-to-transfer-ownership 2 | use russol_contracts::*; 3 | 4 | pub struct LinkedList { 5 | head: Option>, 6 | } 7 | 8 | pub struct LinkedListNode { 9 | next: Option>, 10 | } 11 | 12 | impl LinkedList { 13 | #[ensures(match &(^self).head { 14 | None => false, 15 | Some(lln) => lln.next === self.head 16 | })] 17 | pub fn prepend_value(&mut self) { 18 | let result = replace(&mut self.head, ::std::option::Option::None); 19 | let bx = LinkedListNode { next: result }; 20 | let _0 = Box::new(bx); 21 | let new = ::std::option::Option::Some(_0); 22 | self.head = new 23 | } 24 | } 25 | 26 | #[extern_spec] 27 | #[ensures(*dest === result)] 28 | #[ensures(^dest === src)] 29 | fn replace(dest: &mut T, src: T) -> T { std::mem::replace(dest, src) } 30 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/b-stackoverflow/swap_enum.rs: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/29570781/temporarily-move-out-of-borrowed-content 2 | use russol_contracts::*; 3 | 4 | enum Foo { 5 | Bar(T), 6 | Baz(T), 7 | } 8 | 9 | impl Foo { 10 | // FAILURE: (requires unsafe - std wrapped safe fns don't help!) 11 | // fn switch(&mut self) { 12 | // todo!() 13 | // } 14 | } 15 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/futures/futures.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[params("--solutions=1")] 4 | #[requires(***x == 7_i32)] 5 | #[ensures( ^ ^^x == ^^y)] 6 | #[ensures( * ^^x == *^y)] 7 | #[ensures( ^ *^x == ^z)] 8 | #[ensures( **^x == 11_i32)] 9 | // #[ensures( ^^y == ^z)] // Impossible (with above) because after the call one could `**x = &mut (***x+1)` and suddenly `^^y != ^x` 10 | fn foo<'a, 'b>(x: &mut &'b mut &'a mut i32, y: &'b mut &'a mut i32, z: &'a mut i32) { 11 | *z = 11 as i32; 12 | *y = z; 13 | *x = y 14 | } 15 | 16 | #[ensures( ^^y == ^z)] 17 | fn foo1<'a, 'b>(x: &mut &'b mut &'a mut i32, y: &'b mut &'a mut i32, z: &'a mut i32) { 18 | *y = z 19 | } 20 | 21 | #[ensures(*^x == ^result)] 22 | // #[ensures(^*x == **x)] // Impossible 23 | fn foo2<'a>(x: &'a mut &mut i32) -> &'a mut i32 { 24 | &mut **x 25 | } 26 | 27 | #[requires(**x + *y <= u16::MAX)] 28 | #[ensures(*^x == **x + *y)] 29 | #[ensures(^^x == ^y)] 30 | fn foo3<'a>(x: &mut &'a mut u16, y: &'a mut u16) { 31 | let de = *y; 32 | let de_de = **x; 33 | *y = (de_de + de) as u16; 34 | *x = y 35 | } 36 | 37 | #[requires(**x <= 100_u8)] 38 | #[requires(^result <= 100_u8)] 39 | #[ensures(*^x <= 100_u8)] 40 | #[ensures(*result <= 100_u8)] 41 | fn foo4(x: &mut Box) -> &mut u8 { 42 | &mut **x 43 | } 44 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/futures/reborrow.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | mod private { 4 | use russol_contracts::*; 5 | pub struct Percentage(u16); 6 | 7 | #[extern_spec] 8 | // TODO: this prevents the call atm: 9 | // #[requires(^result <= 100)] 10 | #[ensures(*result == p.0)] 11 | #[ensures(^result == (^p).0)] 12 | #[ensures(if ^result == 10 { is_ten(&^p) } else { true })] 13 | pub fn bar(p: &mut Percentage) -> &mut u16 { 14 | &mut p.0 15 | } 16 | 17 | #[pure] 18 | pub fn is_ten(p: &Percentage) -> bool { 19 | p.0 == 10 20 | } 21 | } 22 | 23 | #[ensures(private::is_ten(&^p))] 24 | fn foo(p: &mut private::Percentage) { 25 | todo!() 26 | } 27 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/clone.rs: -------------------------------------------------------------------------------- 1 | fn clone(x: &T) -> T { 2 | x.clone() 3 | } 4 | struct Foo(T); 5 | impl Foo { 6 | fn clone(&self) -> Self { 7 | let result = self.0.clone(); 8 | Foo(result) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/copy_out.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | struct BorrowAndValue<'a, T> { 4 | borrow: &'a mut T, 5 | value: T, 6 | } 7 | 8 | impl<'a, T> BorrowAndValue<'a, T> { 9 | fn new(borrow: &'a mut T, b2: &mut i32) -> Self where T: Copy { 10 | let de = *borrow; 11 | BorrowAndValue { borrow, value: de } 12 | } 13 | } 14 | 15 | #[derive(Copy, Clone)] 16 | struct Foo(i32); 17 | 18 | fn foo(x: &Foo) -> Foo { 19 | *x 20 | } 21 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/cyclic_then_reborrow.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | enum Node { 4 | Nil, 5 | Cons { f: T, next: Box>, }, 6 | } 7 | 8 | impl Node { 9 | #[pure] 10 | #[trusted_ensures(result >= 0)] 11 | fn len_te(&self) -> usize { 12 | match self { 13 | Node::Nil => 0, 14 | Node::Cons { next, .. } => 1 + next.len_te(), 15 | } 16 | } 17 | #[pure] 18 | fn elems(&self) -> Set { 19 | match self { 20 | Node::Nil => set!{}, 21 | Node::Cons { f, next } => next.elems() + set!{f}, 22 | } 23 | } 24 | } 25 | 26 | #[ensures(result.len_te() == x.len_te())] 27 | #[ensures(if result.len_te() == 0 { result.elems() == set!{} } else { result.elems() == set!{&0} })] 28 | #[ensures(^x === ^result)] 29 | fn zero(x: &mut Node) -> &mut Node { 30 | zero_4(x); 31 | x 32 | } 33 | #[helper] fn zero_4(x: &mut Node) { 34 | match x { 35 | Node::Nil => (), 36 | Node::Cons { f, next } => { 37 | zero_4(&mut **next); 38 | *f = 0 as i32 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/enum.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | enum Option { 4 | Left { left: i16 }, 5 | Right { right: i16 }, 6 | } 7 | 8 | #[requires(match *i { 9 | Option::Left { left } => left < i16::MAX, 10 | Option::Right { right } => right < i16::MAX 11 | })] 12 | #[ensures(match (i, result) { 13 | (Option::Left { left }, Option::Right { right }) => *left+1 == right, 14 | (Option::Right { right }, Option::Left { left }) => left == *right+1, 15 | _ => false, 16 | })] 17 | fn swap(i: &Option) -> Option { 18 | match i { 19 | Option::Left { left } => { 20 | let de = *left; 21 | Option::Right { right: (de + 1) as i16 } 22 | } 23 | Option::Right { right } => { 24 | let de = *right; 25 | Option::Left { left: (de + 1) as i16 } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/find_false.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[requires(^x == 1)] 4 | #[ensures(*x == 999)] // as hard as `false` 5 | fn foo(x: &mut i32) { 6 | *x = 0 as i32; 7 | unreachable!() 8 | } 9 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/integer_construct.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | enum Node { 4 | Nil, 5 | Cons { f: i32, next: Box, }, 6 | } 7 | 8 | impl Node { 9 | #[pure] 10 | #[trusted_ensures(0 <= result)] 11 | fn len_te(&self) -> u16 { 12 | match self { 13 | Node::Nil => 0, 14 | Node::Cons { next, .. } => 1 + next.len_te(), 15 | } 16 | } 17 | } 18 | 19 | #[requires(x.len_te() <= u16::MAX)] 20 | #[ensures((^x).len_te() == x.len_te())] 21 | #[ensures(result == x.len_te())] 22 | fn len(x: &mut Node) -> u16 { 23 | match x { 24 | Node::Nil => 0 as u16, 25 | Node::Cons { next, .. } => { 26 | let result = len(&mut **next); 27 | (result + 1) as u16 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/list.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | use russol_contracts::*; 3 | 4 | struct Tuple<'b, 'c>(&'b mut i32, &'c mut Node); 5 | enum Node { 6 | Nil, 7 | Cons { elem: T, next: Box> } 8 | } 9 | 10 | impl Node { 11 | #[pure] 12 | fn len(&self) -> usize { 13 | match self { 14 | Node::Nil => 0, 15 | Node::Cons { next, .. } => 1 + next.len(), 16 | } 17 | } 18 | #[pure] 19 | fn elems(&self) -> Set { 20 | match self { 21 | Node::Nil => set!{}, 22 | Node::Cons { elem, next } => next.elems() + set!{elem}, 23 | } 24 | } 25 | #[ensures(result.len() == self.len())] 26 | fn modify_elems(&mut self) -> Node<&mut T> { 27 | match self { 28 | Node::Nil => Node::Nil, 29 | Node::Cons { elem, next } => { 30 | let result = next.modify_elems(); 31 | let next = Box::new(result); 32 | Node::Cons { elem, next } 33 | } 34 | } 35 | } 36 | } 37 | 38 | impl Node { 39 | #[pure] 40 | fn elems_eq(&self) -> bool { 41 | match self { 42 | Node::Cons { elem, next: box next@Node::Cons { elem: fnxt, .. }, .. } => 43 | *elem == *fnxt && next.elems_eq(), 44 | _ => true, 45 | } 46 | } 47 | 48 | #[requires(i.len() >= 2)] 49 | #[ensures((^i).len() == 2 + (^result.1).len())] 50 | #[ensures((^i).elems() == match &i { 51 | Node::Cons { elem, .. } => Set::new(&[elem, &^result.0]) + (^result.1).elems(), 52 | _ => set!{}, 53 | })] 54 | fn reborrow_head_and_tail_2<'a: 'b + 'c, 'b, 'c>(i: &'a mut &mut Self) -> Tuple<'b, 'c> { 55 | match &mut **i { 56 | Node::Nil => unreachable!(), 57 | Node::Cons { next, .. } => match &mut **next { 58 | Node::Nil => unreachable!(), 59 | Node::Cons { elem, next } => Tuple(elem, &mut **next), 60 | }, 61 | } 62 | } 63 | 64 | #[requires(self.len() >= 2)] 65 | #[ensures(self.len() - 2 == result.len())] 66 | fn tail2(self) -> Self { 67 | match self { 68 | Node::Nil => unreachable!(), 69 | Node::Cons { next, .. } => match *next { 70 | Node::Nil => unreachable!(), 71 | Node::Cons { next, .. } => *next, 72 | }, 73 | } 74 | } 75 | } 76 | 77 | struct List { 78 | head: Node, 79 | } 80 | impl List { 81 | #[ensures((^self).head.len() == (*self).head.len() + tl.head.len())] 82 | #[ensures((^self).head.elems() == (*self).head.elems() + tl.head.elems())] 83 | fn append(&mut self, tl: Self) { 84 | Self::append_8(&mut self.head, tl.head) 85 | } 86 | #[helper] fn append_8(head_self: &mut Node, head_tl: Node) { 87 | match head_self { 88 | Node::Nil => *head_self = head_tl, 89 | Node::Cons { next, .. } => Self::append_8(&mut **next, head_tl), 90 | } 91 | } 92 | 93 | #[ensures((*self).head.len() == (^self).head.len())] 94 | #[ensures((^self).head.elems_eq() == true)] 95 | fn lstset(&mut self) { 96 | let new = Self::lstset_7(&mut self.head); 97 | self.head = new 98 | } 99 | #[helper] fn lstset_7(head: &mut Node) -> Node { 100 | match head { 101 | Node::Nil => Node::Nil, 102 | Node::Cons { elem, next } => { 103 | let de = *elem; 104 | let new = Self::lstset_7(&mut **next); 105 | match new { 106 | Node::Nil => { 107 | let next = Box::new(Node::Nil); 108 | Node::Cons { elem: de as i32, next } 109 | } 110 | Node::Cons { elem, next } => { 111 | let bx = Node::Cons { elem: elem as i32, next }; 112 | let next = Box::new(bx); 113 | Node::Cons { elem: elem as i32, next } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | #[ensures(self.head.len() == result.head.len())] 121 | #[ensures(self.head.elems() == result.head.elems())] 122 | fn duplicate(&self) -> List { 123 | Self::new_list_6(&self.head) 124 | } 125 | #[helper] fn new_list_6(head: &Node) -> List { 126 | match head { 127 | Node::Nil => List { head: Node::Nil }, 128 | Node::Cons { elem, next } => { 129 | let de = *elem; 130 | let result = Self::new_list_6(&**next); 131 | let next = Box::new(result.head); 132 | let head = Node::Cons { elem: de as i32, next }; 133 | List { head } 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/lstset_awkward.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | struct Node1 { 4 | elem: i32, 5 | next: Option>, 6 | } 7 | pub struct List { head: Option>, } 8 | 9 | impl Node1 { 10 | #[pure] 11 | fn eq(&self, val: i32) -> bool { 12 | self.elem == val && 13 | match &self.next { 14 | None => true, 15 | Some(next) => next.eq(val), 16 | } 17 | } 18 | #[pure] 19 | fn len(&self) -> i32 { 20 | 1 + 21 | match &self.next { 22 | None => 0, 23 | Some(next) => next.len(), 24 | } 25 | } 26 | #[pure] 27 | fn sum(&self) -> i32 { 28 | self.elem + 29 | match &self.next { 30 | None => 0, 31 | Some(next) => next.sum(), 32 | } 33 | } 34 | } 35 | 36 | #[ensures(match (&list.head, &(^list).head) { 37 | (None, None) => true, 38 | (Some(list), Some(fut)) => list.len() == fut.len(), 39 | _ => false, 40 | })] 41 | #[ensures(match &(^list).head { 42 | None => true, 43 | Some(list) => list.sum() == list.len() * val 44 | })] 45 | pub fn listset(list: &mut List, val: i32) { 46 | listset_6(val, &mut list.head) 47 | } 48 | #[helper] fn listset_6(val: i32, head: &mut std::option::Option>) { 49 | match head { 50 | ::std::option::Option::None => (), 51 | ::std::option::Option::Some(_0) => { 52 | listset_6(val, &mut _0.next); 53 | _0.elem = val as i32 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/rebrrw_choice.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | // TODO: requires some simplification of `^left` in suslik after "AddToPost" 4 | #[ensures( 5 | if cond { 6 | left === result && *right == ^right 7 | } else { 8 | right === result && *left == ^left 9 | } 10 | )] 11 | fn rbrrw<'a>(left: &'a mut i32, right: &'a mut i32, cond: bool) -> &'a mut i32 { 12 | if cond { left } else { right } 13 | } 14 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/test.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | 3 | use russol_contracts::*; 4 | 5 | // struct FullA { 6 | // a: i32, 7 | // b: Common, 8 | // c: Common, 9 | // } 10 | 11 | struct End { x: i32, y: Common, } 12 | struct Common { f3: u8, f4: i32 } 13 | struct Start { z: Enum } 14 | enum Enum { 15 | V1 { f5: Common }, 16 | V2 { f6: i32, f7: Common }, 17 | } 18 | 19 | // #[requires(matches!(&x.z, Enum::V2 { .. }))] 20 | 21 | // #[ensures(match x.z { 22 | // Enum::V1 { .. } => false, 23 | // Enum::V2 { f6, .. } => result.x == f6 - i, 24 | // })] 25 | // fn rearrange(x: Start, i: i32) -> End { 26 | // ruslik!() 27 | // } 28 | 29 | 30 | #[params("--solutions=1")] 31 | #[requires(matches!(x.z, Enum::V2 { f6, .. } if f6 >= i.f4 && i.f4 >= 0))] 32 | #[ensures(match (&x).z { 33 | Enum::V1 { f5: ref f5@Common { f3, .. } } => f5.f3 == f3, 34 | // Enum::V1 { .. } => false, 35 | Enum::V2 { f6, .. } => result.x == f6 - i.f4, 36 | })] 37 | // #[ensures(^i == 10)] 38 | 39 | // #[requires(i > 10 && i < 10)] 40 | // #[ensures(result.x + 2 >= x.a + (-1))] 41 | fn swap(i: &mut Common, x: Start) -> End { 42 | let de = i.f4; 43 | match x.z { 44 | Enum::V1 { .. } => unreachable!(), 45 | Enum::V2 { f6, f7 } => End { x: (f6 - de) as i32, y: f7 }, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/tree.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | 4 | enum Tree { 5 | Leaf, 6 | Node { f: i32, left: Box, right: Box, }, 7 | } 8 | 9 | impl Tree { 10 | #[pure] 11 | fn size(&self) -> usize { 12 | match self { 13 | Tree::Leaf => 0, 14 | Tree::Node { left, right, .. } => 1 + left.size() + right.size(), 15 | } 16 | } 17 | 18 | #[ensures(result.size() == self.size())] 19 | fn duplicate(&self) -> Self { 20 | match self { 21 | Tree::Leaf => Tree::Leaf, 22 | Tree::Node { f, left, right } => { 23 | let de = *f; 24 | let result_1 = left.duplicate(); 25 | let result_2 = right.duplicate(); 26 | let right = Box::new(result_2); 27 | let left = Box::new(result_1); 28 | Tree::Node { f: de as i32, left, right } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/tree_bst.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | 3 | use russol_contracts::*; 4 | 5 | enum Tree { 6 | Leaf, 7 | Node { f: T, left: Box>, right: Box>, }, 8 | } 9 | 10 | impl Tree { 11 | #[pure] 12 | fn size(&self) -> usize { 13 | match self { 14 | Tree::Leaf => 0, 15 | Tree::Node { left, right, .. } => 1 + left.size() + right.size(), 16 | } 17 | } 18 | #[pure] 19 | fn elems(&self) -> Set { 20 | match self { 21 | Tree::Leaf => set!{}, 22 | Tree::Node { f, left, right } => left.elems() + right.elems() + set!{f}, 23 | } 24 | } 25 | } 26 | impl Tree { 27 | #[pure] 28 | fn ordered(&self) -> bool { 29 | match self { 30 | Tree::Leaf => true, 31 | Tree::Node { f, left, right } => { 32 | let lord = if let box Tree::Node { f: f_l, .. } = left { *f_l <= *f } else { true }; 33 | let rord = if let box Tree::Node { f: f_r, .. } = right { *f <= *f_r } else { true }; 34 | lord && rord && left.ordered() && right.ordered() 35 | }, 36 | } 37 | } 38 | 39 | #[ensures(match (&self, &^self) { 40 | (Tree::Leaf, _) => (^self).elems() == set!{&v}, 41 | (Tree::Node { f, left, right }, 42 | Tree::Node { left: l, right: r, .. }) => { 43 | if v < *f { 44 | l.elems() == set!{&v} + left.elems() 45 | } else if *f < v { 46 | r.elems() == set!{&v} + right.elems() 47 | } else { true } 48 | } 49 | _ => false 50 | })] 51 | #[ensures((^self).elems() == set!{&v} + self.elems())] 52 | fn insert(&mut self, v: i32) { 53 | match self { 54 | Tree::Leaf => { 55 | let right = Box::new(Tree::Leaf); 56 | let left = Box::new(Tree::Leaf); 57 | let new = Tree::Node { f: v as i32, left, right }; 58 | *self = new 59 | } 60 | Tree::Node { f, left, right } => { 61 | let de = *f; 62 | if v < de { left.insert(v) } else if de < v { right.insert(v) } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/tree_to_list.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | enum Node { 4 | Nil, 5 | Cons { elem: T, next: Box> }, 6 | } 7 | 8 | impl Node { 9 | #[pure] 10 | fn elems(&self) -> Set { 11 | match self { 12 | Node::Nil => set!{}, 13 | Node::Cons { elem, next } => next.elems() + set!{elem}, 14 | } 15 | } 16 | } 17 | 18 | enum Tree { 19 | Leaf, 20 | Node { f: T, left: Box>, right: Box>, }, 21 | } 22 | 23 | impl Tree { 24 | #[pure] 25 | fn elems(&self) -> Set { 26 | match self { 27 | Tree::Leaf => set!{}, 28 | Tree::Node { f, left, right } => left.elems() + right.elems() + set!{f}, 29 | } 30 | } 31 | 32 | #[ensures(result.elems() == self.elems())] 33 | #[params("--closeWhileAbduce=false")] 34 | #[params("--memo=false")] 35 | fn to_list(self) -> Node { 36 | match self { 37 | Tree::Leaf => Node::Nil, 38 | Tree::Node { f, left, right } => { 39 | let result = left.to_list(); 40 | Self::to_list_12(f, *right, result) 41 | } 42 | } 43 | } 44 | #[helper] fn to_list_12(f: T, bx: Tree, result: Node) -> Node { 45 | match result { 46 | Node::Nil => { 47 | let result = bx.to_list(); 48 | let next = Box::new(result); 49 | Node::Cons { elem: f, next } 50 | } 51 | Node::Cons { elem, next } => { 52 | let result = Self::to_list_12(elem, bx, *next); 53 | let next = Box::new(result); 54 | Node::Cons { elem: f, next } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/general/zeroing.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | struct List { 4 | head: Node, 5 | } 6 | enum Node { 7 | Nil, 8 | Cons { f: i32, next: Box, }, 9 | } 10 | 11 | impl Node { 12 | #[pure] 13 | fn len(&self) -> usize { 14 | match self { 15 | Node::Nil => 0, 16 | Node::Cons { next, .. } => 1 + next.len(), 17 | } 18 | } 19 | #[pure] 20 | fn sum(&self) -> i32 { 21 | match self { 22 | Node::Nil => 0, 23 | Node::Cons { f, next } => *f + next.sum(), 24 | } 25 | } 26 | } 27 | 28 | #[ensures((^x).head.len() == x.head.len())] 29 | #[ensures((^x).head.sum() == 0)] 30 | fn zero(x: &mut List) { 31 | zero_6(&mut x.head) 32 | } 33 | #[helper] fn zero_6(head: &mut Node) { 34 | match head { 35 | Node::Nil => (), 36 | Node::Cons { f, next } => { 37 | zero_6(&mut **next); 38 | *f = 0 as i32 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/list_ex/list_paper.creusot: -------------------------------------------------------------------------------- 1 | extern crate creusot_contracts; 2 | use creusot_contracts::*; 3 | 4 | enum Node { 5 | Nil, 6 | Cons { elem: T, next: Box> }, 7 | } 8 | 9 | #[trusted] 10 | #[ensures(*dest == result)] 11 | #[ensures(^dest == src)] 12 | fn replace(dest: &mut T, src: T) -> T { std::mem::replace(dest, src) } 13 | 14 | impl Node { 15 | #[logic] 16 | fn len(&self) -> Int { 17 | match self { 18 | Node::Nil => 0, 19 | Node::Cons { next, .. } => 1 + next.len(), 20 | } 21 | } 22 | 23 | pub fn singleton(elem: T) -> Self { 24 | let next = Box::new(Node::Nil); 25 | Node::Cons { elem, next } 26 | } 27 | 28 | #[requires(self.len() > 0)] 29 | pub fn peek(&self) -> &T { 30 | match self { 31 | Node::Nil => unreachable!(), 32 | Node::Cons { elem, .. } => elem, 33 | } 34 | } 35 | 36 | #[ensures((^self).len() == self.len() + 1)] 37 | pub fn push_len(&mut self, elem: T) { 38 | match self { 39 | Node::Nil => { 40 | let next = Box::new(Node::Nil); 41 | let new = Node::Cons { elem, next }; 42 | *self = new 43 | } 44 | Node::Cons { next, .. } => next.push_len(elem), 45 | } 46 | } 47 | 48 | #[ensures(match ^self { 49 | Node::Cons { ref next, .. } => **next == *self, 50 | Node::Nil => false, 51 | })] 52 | pub fn push(&mut self, elem: T) { 53 | let result = replace(self, Node::Nil); 54 | let next = Box::new(result); 55 | let new = Node::Cons { elem, next }; 56 | *self = new 57 | } 58 | 59 | #[ensures(self.len() > 0 ==> 60 | (^self).len() == self.len()-1 && match result { Some(_) => true, _ => false } 61 | )] 62 | pub fn pop(&mut self) -> Option { 63 | let result = replace(self, Node::Nil); 64 | match result { 65 | Node::Nil => ::std::option::Option::None, 66 | Node::Cons { elem, next } => { 67 | *self = *next; 68 | ::std::option::Option::Some(elem) 69 | } 70 | } 71 | } 72 | 73 | #[ensures((^self).len() == self.len() + (^result).len())] 74 | pub fn peek_last(&mut self) -> &mut Self { 75 | match self { 76 | Node::Nil => self, 77 | Node::Cons { next, .. } => next.peek_last(), 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/list_ex/list_paper.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | enum Node { 4 | Nil, 5 | Cons { elem: T, next: Box> }, 6 | } 7 | 8 | #[extern_spec] 9 | #[ensures(*dest === result)] 10 | #[ensures(^dest === src)] 11 | fn replace(dest: &mut T, src: T) -> T { std::mem::replace(dest, src) } 12 | 13 | #[pure] 14 | fn is_some(o: &Option) -> bool { matches!(o, Some(_)) } 15 | 16 | impl Node { 17 | #[pure] 18 | fn len(&self) -> u16 { 19 | match self { 20 | Node::Nil => 0, 21 | Node::Cons { next, .. } => 1 + next.len(), 22 | } 23 | } 24 | 25 | pub fn singleton(elem: T) -> Self { 26 | todo!() 27 | // let next = Box::new(Node::Nil); 28 | // Node::Cons { elem, next } 29 | } 30 | 31 | #[requires(self.len() > 0)] 32 | pub fn peek(&self) -> &T { 33 | match self { 34 | Node::Nil => unreachable!(), 35 | Node::Cons { elem, .. } => elem, 36 | } 37 | } 38 | 39 | #[ensures((^self).len() == self.len() + 1)] 40 | pub fn push_len(&mut self, elem: T) { 41 | match self { 42 | Node::Nil => { 43 | let next = Box::new(Node::Nil); 44 | let new = Node::Cons { elem, next }; 45 | *self = new 46 | } 47 | Node::Cons { next, .. } => next.push_len(elem), 48 | } 49 | } 50 | 51 | #[ensures(match ^self { 52 | Node::Cons { ref next, .. } => **next === *self, 53 | Node::Nil => false, 54 | })] 55 | pub fn push(&mut self, elem: T) { 56 | let result = replace(self, Node::Nil); 57 | let next = Box::new(result); 58 | let new = Node::Cons { elem, next }; 59 | *self = new 60 | } 61 | 62 | #[ensures(self.len() > 0 ==> 63 | (^self).len() == self.len()-1 && is_some(&result) 64 | )] 65 | pub fn pop(&mut self) -> Option { 66 | let result = replace(self, Node::Nil); 67 | match result { 68 | Node::Nil => ::std::option::Option::None, 69 | Node::Cons { elem, next } => { 70 | *self = *next; 71 | ::std::option::Option::Some(elem) 72 | } 73 | } 74 | } 75 | 76 | #[ensures((^self).len() == self.len() + (^result).len())] 77 | pub fn peek_last(&mut self) -> &mut Self { 78 | match self { 79 | Node::Nil => self, 80 | Node::Cons { next, .. } => next.peek_last(), 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/rust/c-custom/list_ex/list_paper_alt.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | enum Node1 { 4 | Nil, 5 | Cons { node: Box<(T, Node1)> }, 6 | } 7 | 8 | #[extern_spec] 9 | #[ensures(*dest === result)] 10 | #[ensures(^dest === src)] 11 | fn replace(dest: &mut T, src: T) -> T { std::mem::replace(dest, src) } 12 | 13 | #[pure] 14 | fn is_some(o: &Option) -> bool { matches!(o, Some(_)) } 15 | 16 | impl Node1 { 17 | #[pure] 18 | fn len(&self) -> u16 { 19 | match self { 20 | Node1::Nil => 0, 21 | Node1::Cons { node } => 1 + node.1.len(), 22 | } 23 | } 24 | 25 | pub fn singleton(elem: T) -> Self { 26 | let bx = (elem, Node1::Nil); 27 | let node = Box::new(bx); 28 | Node1::Cons { node } 29 | } 30 | 31 | #[requires(self.len() > 0)] 32 | pub fn peek(&self) -> &T { 33 | match self { 34 | Node1::Nil => unreachable!(), 35 | Node1::Cons { node } => &node.0, 36 | } 37 | } 38 | 39 | #[ensures((^self).len() == self.len() + 1)] 40 | pub fn push_len(&mut self, elem: T) { 41 | match self { 42 | Node1::Nil => { 43 | let bx = (elem, Node1::Nil); 44 | let node = Box::new(bx); 45 | let new = Node1::Cons { node }; 46 | *self = new 47 | } 48 | Node1::Cons { node } => node.1.push_len(elem), 49 | } 50 | } 51 | 52 | #[ensures(match ^self { 53 | Node1::Cons { ref node } => node.1 === *self, 54 | Node1::Nil => false, 55 | })] 56 | pub fn push(&mut self, elem: T) { 57 | let result = replace(self, Node1::Nil); 58 | let bx = (elem, result); 59 | let node = Box::new(bx); 60 | let new = Node1::Cons { node }; 61 | *self = new 62 | } 63 | 64 | #[ensures(self.len() > 0 ==> 65 | (^self).len() == self.len()-1 && is_some(&result) 66 | )] 67 | pub fn pop(&mut self) -> Option { 68 | let result = replace(self, Node1::Nil); 69 | match result { 70 | Node1::Nil => ::std::option::Option::None, 71 | Node1::Cons { node } => { 72 | *self = node.1; 73 | ::std::option::Option::Some(node.0) 74 | } 75 | } 76 | } 77 | 78 | #[ensures((^self).len() == self.len() + (^result).len())] 79 | pub fn peek_last(&mut self) -> &mut Self { 80 | match self { 81 | Node1::Nil => self, 82 | Node1::Cons { node } => node.1.peek_last(), 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/suslik/a-integers/integers.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[ensures(^x == *y)] 4 | #[ensures(^y == *x)] 5 | fn swap(x: &mut i32, y: &mut i32) { 6 | let de_y = *y; 7 | let de_x = *x; 8 | *x = de_y as i32; 9 | *y = de_x as i32 10 | } 11 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/suslik/b-singly_linked_list/sll.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | use russol_contracts::*; 3 | 4 | enum Node { 5 | Nil, 6 | Cons { elem: T, next: Box> }, 7 | } 8 | 9 | impl Node { 10 | #[pure] 11 | fn len(&self) -> u16 { 12 | match self { 13 | Node::Nil => 0, 14 | Node::Cons { next, .. } => 1 + next.len(), 15 | } 16 | } 17 | #[pure] 18 | fn elems(&self) -> Set { 19 | match self { 20 | Node::Nil => set![], 21 | Node::Cons { elem, next } => next.elems() + set!{ elem }, 22 | } 23 | } 24 | // #[pure] 25 | // fn count(&self, v: i32) -> u16 { 26 | // match self { 27 | // Node::Nil => 0, 28 | // Node::Cons { f, next } => next.count(v) + if *f == v { 1 } else { 0 }, 29 | // } 30 | // } 31 | // #[pure] 32 | // fn is_upper_bound(&self, v: i32) -> bool { 33 | // match self { 34 | // Node::Nil => true, 35 | // Node::Cons { f, next } => next.is_upper_bound(v) && v >= *f, 36 | // } 37 | // } 38 | // #[pure] 39 | // fn count_unique(&self) -> u16 { 40 | // match self { 41 | // Node::Nil => 0, 42 | // Node::Cons { f, next } => next.count_unique() + if next.count(*f) == 0 { 1 } else { 0 }, 43 | // } 44 | // } 45 | 46 | #[ensures(result.len() == self.len() + x2.len())] 47 | fn sll_append_copy(&self, x2: &Self) -> Self where T: Copy { 48 | match x2 { 49 | Node::Nil => self.sll_append_copy_7(), 50 | Node::Cons { elem, next } => { 51 | let de = *elem; 52 | let result = self.sll_append_copy(&**next); 53 | let next = Box::new(result); 54 | Node::Cons { elem: de, next } 55 | } 56 | } 57 | } 58 | #[helper] fn sll_append_copy_7(&self) -> Node { 59 | match self { 60 | Node::Nil => Node::Nil, 61 | Node::Cons { elem, next } => { 62 | let de = *elem; 63 | let result = next.sll_append_copy_7(); 64 | let next = Box::new(result); 65 | Node::Cons { elem: de, next } 66 | } 67 | } 68 | } 69 | 70 | #[ensures((^self).len() == (*self).len() + tl.len())] 71 | fn sll_append(&mut self, tl: Self) { 72 | match self { 73 | Node::Nil => *self = tl, 74 | Node::Cons { next, .. } => next.sll_append(tl), 75 | } 76 | } 77 | 78 | #[ensures(*self === result)] 79 | fn sll_copy(&self) -> Self where T: Copy { 80 | match self { 81 | Node::Nil => Node::Nil, 82 | Node::Cons { elem, next } => { 83 | let de = *elem; 84 | let result = next.sll_copy(); 85 | let next = Box::new(result); 86 | Node::Cons { elem: de, next } 87 | } 88 | } 89 | } 90 | 91 | // #[ensures(result.elems() == self.elems() - set!{ &v })] 92 | // fn sll_delete_all(self, v: T) -> Self where T: Eq { 93 | // ruslik!() 94 | // } 95 | 96 | // fn sll_diff(x: &Node, y: &Node) -> Node { 97 | // ruslik!() 98 | // } 99 | 100 | // sll_free{,2} 101 | 102 | #[ensures((^self).len() == self.len())] 103 | #[ensures((^self).elems() <= set!{ &v })] 104 | fn sll_init(&mut self, v: T) where T: Copy { 105 | let new = self.sll_init_3(v); 106 | *self = new 107 | } 108 | #[helper] fn sll_init_3(&mut self, v: T) -> Node { 109 | match self { 110 | Node::Nil => Node::Nil, 111 | Node::Cons { next, .. } => { 112 | let new = next.sll_init_3(v); 113 | let next = Box::new(new); 114 | Node::Cons { elem: v, next } 115 | } 116 | } 117 | } 118 | 119 | // fn sll_intersect(x: &Node, y: &Node) -> Node { 120 | // ruslik!() 121 | // } 122 | 123 | #[requires(self.len() <= u16::MAX)] 124 | #[ensures(result == self.len())] 125 | fn sll_len(&self) -> u16 { 126 | match self { 127 | Node::Nil => 0 as u16, 128 | Node::Cons { next, .. } => { 129 | let result = next.sll_len(); 130 | (result + 1) as u16 131 | } 132 | } 133 | } 134 | 135 | // #[ensures((^x).is_upper_bound(result))] 136 | // #[ensures((^x).len() == 0 || (^x).count(result) >= 1)] 137 | // fn sll_{max,min}(&self) -> &T where T: Ord { 138 | // ruslik!() 139 | // } 140 | 141 | // TODO: uncommenting this makes it a lot faster, change that: 142 | // #[params("--closeWhileAbduce=false")] 143 | #[ensures((^self).len() == (*self).len() + y.len() + z.len())] 144 | fn sll_append3(&mut self, y: Self, z: Self) { 145 | match self { 146 | Node::Nil => { 147 | let new = Self::sll_append3_7(y, z); 148 | *self = new 149 | } 150 | Node::Cons { next, .. } => next.sll_append3(y, z), 151 | } 152 | } 153 | #[helper] fn sll_append3_7(y: Node, z: Node) -> Node { 154 | match y { 155 | Node::Nil => z, 156 | Node::Cons { elem, next } => { 157 | let new = Self::sll_append3_7(z, *next); 158 | let next = Box::new(new); 159 | Node::Cons { elem, next } 160 | } 161 | } 162 | } 163 | 164 | fn sll_singleton(elem: T) -> Self { 165 | let next = Box::new(Node::Nil); 166 | Node::Cons { elem, next } 167 | } 168 | 169 | // fn sll_union(...) -> Self { 170 | // ruslik!() 171 | // } 172 | 173 | // #[ensures(result.count_unique() == result.len())] 174 | // #[ensures(x.count_unique() == result.len())] 175 | // fn sll_unique(x: Node, ghost: u16) -> Node { 176 | // ruslik!() 177 | // } 178 | } 179 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/suslik/b-singly_linked_list/sll_srtl.creusot: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | extern crate creusot_contracts; 3 | use creusot_contracts::*; 4 | 5 | enum Node { 6 | Nil, 7 | Cons { elem: T, next: Box> }, 8 | } 9 | 10 | impl Node { 11 | #[logic] 12 | // #[trusted_ensures(result >= 0)] 13 | fn len(&self) -> Int { 14 | match self { 15 | Node::Nil => 0, 16 | Node::Cons { next, .. } => 1 + next.len(), 17 | } 18 | } 19 | #[logic] 20 | fn elems(&self) -> logic::Set { 21 | match self { 22 | Node::Nil => logic::Set::EMPTY, 23 | Node::Cons { elem, next } => next.elems().insert(*elem), 24 | } 25 | } 26 | // #[pure] 27 | // fn count(&self, v: i32) -> u16 { 28 | // match self { 29 | // Node::Nil => 0, 30 | // Node::Cons { f, next } => next.count(v) + if *f == v { 1 } else { 0 }, 31 | // } 32 | // } 33 | // #[pure] 34 | // fn is_upper_bound(&self, v: i32) -> bool { 35 | // match self { 36 | // Node::Nil => true, 37 | // Node::Cons { f, next } => next.is_upper_bound(v) && v >= *f, 38 | // } 39 | // } 40 | // #[pure] 41 | // fn count_unique(&self) -> u16 { 42 | // match self { 43 | // Node::Nil => 0, 44 | // Node::Cons { f, next } => next.count_unique() + if next.count(*f) == 0 { 1 } else { 0 }, 45 | // } 46 | // } 47 | 48 | #[ensures(result.len() == self.len() + x2.len())] 49 | fn sll_append_copy(&self, x2: &Self) -> Self where T: Copy { 50 | match x2 { 51 | Node::Nil => self.sll_append_copy_7(), 52 | Node::Cons { elem, next } => { 53 | let de = *elem; 54 | let result = self.sll_append_copy(&**next); 55 | let next = Box::new(result); 56 | Node::Cons { elem: de, next } 57 | } 58 | } 59 | } 60 | #[ensures(result.len() == self.len())] 61 | fn sll_append_copy_7(&self) -> Node where T: Copy { 62 | match self { 63 | Node::Nil => Node::Nil, 64 | Node::Cons { elem, next } => { 65 | let de = *elem; 66 | let result = next.sll_append_copy_7(); 67 | let next = Box::new(result); 68 | Node::Cons { elem: de, next } 69 | } 70 | } 71 | } 72 | 73 | #[ensures((^self).len() == (*self).len() + tl.len())] 74 | fn sll_append(&mut self, tl: Self) { 75 | match self { 76 | Node::Nil => *self = tl, 77 | Node::Cons { next, .. } => next.sll_append(tl), 78 | } 79 | } 80 | 81 | #[ensures(*self == result)] 82 | fn sll_copy(&self) -> Self where T: Copy { 83 | match self { 84 | Node::Nil => Node::Nil, 85 | Node::Cons { elem, next } => { 86 | let de = *elem; 87 | let result = next.sll_copy(); 88 | let next = Box::new(result); 89 | Node::Cons { elem: de, next } 90 | } 91 | } 92 | } 93 | 94 | // #[ensures(result.elems() == self.elems() - set!{ &v })] 95 | // fn sll_delete_all(self, v: T) -> Self where T: Eq { 96 | // ruslik!() 97 | // } 98 | 99 | // fn sll_diff(x: &Node, y: &Node) -> Node { 100 | // ruslik!() 101 | // } 102 | 103 | // sll_free{,2} 104 | 105 | #[ensures((^self).len() == self.len())] 106 | #[ensures((^self).elems().remove(v) == logic::Set::EMPTY)] 107 | fn sll_init(&mut self, v: T) where T: Copy { 108 | let new = self.sll_init_3(v); 109 | *self = new 110 | } 111 | #[ensures((^self) == *self)] 112 | #[ensures(self.len() == result.len())] 113 | #[ensures(result.elems().remove(v) == logic::Set::EMPTY)] 114 | fn sll_init_3(&mut self, v: T) -> Node where T: Copy { 115 | match self { 116 | Node::Nil => Node::Nil, 117 | Node::Cons { next, .. } => { 118 | let new = next.sll_init_3(v); 119 | let next = Box::new(new); 120 | Node::Cons { elem: v, next } 121 | } 122 | } 123 | } 124 | 125 | // fn sll_intersect(x: &Node, y: &Node) -> Node { 126 | // ruslik!() 127 | // } 128 | 129 | #[requires(self.len() <= @u16::MAX)] 130 | #[ensures(@result == self.len())] 131 | fn sll_len(&self) -> u16 { 132 | match self { 133 | Node::Nil => 0 as u16, 134 | Node::Cons { next, .. } => { 135 | let result = next.sll_len(); 136 | (result + 1) as u16 137 | } 138 | } 139 | } 140 | 141 | // #[ensures((^x).is_upper_bound(result))] 142 | // #[ensures((^x).len() == 0 || (^x).count(result) >= 1)] 143 | // fn sll_{max,min}(&self) -> &T where T: Ord { 144 | // ruslik!() 145 | // } 146 | 147 | // TODO: uncommenting this makes it a lot faster, change that: 148 | // #[params("--closeWhileAbduce=false")] 149 | // #[ensures((^self).len() == (*self).len() + y.len() + z.len())] 150 | // fn sll_append3(&mut self, y: Self, z: Self) { 151 | // match self { 152 | // Node::Nil => { 153 | // let new = Self::sll_append3_7(y, z); 154 | // *self = new 155 | // } 156 | // Node::Cons { next, .. } => next.sll_append3(y, z), 157 | // } 158 | // } 159 | // #[helper] fn sll_append3_7(y: Node, z: Node) -> Node { 160 | // match y { 161 | // Node::Nil => z, 162 | // Node::Cons { elem, next } => { 163 | // let new = Self::sll_append3_7(z, *next); 164 | // let next = Box::new(new); 165 | // Node::Cons { elem, next } 166 | // } 167 | // } 168 | // } 169 | 170 | fn sll_singleton(elem: T) -> Self { 171 | let next = Box::new(Node::Nil); 172 | Node::Cons { elem, next } 173 | } 174 | 175 | // fn sll_union(...) -> Self { 176 | // ruslik!() 177 | // } 178 | 179 | // #[ensures(result.count_unique() == result.len())] 180 | // #[ensures(x.count_unique() == result.len())] 181 | // fn sll_unique(x: Node, ghost: u16) -> Node { 182 | // ruslik!() 183 | // } 184 | } 185 | 186 | #[logic] 187 | fn is_sorted_logic(arg: Node) -> bool { 188 | match arg { 189 | Node::Nil => true, 190 | Node::Cons { elem, next } => { 191 | match *next { 192 | Node::Nil => true, 193 | Node::Cons { elem: e, .. } => { 194 | elem <= e && is_sorted_logic(*next) 195 | } 196 | } 197 | } 198 | } 199 | } 200 | 201 | impl Node { 202 | #[trusted] 203 | #[requires(is_sorted_logic(self))] 204 | #[ensures(result.elems() == self.elems().insert(v))] 205 | #[ensures(is_sorted_logic(result))] 206 | fn srtl_insert(self, v: u16) -> Self { todo!() } 207 | 208 | #[ensures(result.elems() == self.elems())] 209 | #[ensures(is_sorted_logic(result))] 210 | fn insertion_sort(&self) -> Self { 211 | match self { 212 | Node::Nil => Node::Nil, 213 | Node::Cons { elem, next } => { 214 | let de = *elem; 215 | let result = next.insertion_sort(); 216 | result.srtl_insert(de) 217 | } 218 | } 219 | } 220 | 221 | // Branch abduction: 222 | // #[requires(self.is_sorted_logic() && other.is_sorted_logic())] 223 | // #[ensures(result.elems() == self.elems() + other.elems())] 224 | // #[ensures(result.is_sorted_logic())] 225 | // fn srtl_merge(self, other: Self) -> Self { 226 | // todo!() 227 | // } 228 | 229 | // Requires intervals: 230 | // #[pure] 231 | // fn is_sorted_rev(&self) -> bool { 232 | // match self { 233 | // Node::Nil => true, 234 | // Node::Cons { next: box next@Node::Nil, .. } => next.is_sorted_rev(), 235 | // Node::Cons { elem, next: box next@Node::Cons { elem: e, .. } } => 236 | // *elem >= *e && next.is_sorted_rev(), 237 | // } 238 | // } 239 | // #[requires(self.is_sorted_logic())] 240 | // #[ensures(result.is_sorted_rev())] 241 | // #[ensures(result.elems() == self.elems())] 242 | // fn srtl_rev(self) -> Self { 243 | // todo!() 244 | // } 245 | } 246 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/suslik/c-sorted_list/srtl.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | use russol_contracts::*; 3 | 4 | enum Node { 5 | Nil, 6 | Cons { elem: T, next: Box> }, 7 | } 8 | 9 | impl Node { 10 | #[pure] 11 | fn len(&self) -> u16 { 12 | match self { 13 | Node::Nil => 0, 14 | Node::Cons { next, .. } => 1 + next.len(), 15 | } 16 | } 17 | #[pure] 18 | fn elems(&self) -> Set { 19 | match self { 20 | Node::Nil => set![], 21 | Node::Cons { elem, next } => next.elems() + set!{ elem }, 22 | } 23 | } 24 | } 25 | 26 | impl Node { 27 | #[pure] 28 | fn is_sorted(&self) -> bool { 29 | match self { 30 | Node::Nil | Node::Cons { next: box Node::Nil, .. } => true, 31 | Node::Cons { elem, next: box next@Node::Cons { elem: e, .. } } => 32 | *elem <= *e && next.is_sorted(), 33 | } 34 | } 35 | 36 | #[extern_spec] 37 | #[requires(self.is_sorted())] 38 | #[ensures(result.is_sorted())] 39 | #[ensures(result.elems() == self.elems() + set!{ &v })] 40 | fn srtl_insert(self, v: u16) -> Self { todo!() } 41 | 42 | #[ensures(result.elems() == self.elems())] 43 | #[ensures(result.is_sorted())] 44 | fn insertion_sort(&self) -> Self { 45 | match self { 46 | Node::Nil => Node::Nil, 47 | Node::Cons { elem, next } => { 48 | let de = *elem; 49 | let result = next.insertion_sort(); 50 | result.srtl_insert(de) 51 | } 52 | } 53 | } 54 | 55 | // Branch abduction: 56 | // #[requires(self.is_sorted() && other.is_sorted())] 57 | // #[ensures(result.elems() == self.elems() + other.elems())] 58 | // #[ensures(result.is_sorted())] 59 | // fn srtl_merge(self, other: Self) -> Self { 60 | // todo!() 61 | // } 62 | 63 | // Doesn't work properly since it would require `interval`s 64 | #[requires(if let Node::Cons { elem, .. } = self { v <= elem } else { true })] 65 | #[ensures(result.len() == self.len() + 1)] 66 | fn srtl_prepend(self, v: u16) -> Self { 67 | let next = Box::new(self); 68 | Node::Cons { elem: v as u16, next } 69 | } 70 | 71 | // Requires intervals: 72 | // #[pure] 73 | // fn is_sorted_rev(&self) -> bool { 74 | // match self { 75 | // Node::Nil => true, 76 | // Node::Cons { next: box next@Node::Nil, .. } => next.is_sorted_rev(), 77 | // Node::Cons { elem, next: box next@Node::Cons { elem: e, .. } } => 78 | // *elem >= *e && next.is_sorted_rev(), 79 | // } 80 | // } 81 | // #[requires(self.is_sorted())] 82 | // #[ensures(result.is_sorted_rev())] 83 | // #[ensures(result.elems() == self.elems())] 84 | // fn srtl_rev(self) -> Self { 85 | // todo!() 86 | // } 87 | } 88 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/suslik/d-list_of_lists/multi-list.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | use russol_contracts::*; 3 | 4 | enum List { 5 | Nil, 6 | Cons(Box<(T, List)>), 7 | } 8 | 9 | impl List { 10 | #[pure] 11 | fn elems(&self) -> Set { 12 | match self { 13 | List::Nil => set!{}, 14 | List::Cons(box (hd, tl)) => tl.elems() + set!{ hd }, 15 | } 16 | } 17 | #[pure] 18 | #[trusted_ensures(result >= 0 && result <= u16::MAX)] 19 | fn len(&self) -> u16 { 20 | match self { 21 | List::Nil => 0, 22 | List::Cons(box (_, tl)) => 1 + tl.len(), 23 | } 24 | } 25 | } 26 | 27 | // multilist 28 | impl List> { 29 | #[pure] 30 | fn elems_list(&self) -> Set { 31 | match self { 32 | List::Nil => set!{}, 33 | List::Cons(box (hd, tl)) => hd.elems() + tl.elems_list(), 34 | } 35 | } 36 | #[pure] 37 | #[trusted_ensures(result >= 0 && result <= u16::MAX)] 38 | fn mlen(&self) -> u16 { 39 | match self { 40 | List::Nil => 0, 41 | List::Cons(box (hd, tl)) => hd.len() + tl.mlen(), 42 | } 43 | } 44 | 45 | #[ensures(result.elems() == self.elems_list())] 46 | #[params("--closeWhileAbduce=false")] 47 | fn flatten(self) -> List { 48 | match self { 49 | List::Nil => List::Nil, 50 | List::Cons(_0) => Self::flatten_7(_0.0, _0.1), 51 | } 52 | } 53 | #[helper] fn flatten_7(_0: List, _1: List>) -> List { 54 | match _0 { 55 | List::Nil => _1.flatten(), 56 | List::Cons(_0) => { 57 | let result = Self::flatten_7(_0.1, _1); 58 | let bx = (_0.0, result); 59 | let _0 = Box::new(bx); 60 | List::Cons(_0) 61 | } 62 | } 63 | } 64 | 65 | // FAILURE: 66 | // Doesn't work due to having `hd.tail.len() + tl.mlen()` in a variable, 67 | // but not being able to construct `1 + hd.tail.len() + tl.mlen()` 68 | // #[requires(self.mlen() <= u16::MAX)] 69 | // #[ensures(result == self.mlen())] 70 | // fn multilist_length(&self) -> u16 { 71 | // match self { 72 | // List::Nil => 0, 73 | // List::Cons(box (hd, tl)) => hd.len() + tl.mlen(), 74 | // } 75 | // } 76 | } 77 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/suslik/e-binary_tree/tree.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | enum Node { 4 | Nil, 5 | Cons { elem: T, next: Box> }, 6 | } 7 | 8 | impl Node { 9 | #[pure] 10 | fn elems(&self) -> Set { 11 | match self { 12 | Node::Nil => set!{}, 13 | Node::Cons { elem, next } => next.elems() + set!{ elem }, 14 | } 15 | } 16 | 17 | #[extern_spec] 18 | #[ensures(result.elems() == self.elems() + other.elems())] 19 | fn append(self, other: Self, token: Token) -> Self { 20 | todo!() 21 | } 22 | } 23 | 24 | enum Tree { 25 | Leaf, 26 | Node { elem: T, left: Box>, right: Box>, }, 27 | } 28 | 29 | impl Tree { 30 | #[pure] 31 | fn elems(&self) -> Set { 32 | match self { 33 | Tree::Leaf => set!{}, 34 | Tree::Node { elem, left, right } => left.elems() + right.elems() + set!{ elem }, 35 | } 36 | } 37 | 38 | #[ensures(result.elems() == self.elems())] 39 | fn tree_copy(&self) -> Self where T: Copy { 40 | match self { 41 | Tree::Leaf => Tree::Leaf, 42 | Tree::Node { elem, left, right } => { 43 | let de = *elem; 44 | let result_1 = left.tree_copy(); 45 | let result_2 = right.tree_copy(); 46 | let right = Box::new(result_2); 47 | let left = Box::new(result_1); 48 | Tree::Node { elem: de, left, right } 49 | } 50 | } 51 | } 52 | 53 | #[ensures((^l).elems() == self.elems() + l.elems())] 54 | #[params("--closeWhileAbduce=false")] 55 | fn tree_flatten_acc(self, l: &mut Node) { 56 | match l { 57 | Node::Nil => { 58 | let new = self.tree_flatten_acc_7(); 59 | *l = new 60 | } 61 | Node::Cons { next, .. } => self.tree_flatten_acc(&mut **next), // <- TODO: investigate why the args here get swapped 62 | } 63 | } 64 | #[helper] fn tree_flatten_acc_7(self) -> Node { 65 | match self { 66 | Tree::Leaf => Node::Nil, 67 | Tree::Node { elem, left, right } => { 68 | let new = left.tree_flatten_acc_7(); 69 | Self::tree_flatten_acc_18(elem, *right, new) 70 | } 71 | } 72 | } 73 | #[helper] fn tree_flatten_acc_18(elem: T, bx: Tree, new: Node) -> Node { 74 | match new { 75 | Node::Nil => { 76 | let new = bx.tree_flatten_acc_7(); 77 | let next = Box::new(new); 78 | Node::Cons { elem, next } 79 | } 80 | Node::Cons { elem: elem_new, next } => { 81 | let new = Self::tree_flatten_acc_18(elem_new, bx, *next); 82 | let next = Box::new(new); 83 | Node::Cons { elem, next } 84 | } 85 | } 86 | } 87 | 88 | // fn tree_dll{,_linear} 89 | 90 | #[ensures(result.elems() == self.elems())] 91 | #[params("--closeWhileAbduce=false")] 92 | fn tree_flatten_helper(self, token: Token) -> Node { 93 | match self { 94 | Tree::Leaf => Node::Nil, 95 | Tree::Node { elem, left, right } => { 96 | let result_1 = left.tree_flatten_helper(token); 97 | let result_2 = right.tree_flatten_helper(token); 98 | let result = result_1.append(result_2, token); 99 | let next = Box::new(result); 100 | Node::Cons { elem, next } 101 | } 102 | } 103 | } 104 | 105 | #[ensures(result.elems() == self.elems())] 106 | #[params("--closeWhileAbduce=false")] 107 | fn tree_flatten(self) -> Node { 108 | match self { 109 | Tree::Leaf => Node::Nil, 110 | Tree::Node { elem, left, right } => { 111 | let result = left.tree_flatten(); 112 | Self::tree_flatten_12(elem, *right, result) 113 | } 114 | } 115 | } 116 | #[helper] fn tree_flatten_12(elem: T, bx: Tree, result: Node) -> Node { 117 | match result { 118 | Node::Nil => { 119 | let result = bx.tree_flatten(); 120 | let next = Box::new(result); 121 | Node::Cons { elem, next } 122 | } 123 | Node::Cons { elem: elem_result, next } => { 124 | let result = Self::tree_flatten_12(elem_result, bx, *next); 125 | let next = Box::new(result); 126 | Node::Cons { elem, next } 127 | } 128 | } 129 | } 130 | 131 | // fn free{,2} 132 | 133 | #[pure] 134 | #[trusted_ensures(result <= u16::MAX)] 135 | fn size(&self) -> u16 { 136 | match self { 137 | Tree::Leaf => 0, 138 | Tree::Node { left, right, .. } => left.size() + right.size() + 1, 139 | } 140 | } 141 | 142 | #[ensures(result == self.size())] 143 | fn tree_size(&self) -> u16 { 144 | match self { 145 | Tree::Leaf => 0 as u16, 146 | Tree::Node { left, right, .. } => { 147 | let result_1 = left.tree_size(); 148 | let result_2 = right.tree_size(); 149 | (result_1 + result_2 + 1) as u16 150 | } 151 | } 152 | } 153 | } 154 | 155 | #[derive(Copy, Clone)] 156 | struct Token<'a>(&'a Token<'a>); 157 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/suslik/f-rose_tree/rose-tree.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | use russol_contracts::*; 3 | 4 | enum RoseTree { 5 | Nil, 6 | Cons { elem: T, next: List> }, 7 | } 8 | enum List { 9 | Nil, 10 | Cons(Box<(T, List)>), 11 | } 12 | 13 | impl List { 14 | #[pure] 15 | fn elems(&self) -> Set { 16 | match self { 17 | List::Nil => set!{}, 18 | List::Cons(box (hd, tl)) => tl.elems() + set!{ hd }, 19 | } 20 | } 21 | } 22 | impl List> { 23 | #[pure] 24 | fn elems_tree(&self) -> Set { 25 | match self { 26 | List::Nil => set!{}, 27 | List::Cons(box (hd, tl)) => hd.elems() + tl.elems_tree(), 28 | } 29 | } 30 | } 31 | 32 | // rose-tree 33 | impl RoseTree { 34 | #[pure] 35 | fn elems(&self) -> Set { 36 | match self { 37 | RoseTree::Nil => set!{}, 38 | RoseTree::Cons { elem, next } => next.elems_tree() + set!{ elem }, 39 | } 40 | } 41 | 42 | #[ensures(result.elems() == self.elems())] 43 | fn copy(&self) -> Self where T: Copy { 44 | ruslik!() 45 | } 46 | 47 | #[ensures(result.elems() == self.elems())] 48 | #[params("--closeWhileAbduce=false")] 49 | fn flatten(self) -> List { 50 | ruslik!() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/account.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code, non_snake_case)] 2 | use russol_contracts::*; 3 | 4 | struct Account { 5 | bal: u16, 6 | } 7 | 8 | impl Account { 9 | 10 | #[pure] 11 | fn balance(&self) -> u16 { 12 | self.bal 13 | } 14 | 15 | #[requires(self.balance() + amount <= u16::MAX)] 16 | #[ensures((^self).balance() == self.balance() + amount)] 17 | fn deposit(&mut self, amount: u16) { 18 | let de = self.bal; 19 | let new = Account { bal: (de + amount) as u16 }; 20 | *self = new 21 | } 22 | 23 | #[requires(amount <= self.balance())] 24 | #[ensures((^self).balance() == self.balance() - amount)] 25 | fn withdraw(&mut self, amount: u16) { 26 | let de = self.bal; 27 | let new = Account { bal: (de - amount) as u16 }; 28 | *self = new 29 | } 30 | 31 | #[requires(other.balance() + amount <= u16::MAX)] 32 | #[requires(amount <= self.balance())] 33 | #[ensures((^self).balance() == self.balance() - amount)] 34 | #[ensures((^other).balance() == other.balance() + amount)] 35 | fn transfer(&mut self, other: &mut Account, amount: u16) { 36 | let de_bal_other = other.bal; 37 | let de_bal_self = self.bal; 38 | self.bal = (de_bal_self - amount) as u16; 39 | other.bal = (de_bal_other + amount) as u16 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/box-specification.rs: -------------------------------------------------------------------------------- 1 | //! Example: test specification of box dereferentiation 2 | 3 | use russol_contracts::*; 4 | 5 | #[ensures(result == *my_box)] 6 | fn foo(my_box: Box) -> i32 { 7 | *my_box 8 | } 9 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/bst_generics_paper.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | use russol_contracts::*; 3 | 4 | pub enum Tree { 5 | Node(i16, Box, Box), 6 | Empty, 7 | } 8 | 9 | impl Tree { 10 | #[pure] 11 | pub fn elems(&self) -> Set { 12 | if let Tree::Node(value, left, right) = self { 13 | left.elems() + right.elems() + set!{ value } 14 | } else { set!{} } 15 | } 16 | 17 | #[pure] 18 | pub fn rightmost(&self) -> i16 { 19 | match self { 20 | Tree::Empty => i16::MAX, 21 | Tree::Node(value, _, box Tree::Empty) => *value, 22 | Tree::Node(_, _, right) => right.rightmost(), 23 | } 24 | } 25 | 26 | #[pure] 27 | pub fn leftmost(&self) -> i16 { 28 | match self { 29 | Tree::Empty => i16::MIN, 30 | Tree::Node(value, box Tree::Empty, _) => *value, 31 | Tree::Node(_, left, _) => left.leftmost(), 32 | } 33 | } 34 | 35 | #[pure] 36 | pub fn bst_invariant(&self) -> bool { 37 | if let Tree::Node(value, left, right) = self { 38 | (if let Tree::Node(..) = &**left { *value >= left.rightmost() } else { true }) && 39 | (if let Tree::Node(..) = &**right { *value <= right.leftmost() } else { true }) && 40 | left.bst_invariant() && right.bst_invariant() 41 | } else { true } 42 | } 43 | 44 | // Requires branch abduction: 45 | // #[requires(self.bst_invariant())] 46 | // #[ensures((^self).bst_invariant())] 47 | // #[ensures((^self).elems()[&new_value])] 48 | // #[ensures((^self).elems() >= self.elems())] 49 | // pub fn insert(&mut self, new_value: i16) { 50 | // if let Tree::Node(value, left, right) = self { 51 | // match new_value.cmp(value) { 52 | // Equal => (), 53 | // Less => left.insert(new_value), 54 | // Greater => right.insert(new_value), 55 | // } 56 | // } else { 57 | // *self = Tree::Node(new_value, Box::new(Tree::Empty), Box::new(Tree::Empty)) 58 | // } 59 | // } 60 | 61 | #[requires(self.bst_invariant())] 62 | #[ensures((^self).bst_invariant())] 63 | #[requires( 64 | if let Tree::Node(_, left, right) = &*self { 65 | (if let Tree::Node(..) = &**left { ^result >= left.rightmost() } else { true }) && 66 | (if let Tree::Node(..) = &**right { ^result <= right.leftmost() } else { true }) 67 | } else { false } 68 | )] 69 | // Not required: 70 | // #[ensures( 71 | // match (&^self, &*self) { 72 | // (Tree::Node(fv, fl, fr), Tree::Node(ov, ol, or)) => 73 | // *fv == ^result && *ov == *result && fl === ol && fr === or, 74 | // _ => false, 75 | // } 76 | // )] 77 | pub fn get_root_value(&mut self) -> &mut i16 { 78 | match self { 79 | Tree::Node(_0, _, _) => _0, 80 | Tree::Empty => unreachable!(), 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/fields-spec.rs: -------------------------------------------------------------------------------- 1 | //! Example: test encoding of fields in specifications 2 | 3 | use russol_contracts::*; 4 | 5 | struct S { 6 | a: i32, 7 | b: i32 8 | } 9 | 10 | #[requires(x.0 == 123 && x.1 == 42)] 11 | #[ensures(result.0 == 42 && result.1 == 123)] 12 | fn test_tuple_field(x: (i32, i32)) -> (i32, i32) { 13 | (42 as i32, 123 as i32) 14 | } 15 | 16 | #[requires(x.a == 123 && x.b == 42)] 17 | #[ensures(result.a == 42 && result.b == 123)] 18 | fn test_struct_field(x: S) -> S { 19 | S { a: 42 as i32, b: 123 as i32 } 20 | } 21 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/generics-basic-5.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | struct Number { 4 | a: A, 5 | b: B, 6 | c: C, 7 | } 8 | 9 | #[requires(-10_000 < arg.b && arg.b < 10_000)] 10 | #[ensures((^arg).b == arg.b - 1000)] 11 | fn test1(arg: &mut Number) { 12 | let de = arg.b; 13 | arg.b = (de - 1000) as i32 14 | } 15 | 16 | #[requires(-10_000 < arg.b.b && arg.b.b < 10_000)] 17 | #[ensures((^arg).b.b == arg.b.b - 1000)] 18 | fn test2(arg: &mut Number, D>) { 19 | let de = arg.b.b; 20 | arg.b.b = (de - 1000) as i32 21 | } 22 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/generics-basic-6.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | use std::marker::PhantomData; 4 | 5 | struct Foo { 6 | i: i32, 7 | x: BarBaz, 8 | } 9 | 10 | struct BarBaz { 11 | i: i32, 12 | x: PhantomData, 13 | } 14 | 15 | #[ensures(result.i == arg.x.i)] 16 | #[ensures(result.x.i == arg.i)] 17 | fn test1(arg: Foo) -> Foo { 18 | let x = BarBaz { i: arg.i as i32, x: ::std::marker::PhantomData }; 19 | Foo { i: arg.x.i as i32, x } 20 | } 21 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/match-expr-variants.rs: -------------------------------------------------------------------------------- 1 | //! Example: test match expressions 2 | 3 | use russol_contracts::*; 4 | 5 | #[requires(x == -42)] 6 | #[ensures(match result { Some(..) => true, _ => false })] 7 | fn test_match_expr(x: i32) -> Option { 8 | ::std::option::Option::Some(0 as i32) 9 | } 10 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/match-expr.rs: -------------------------------------------------------------------------------- 1 | //! Example: test match expressions 2 | 3 | use russol_contracts::*; 4 | 5 | #[requires(x == 42)] 6 | #[ensures(match result { 84 => true, 123 | 456 => false, _ => false })] 7 | fn test_match_expr(x: u32) -> u32 { 8 | 84 as u32 9 | } 10 | 11 | #[requires(x == -42)] 12 | #[ensures(match result { Some(k) => k == -42, _ => false })] 13 | fn test_match_option_expr(x: i32) -> Option { 14 | ::std::option::Option::Some((-42) as i32) 15 | } 16 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/move.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | struct T { 4 | f: u32, 5 | } 6 | 7 | #[ensures(^x == 4)] 8 | fn test1(x: &mut u32) { 9 | *x = 4 as u32 10 | } 11 | 12 | #[ensures((^x).f == 4)] 13 | fn test2(x: &mut T) { 14 | let new = T { f: 4 as u32 }; 15 | *x = new 16 | } 17 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/ownership2.rs: -------------------------------------------------------------------------------- 1 | #![feature(nll)] 2 | #![feature(box_patterns)] 3 | 4 | use russol_contracts::*; 5 | 6 | struct Point { 7 | x: Box, 8 | y: Box, 9 | } 10 | 11 | #[requires(u16::MAX - *a >= b)] 12 | #[ensures(*result == *a + b)] 13 | fn add(a: Box, b: u16) -> Box { 14 | Box::new((*a + b) as u16) 15 | } 16 | 17 | #[requires(u16::MAX - *p.x >= s)] 18 | #[ensures(*result.x == *p.x + s)] 19 | #[ensures(*result.y == *p.y)] 20 | fn shift_x(p: Point, s: u16) -> Point { 21 | let x = Box::new((*p.x + s) as u16); 22 | Point { x, y: p.y } 23 | } 24 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/shared.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[requires(*x == 5)] 4 | #[ensures(*x == 5)] 5 | pub fn test6(x: &u32) { 6 | () 7 | } 8 | 9 | pub fn test(x: &'static T) -> &'_ T { 10 | x 11 | } 12 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/sign_mix.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | pub fn u32_i64(x: u32) -> i64 { 4 | x as i64 5 | } 6 | 7 | pub fn u32_isize(x: u32) -> isize { 8 | x as isize 9 | } 10 | 11 | #[requires(x < std::i32::MAX as u64)] 12 | pub fn u64_u32(x: u64) -> i32 { 13 | x as i32 14 | } 15 | 16 | #[requires(x < std::i16::MAX as u64)] 17 | pub fn u64_i16(x: u64) -> i16 { 18 | x as i16 19 | } 20 | 21 | #[requires(x < std::i8::MAX as u64)] 22 | pub fn u64_i8(x: u64) -> i8 { 23 | x as i8 24 | } 25 | 26 | #[requires(x < std::i8::MAX as u16)] 27 | pub fn u16_i8(x: u16) -> i8 { 28 | x as i8 29 | } 30 | 31 | // The issue is that Suslik can't parse the value `std::u32::MAX` 32 | // #[requires(x < std::u32::MAX as i64)] 33 | // #[requires(0 <= x)] 34 | // pub fn i64_u32(x: i64) -> u32 { 35 | // x as u32 36 | // } 37 | 38 | #[requires(x < std::u16::MAX as i64)] 39 | #[requires(0 <= x)] 40 | pub fn i64_u16(x: i64) -> u16 { 41 | x as u16 42 | } 43 | 44 | #[requires(x < std::u8::MAX as i64)] 45 | #[requires(0 <= x)] 46 | pub fn i64_u8(x: i64) -> u8 { 47 | x as u8 48 | } 49 | 50 | #[requires(x < std::u8::MAX as i16)] 51 | #[requires(0 <= x)] 52 | pub fn i16_u8(x: i16) -> u8 { 53 | x as u8 54 | } 55 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/signed.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | pub fn i32_i64(x: i32) -> i64 { 4 | x as i64 5 | } 6 | 7 | pub fn i32_isize(x: i32) -> isize { 8 | x as isize 9 | } 10 | 11 | // #[requires(x < std::i32::MAX as i64)] 12 | // #[requires(std::i32::MIN as i64 <= x)] 13 | // pub fn i64_u32(x: i64) -> i32 { 14 | // x as i32 15 | // } 16 | 17 | #[requires(x < std::i16::MAX as i64)] 18 | #[requires(std::i16::MIN as i64 <= x)] 19 | pub fn i64_i16(x: i64) -> i16 { 20 | 0 as i16 21 | } 22 | 23 | #[requires(x < std::i8::MAX as i64)] 24 | #[requires(std::i8::MIN as i64 <= x)] 25 | pub fn i64_i8(x: i64) -> i8 { 26 | 0 as i8 27 | } 28 | 29 | #[requires(x < std::i8::MAX as i16)] 30 | #[requires(std::i8::MIN as i16 <= x)] 31 | pub fn u16_i8(x: i16) -> i8 { 32 | 0 as i8 33 | } 34 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/unsigned.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | pub fn u32_u64(x: u32) -> u64 { 4 | x as u64 5 | } 6 | 7 | pub fn u32_usize(x: u32) -> usize { 8 | x as usize 9 | } 10 | 11 | // #[requires(x < std::u32::MAX as u64)] 12 | // pub fn u64_u32(x: u64) -> u32 { 13 | // x as u32 14 | // } 15 | 16 | #[requires(x < std::u16::MAX as u64)] 17 | pub fn u64_u16(x: u64) -> u16 { 18 | x as u16 19 | } 20 | 21 | #[requires(x < std::u8::MAX as u64)] 22 | pub fn u64_u8(x: u64) -> u8 { 23 | x as u8 24 | } 25 | 26 | #[requires(x < std::u8::MAX as u16)] 27 | pub fn u16_u8(x: u16) -> u8 { 28 | x as u8 29 | } 30 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/a-prusti/wand-identity2.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use russol_contracts::*; 4 | 5 | struct T { 6 | val: i32 7 | } 8 | 9 | #[ensures((^x).val == (^result).val)] 10 | fn identity(x: &mut T) -> &mut T { 11 | x 12 | } 13 | 14 | #[ensures(result.val == v)] // TODO x.val is illegal, but Prusti doesn't report a readable error message. 15 | #[ensures((^x).val == (^result).val)] 16 | fn identity2(x: &mut T, v: i32) -> &mut T { 17 | let new = T { val: v }; 18 | *x = new; 19 | x 20 | } 21 | 22 | #[ensures(*result == v)] 23 | #[ensures((^x).val == (^result))] 24 | fn identity3(x: &mut T, v: i32) -> &mut i32 { 25 | x.val = v as i32; 26 | &mut x.val 27 | } 28 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/b-creusot/inc_some_2_list.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | pub enum List { 4 | Nil, 5 | Cons(u16, Box), 6 | } 7 | use List::*; 8 | 9 | impl List { 10 | #[pure] 11 | // #[trusted_ensures(result >= 0)] 12 | fn sum(&self) -> u16 { 13 | match self { 14 | Cons(a, l) => *a + l.sum(), 15 | Nil => 0, 16 | } 17 | } 18 | 19 | #[requires(self.sum() <= u16::MAX)] 20 | #[ensures(result == self.sum())] 21 | fn sum_x(&self) -> u16 { 22 | match self { 23 | List::Nil => 0 as u16, 24 | List::Cons(_0, _1) => { 25 | let de = *_0; 26 | let result = _1.sum_x(); 27 | (de + result) as u16 28 | } 29 | } 30 | } 31 | 32 | #[requires(self.sum() > 0)] 33 | #[ensures((^self).sum() - self.sum() == 34 | ^result.0 + (^result.1).sum() - *result.0 - (*result.1).sum())] 35 | // We need: `trusted_ensures(result >= 0)` on `sum` for this, 36 | // but trusted_ensures doesn't work well with `^self`. 37 | // #[ensures(*result.0 <= self.sum())] 38 | // #[ensures(result.1.sum() <= self.sum())] 39 | fn take_some_rest(&mut self) -> (&mut u16, &mut List) { 40 | match self { 41 | List::Nil => unreachable!(), 42 | List::Cons(_0, _1) => (_0, &mut **_1), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/b-creusot/swap_borrows.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[ensures(result.0 === x.1 && result.1 === x.0)] 4 | fn swap(x: (T, T)) -> (T, T) { 5 | (x.1, x.0) 6 | } 7 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/b-creusot/take_max.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[ensures(if *ma >= *mb { *mb == ^mb && result === ma } 4 | else { *ma == ^ma && result === mb })] 5 | fn take_max<'a>(ma: &'a mut u16, mb: &'a mut u16) -> &'a mut u16 { 6 | let de_mb = *mb; 7 | let de_ma = *ma; 8 | if de_mb <= de_ma { ma } else { mb } 9 | } 10 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/b-creusot/unnest.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[ensures(*result == **x)] 4 | #[ensures(^result == *^x)] 5 | #[ensures(^*x == ^^x)] 6 | pub fn unnest<'a, 'b: 'a>(x: &'a mut &'b mut i32) -> &'a mut i32 { 7 | &mut **x 8 | } 9 | -------------------------------------------------------------------------------- /ruslic/tests/synth/paper/verifier/b-creusot/wrapping.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[extern_spec] 4 | #[ensures(result == if a + b >= 256 { a + b - 256 } else { a + b })] 5 | pub fn wrapping_add(a: u8, b: u8) -> u8 { 6 | a.wrapping_add(b) 7 | } 8 | 9 | #[ensures(result == a + b || result == a + b - 256)] 10 | pub fn test_u8_wrapping_add(a: u8, b: u8) -> u8 { 11 | let result = wrapping_add(b as u8, a); 12 | if 256 <= b + a { result } else { result } 13 | } 14 | -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/aho-corasick-0.7.20.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/aho-corasick-0.7.20.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/ansi_term-0.12.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/ansi_term-0.12.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/anyhow-1.0.70.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/anyhow-1.0.70.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/arrayvec-0.7.2.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/arrayvec-0.7.2.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/atty-0.2.14.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/atty-0.2.14.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/autocfg-1.1.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/autocfg-1.1.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/base64-0.21.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/base64-0.21.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/bitflags-2.0.2.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/bitflags-2.0.2.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/block-buffer-0.10.4.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/block-buffer-0.10.4.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/byteorder-1.4.3.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/byteorder-1.4.3.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/bytes-1.4.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/bytes-1.4.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/cc-1.0.79.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/cc-1.0.79.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/cfg-if-1.0.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/cfg-if-1.0.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/chrono-0.4.24.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/chrono-0.4.24.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/clap-4.2.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/clap-4.2.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/crossbeam-channel-0.5.7.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/crossbeam-channel-0.5.7.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/crossbeam-epoch-0.9.14.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/crossbeam-epoch-0.9.14.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/crossbeam-utils-0.8.15.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/crossbeam-utils-0.8.15.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/digest-0.10.6.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/digest-0.10.6.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/either-1.8.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/either-1.8.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/env_logger-0.10.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/env_logger-0.10.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/fnv-1.0.7.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/fnv-1.0.7.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/futures-0.3.28.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/futures-0.3.28.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/futures-channel-0.3.28.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/futures-channel-0.3.28.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/futures-core-0.3.28.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/futures-core-0.3.28.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/futures-io-0.3.28.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/futures-io-0.3.28.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/futures-sink-0.3.28.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/futures-sink-0.3.28.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/futures-task-0.3.28.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/futures-task-0.3.28.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/futures-util-0.3.28.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/futures-util-0.3.28.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/generic-array-0.14.7.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/generic-array-0.14.7.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/getrandom-0.2.8.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/getrandom-0.2.8.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/hashbrown-0.13.2.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/hashbrown-0.13.2.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/heck-0.4.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/heck-0.4.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/http-0.2.9.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/http-0.2.9.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/hyper-0.14.25.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/hyper-0.14.25.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/idna-0.3.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/idna-0.3.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/indexmap-1.9.3.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/indexmap-1.9.3.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/itertools-0.10.5.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/itertools-0.10.5.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/itoa-1.0.6.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/itoa-1.0.6.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/lazy_static-1.4.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/lazy_static-1.4.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/libc-0.2.140.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/libc-0.2.140.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/lock_api-0.4.9.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/lock_api-0.4.9.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/log-0.4.17.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/log-0.4.17.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/matches-0.1.10.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/matches-0.1.10.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/memchr-2.5.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/memchr-2.5.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/memoffset-0.8.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/memoffset-0.8.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/miniz_oxide-0.7.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/miniz_oxide-0.7.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/mio-0.8.6.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/mio-0.8.6.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/nom-7.1.3.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/nom-7.1.3.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/num-integer-0.1.45.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/num-integer-0.1.45.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/num-traits-0.2.15.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/num-traits-0.2.15.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/num_cpus-1.15.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/num_cpus-1.15.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/once_cell-1.17.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/once_cell-1.17.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/parking_lot-0.12.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/parking_lot-0.12.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/parking_lot_core-0.9.7.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/parking_lot_core-0.9.7.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/percent-encoding-2.2.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/percent-encoding-2.2.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/pin-project-lite-0.2.9.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/pin-project-lite-0.2.9.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/pin-utils-0.1.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/pin-utils-0.1.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/pkg-config-0.3.26.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/pkg-config-0.3.26.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/ppv-lite86-0.2.17.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/ppv-lite86-0.2.17.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/proc-macro2-1.0.54.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/proc-macro2-1.0.54.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/quote-1.0.26.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/quote-1.0.26.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/rand-0.8.5.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/rand-0.8.5.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/rand_chacha-0.3.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/rand_chacha-0.3.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/rand_core-0.6.4.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/rand_core-0.6.4.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/regex-1.7.3.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/regex-1.7.3.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/regex-syntax-0.6.29.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/regex-syntax-0.6.29.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/rustc_version-0.4.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/rustc_version-0.4.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/ryu-1.0.13.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/ryu-1.0.13.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/scopeguard-1.1.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/scopeguard-1.1.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/semver-1.0.17.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/semver-1.0.17.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/serde-1.0.159.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/serde-1.0.159.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/serde_derive-1.0.159.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/serde_derive-1.0.159.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/serde_json-1.0.95.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/serde_json-1.0.95.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/sha2-0.10.6.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/sha2-0.10.6.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/slab-0.4.8.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/slab-0.4.8.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/smallvec-1.10.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/smallvec-1.10.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/socket2-0.5.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/socket2-0.5.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/strsim-0.10.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/strsim-0.10.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/syn-2.0.12.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/syn-2.0.12.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/termcolor-1.2.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/termcolor-1.2.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/textwrap-0.16.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/textwrap-0.16.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/thiserror-1.0.40.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/thiserror-1.0.40.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/thiserror-impl-1.0.40.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/thiserror-impl-1.0.40.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/thread_local-1.1.7.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/thread_local-1.1.7.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/time-0.3.20.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/time-0.3.20.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/tinyvec-1.6.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/tinyvec-1.6.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/tokio-1.27.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/tokio-1.27.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/tokio-util-0.7.7.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/tokio-util-0.7.7.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/toml-0.7.3.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/toml-0.7.3.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/tracing-0.1.37.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/tracing-0.1.37.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/tracing-core-0.1.30.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/tracing-core-0.1.30.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/typenum-1.16.0.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/typenum-1.16.0.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/unicode-bidi-0.3.13.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/unicode-bidi-0.3.13.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/unicode-normalization-0.1.22.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/unicode-normalization-0.1.22.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/unicode-width-0.1.10.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/unicode-width-0.1.10.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/unicode-xid-0.2.4.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/unicode-xid-0.2.4.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/url-2.3.1.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/url-2.3.1.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/version_check-0.9.4.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/version_check-0.9.4.crate -------------------------------------------------------------------------------- /ruslic/tests/top_100_crates/winapi-0.3.9.crate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasAlaif/russol-alpha/57d61d10e405c06f2dd2729dd3c55e2135c78b19/ruslic/tests/top_100_crates/winapi-0.3.9.crate -------------------------------------------------------------------------------- /ruslic/tests/unsupported/generics_unsound.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | // Unsoundesses due to ignoring generics bounds 4 | 5 | #[extern_spec] 6 | fn leak(b: Box) -> &'static mut T { 7 | Box::leak(b) 8 | } 9 | // Ruslik will synth the following: 10 | fn leak_synth<'a, T>(x: T) -> &'a mut T { 11 | let b = Box::new(x); 12 | leak(b) 13 | } 14 | 15 | // However, the rust compiler rejects this due to adding an implicit bound that 16 | // `leak`, but since we don't care about lifetime bounds on generics 17 | // (and subtyping in general for that matter) we ignore these and synthesise 18 | // a body which gets rejected. Note: this is purely due to the hacky way in which 19 | // we handle generics. 20 | 21 | // This is an identical issue: 22 | #[extern_spec] 23 | fn copy(c: &T) -> T { 24 | *c 25 | } 26 | fn copy_synth(c: &T) -> T { 27 | copy(c) 28 | } 29 | -------------------------------------------------------------------------------- /ruslic/tests/unsupported/privacy.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | mod m { 4 | // Private field 5 | pub struct Tuple(pub i32, i32); 6 | 7 | // Private struct 8 | #[derive(Clone, Copy)] 9 | struct Priv(i32); 10 | // Cannot touch second field 11 | pub struct HasPriv { pub x: i32, pub y: Priv } 12 | pub struct Twice(pub HasPriv, pub HasPriv); 13 | } 14 | 15 | // Cannot access private field - must mutate argument 16 | #[ensures(result.0 == 42)] 17 | fn foo(c: m::Tuple) -> m::Tuple { 18 | let mut c = c; 19 | c.0 = 42; 20 | c 21 | } 22 | 23 | // Cannot access private type 24 | #[ensures(result.0.x == 4)] 25 | #[ensures(result.1.x == 2)] 26 | fn bar(c: m::HasPriv) -> m::Twice { 27 | // Not possible to do `y: c.y` 28 | let a = m::HasPriv { x: 4, ..c }; 29 | let b = m::HasPriv { x: 2, ..c }; 30 | m::Twice(a, b) 31 | } 32 | -------------------------------------------------------------------------------- /ruslic/tests/unsupported/reborrows.rs: -------------------------------------------------------------------------------- 1 | use russol_contracts::*; 2 | 3 | #[ensures(^*x == **x)] 4 | fn foo<'b, 'a>(x: &'a mut &'b mut i32, y: &'b mut i32) -> &'a mut i32 { 5 | *x = y; 6 | // Cannot synthesize, because cannot write before open 7 | let tmp = &mut **x; 8 | tmp 9 | // expire tmp -> x 10 | } 11 | 12 | // It would like to do: 13 | // let tmp = &mut **x; 14 | // *x = y; 15 | // tmp 16 | // ^ But that is not valid code so it cannot 17 | -------------------------------------------------------------------------------- /ruslic/tests/unsupported/sorted.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | use russol_contracts::*; 3 | 4 | enum Node { 5 | Nil, 6 | Cons { f: T, next: Box> }, 7 | } 8 | 9 | impl Node { 10 | #[pure] 11 | fn len(&self) -> u16 { 12 | match self { 13 | Node::Nil => 0, 14 | Node::Cons { next, .. } => 1 + next.len(), 15 | } 16 | } 17 | #[pure] 18 | fn elems(&self) -> Set { 19 | match self { 20 | Node::Nil => set![], 21 | Node::Cons { f, next } => set![f] + next.elems(), 22 | } 23 | } 24 | #[pure] 25 | fn is_sorted(&self) -> bool { 26 | match self { 27 | Node::Nil => true, 28 | Node::Cons { f, next } => next.is_sorted() && 29 | if let box Node::Cons { f: f_n, .. } = next { *f <= *f_n } else { true }, 30 | } 31 | } 32 | } 33 | 34 | /// Unsupported 35 | #[requires(list.is_sorted())] 36 | #[ensures((^list).is_sorted())] 37 | // #[ensures(match (&*list, &^list) { 38 | // (Node::Nil, Node::Cons { f, next: box Node::Nil }) => f === v, 39 | // (Node::Cons { f, .. }, Node::Cons { f: ff, .. }) => if *f < v { f === ff } else { v === *ff }, 40 | // _ => false 41 | // })] 42 | #[ensures((^list).len() == list.len() + 1)] 43 | fn sorted_insert(list: &mut Node, v: i32) { 44 | ruslik!() 45 | } 46 | -------------------------------------------------------------------------------- /russol-contracts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "russol-contracts" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | russol-macros = { path = "../russol-macros" } 8 | -------------------------------------------------------------------------------- /russol-contracts/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | /// A macro for writing a precondition on a function. 4 | pub use russol_macros::requires; 5 | pub fn requires bool>(_closure: T) {} 6 | 7 | /// A macro for writing a postcondition on a function. 8 | pub use russol_macros::ensures; 9 | pub fn ensures bool>(_closure: T) {} 10 | 11 | /// A macro for writing a postcondition on a pure function. 12 | pub use russol_macros::trusted_ensures; 13 | pub fn trusted_ensures bool>(_closure: T) {} 14 | 15 | pub use russol_macros::extern_spec; 16 | pub use russol_macros::helper; 17 | pub use russol_macros::params; 18 | /// A macro for writing a measure. 19 | pub use russol_macros::pure; 20 | pub use russol_macros::synth; 21 | 22 | pub use russol_macros::ruslik; 23 | 24 | /// This function is used to evaluate an expression in the “old” 25 | /// context, that is at the beginning of the method call. 26 | // pub fn old(_arg: &T) -> T { 27 | // panic!("Cannot execute `old`, use `clone` instead.") 28 | // } 29 | 30 | /// A snapshot type 31 | #[non_exhaustive] 32 | #[derive(Copy, Clone)] 33 | pub struct Snapshot(core::marker::PhantomData); 34 | pub trait Snapshotable { 35 | fn snap(&self) -> Snapshot { 36 | panic!("Cannot take snapshot in executable code!") 37 | } 38 | } 39 | impl Snapshotable for T {} 40 | impl Eq for Snapshot {} 41 | impl PartialEq for Snapshot { 42 | fn eq(&self, _: &Self) -> bool { 43 | panic!("Cannot compare snapshots in executable code!") 44 | } 45 | } 46 | 47 | // #[macro_export] 48 | // macro_rules! equiv { 49 | // ($lhs:expr, $rhs:expr) => { ($lhs).snap() == ($rhs).snap() }; 50 | // } 51 | 52 | /// A sequence type 53 | #[non_exhaustive] 54 | #[derive(Copy, Clone)] 55 | pub struct Set(core::marker::PhantomData); 56 | impl Set { 57 | pub fn new(_: &[&T]) -> Self { 58 | panic!() 59 | } 60 | // pub fn union(self, _: Self) -> Self { panic!() } 61 | // pub fn subset(self, _: Self) -> bool { panic!() } 62 | // pub fn diff(self, _: Self) -> Self { panic!() } 63 | // pub fn intersect(self, _: Self) -> Self { panic!() } 64 | // pub fn contains(self, _: i32) -> bool { panic!() } 65 | } 66 | 67 | #[macro_export] 68 | macro_rules! set { 69 | ($($val:expr),*) => { $crate::Set::new(&[$($val,)*]) }; 70 | } 71 | impl core::ops::Index<&T> for Set { 72 | type Output = bool; 73 | fn index(&self, _: &T) -> &bool { 74 | panic!() 75 | } 76 | } 77 | impl core::ops::Add for Set { 78 | type Output = Self; 79 | fn add(self, _: Self) -> Self { 80 | panic!() 81 | } 82 | } 83 | impl core::ops::Sub for Set { 84 | type Output = Self; 85 | fn sub(self, _: Self) -> Self { 86 | panic!() 87 | } 88 | } 89 | impl core::ops::Mul for Set { 90 | type Output = Self; 91 | fn mul(self, _: Self) -> Self { 92 | panic!() 93 | } 94 | } 95 | impl Eq for Set {} 96 | impl PartialEq for Set { 97 | fn eq(&self, _: &Self) -> bool { 98 | panic!() 99 | } 100 | } 101 | impl PartialOrd for Set { 102 | fn partial_cmp(&self, _: &Self) -> Option { 103 | panic!() 104 | } 105 | } 106 | impl Ord for Set { 107 | fn cmp(&self, _other: &Self) -> core::cmp::Ordering { 108 | panic!() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /russol-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "russol-macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | syn = { version = "^1.0", features = ["full", "parsing", "proc-macro", "printing", "visit-mut"] } 8 | quote = "^1.0" 9 | proc-macro2 = { version = "^1.0", features = ["span-locations"] } 10 | 11 | [lib] 12 | proc-macro = true 13 | -------------------------------------------------------------------------------- /russol-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(box_patterns)] 3 | #![feature(let_chains)] 4 | #![feature(proc_macro_span, proc_macro_diagnostic, proc_macro_span_shrink)] 5 | 6 | extern crate alloc; 7 | 8 | use alloc::string::ToString; 9 | use proc_macro::TokenStream; 10 | use proc_macro2::TokenStream as TokenStream2; 11 | use quote::ToTokens; 12 | use syn::parse_quote; 13 | 14 | mod rewriter; 15 | 16 | #[proc_macro_attribute] 17 | pub fn requires(attr: TokenStream, tokens: TokenStream) -> TokenStream { 18 | parse_fn_specs(tokens.into(), attr.into(), SpecKind::Requires) 19 | .unwrap_or_else(|x| x) 20 | .into() 21 | } 22 | 23 | #[proc_macro_attribute] 24 | pub fn ensures(attr: TokenStream, tokens: TokenStream) -> TokenStream { 25 | parse_fn_specs(tokens.into(), attr.into(), SpecKind::Ensures) 26 | .unwrap_or_else(|x| x) 27 | .into() 28 | } 29 | 30 | #[proc_macro_attribute] 31 | pub fn trusted_ensures(attr: TokenStream, tokens: TokenStream) -> TokenStream { 32 | parse_fn_specs(tokens.into(), attr.into(), SpecKind::TrustedEnsures) 33 | .unwrap_or_else(|x| x) 34 | .into() 35 | } 36 | 37 | #[proc_macro_attribute] 38 | pub fn pure(_attr: TokenStream, tokens: TokenStream) -> TokenStream { 39 | match syn::parse2::(tokens.into()) { 40 | Ok(mut item_fn) => { 41 | item_fn.attrs.push(parse_quote! { #[rustfmt::ruslik_pure] }); 42 | item_fn.into_token_stream() 43 | } 44 | Err(e) => e.to_compile_error(), 45 | } 46 | .into() 47 | } 48 | 49 | #[proc_macro_attribute] 50 | pub fn helper(_attr: TokenStream, tokens: TokenStream) -> TokenStream { 51 | match syn::parse2::(tokens.into()) { 52 | Ok(mut item_fn) => { 53 | item_fn 54 | .attrs 55 | .push(parse_quote! { #[rustfmt::ruslik_helper] }); 56 | item_fn.into_token_stream() 57 | } 58 | Err(e) => e.to_compile_error(), 59 | } 60 | .into() 61 | } 62 | 63 | #[proc_macro_attribute] 64 | pub fn synth(_attr: TokenStream, tokens: TokenStream) -> TokenStream { 65 | match syn::parse2::(tokens.into()) { 66 | Ok(mut item_fn) => { 67 | item_fn 68 | .attrs 69 | .push(parse_quote! { #[rustfmt::ruslik_synth] }); 70 | item_fn.into_token_stream() 71 | } 72 | Err(e) => e.to_compile_error(), 73 | } 74 | .into() 75 | } 76 | 77 | #[proc_macro_attribute] 78 | pub fn params(attr: TokenStream, tokens: TokenStream) -> TokenStream { 79 | match syn::parse2::(tokens.into()) { 80 | Ok(mut item_fn) => { 81 | let attr: TokenStream2 = attr.into(); 82 | item_fn 83 | .attrs 84 | .push(parse_quote! { #[rustfmt::ruslik_params = #attr] }); 85 | item_fn.into_token_stream() 86 | } 87 | Err(e) => e.to_compile_error(), 88 | } 89 | .into() 90 | } 91 | 92 | #[proc_macro_attribute] 93 | pub fn extern_spec(_attr: TokenStream, tokens: TokenStream) -> TokenStream { 94 | match syn::parse2::(tokens.into()) { 95 | Ok(mut item_fn) => { 96 | item_fn 97 | .attrs 98 | .push(parse_quote! { #[rustfmt::ruslik_extern_spec] }); 99 | item_fn.into_token_stream() 100 | } 101 | Err(e) => e.to_compile_error(), 102 | } 103 | .into() 104 | } 105 | 106 | #[derive(Clone, Copy)] 107 | pub(crate) enum SpecKind { 108 | Requires, 109 | Ensures, 110 | TrustedEnsures, 111 | } 112 | fn parse_fn_specs( 113 | fun: TokenStream2, 114 | attr: TokenStream2, 115 | attr_kind: SpecKind, 116 | ) -> Result { 117 | let mut item_fn: syn::ItemFn = syn::parse2(fun).map_err(|e| e.to_compile_error())?; 118 | let orig_len = item_fn.block.stmts.len(); 119 | item_fn.block.stmts.insert( 120 | 0, 121 | rewriter::parse_attr(attr, attr_kind, &item_fn.sig.output)?, 122 | ); 123 | 124 | item_fn.attrs = item_fn 125 | .attrs 126 | .into_iter() 127 | .filter_map(|attr| { 128 | let sk = match attr.path.segments[0].ident.to_string().as_str() { 129 | "requires" => SpecKind::Requires, 130 | "ensures" => SpecKind::Ensures, 131 | "ruslik_spec_count" => panic!("The attribute `ruslik_spec_count` is reserved!"), 132 | _ => return Some(Ok(attr)), 133 | }; 134 | match rewriter::parse_attr(attr.tokens, sk, &item_fn.sig.output) { 135 | Ok(expr) => { 136 | item_fn.block.stmts.insert(0, expr); 137 | None 138 | } 139 | Err(e) => Some(Err(e)), 140 | } 141 | }) 142 | .collect::>()?; 143 | 144 | let spec_count = (item_fn.block.stmts.len() - orig_len).to_string(); 145 | // Either we register `ruslik` as a tool (but this runs into issues when compiling without ruslic) 146 | // Or we hijack a built-in tool for attributes (`rustfmt` or `clippy`) 147 | item_fn 148 | .attrs 149 | .push(parse_quote! { #[rustfmt::ruslik_spec_count = #spec_count] }); 150 | Ok(item_fn.into_token_stream()) 151 | } 152 | 153 | #[proc_macro] 154 | pub fn ruslik(input: TokenStream) -> TokenStream { 155 | let input: TokenStream2 = input.into(); 156 | quote::quote! { panic!(#input) }.into() 157 | } 158 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2022-09-14" 3 | components = [ "rustc-dev", "llvm-tools-preview", "rust-std", "rustfmt" ] 4 | profile = "minimal" --------------------------------------------------------------------------------