From 14ee0f2ff601cf231dd89a735ab71988f70ae4ad Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Tue, 1 Apr 2025 14:06:38 +1100 Subject: [PATCH 01/29] add KMIR to the docs using prepared tool template text --- doc/src/SUMMARY.md | 1 + doc/src/tools.md | 2 +- doc/src/tools/kmir.md | 90 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 doc/src/tools/kmir.md diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index f1de498184d14..65ff54da03f28 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -10,6 +10,7 @@ - [Kani](./tools/kani.md) - [GOTO Transcoder](./tools/goto-transcoder.md) - [VeriFast](./tools/verifast.md) + - [KMIR](./tools/kmir.md) --- diff --git a/doc/src/tools.md b/doc/src/tools.md index 4de93a49686ee..b746c7afd3ec9 100644 --- a/doc/src/tools.md +++ b/doc/src/tools.md @@ -14,7 +14,7 @@ please see the [Tool Application](general-rules.md#tool-applications) section. | Kani Rust Verifier | [![Kani](https://github.com/model-checking/verify-rust-std/actions/workflows/kani.yml/badge.svg)](https://github.com/model-checking/verify-rust-std/actions/workflows/kani.yml) | | GOTO-Transcoder (ESBMC) | [![GOTO-Transcoder](https://github.com/model-checking/verify-rust-std/actions/workflows/goto-transcoder.yml/badge.svg)](https://github.com/model-checking/verify-rust-std/actions/workflows/goto-transcoder.yml) | | VeriFast for Rust | [![VeriFast](https://github.com/model-checking/verify-rust-std/actions/workflows/verifast.yml/badge.svg)](https://github.com/model-checking/verify-rust-std/actions/workflows/verifast.yml) | - + | KMIR Symbolic Rust Execution | [![KMIR](https://github.com/model-checking/verify-rust-std/actions/workflows/kmir.yml/badge.svg)](https://github.com/model-checking/verify-rust-std/actions/workflows/kmir.yml) | diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md new file mode 100644 index 0000000000000..ae1967561fadc --- /dev/null +++ b/doc/src/tools/kmir.md @@ -0,0 +1,90 @@ +## Tool Name +**KMIR** + +## Description +[KMIR](https://github.com/runtimeverification/mir-semantics) is a formal verification tool for Rust that defines the operational semantics of Rust’s Middle Intermediate Representation (MIR) in K (through Stable MIR). By leveraging the [K framework](https://kframework.org/), KMIR provides a parser, interpreter, and symbolic execution engine for MIR programs. This tool enables direct execution of concrete and symbolic input, with step-by-step inspection of the internal state of the MIR program's execution, serving as a foundational step toward full formal verification of Rust programs. Through the dependency [Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/), KMIR allows developers to extract serialized Stable MIR from Rust’s compilation process, execute it, and eventually prove critical properties of their code. Soon, KMIR will be available via our package manager, [kup](https://github.com/runtimeverification/kup), which will make it easily installable and integrable into various workflows. + +This diagram describes the extraction and verification workflow for KMIR: + +![kmir_env_diagram_march_2025](https://github.com/user-attachments/assets/bf426c8d-f241-4ad6-8cb2-86ca06d8d15b) + + +Particular to this challenge, KMIR verifies program correctness using the correct-by-construction symbolic execution engine and verifier derived from the K encoding of the Stable MIR semantics. The K semantics framework is based on reachability logic, which is a theory describing transition systems in [matching logic](http://www.matching-logic.org/). Transition rules of the semantics are rewriting steps that match patterns and transform the current continuation and state accordingly. An all-path-reachability proof in this system verifies that a particular _target_ end state is _always_ reachable from a given starting state. The rewrite rules branch on symbolic inputs covering the possible transitions, creating a model that is provably complete, and requiring unification on every leaf state. A one-path-reachability proof is similar to the above, but the proof requirement is that at least one leaf state unifies with the target state. One feature of such a system is that the requirement for an SMT is minimized to determining completeness on path conditions when branching would occur. + +KMIR also prioritizes UI with interactive proof exploration available out-of-the-box through the terminal KCFG (K Control Flow Graph) viewer, allowing users to inspect intermediate states of the proof to get feedback on the successful path conditions and failing at unifying with the target state. An example of a KMIR proof being analyzed using the KCFG viewer can be seen below: + +image + + +## Tool Information + +* [x] Does the tool perform Rust verification? + *Yes – It performs verification at the MIR level, which is a critical intermediate representation of Rust programs.* +* [x] Does the tool deal with *unsafe* Rust code? + *Yes – By operating on MIR, KMIR can analyze both safe and unsafe Rust code.* +* [x] Does the tool run independently in CI? + *Yes – KMIR can be integrated into CI workflows via our package manager and Nix-based build system.* +* [x] Is the tool open source? + *Yes – KMIR is open source and available on GitHub.* +* [x] Is the tool under development? + *Yes – KMIR is actively under development, with ongoing improvements to MIR syntax coverage and verification capabilities.* +* [x] Will you or your team be able to provide support for the tool? + *Yes – The Runtime Verification team is committed to supporting KMIR and will provide ongoing maintenance and community support.* + +## Comparison to Other Approved Tools +The other tools approved at the time of writing are Kani, Verifast, and Goto-transcoder (ESBMC). + +- **Verification Backend:** KMIR primarily differs from all of these tools by utilizing a unique verification backend through the K framework and reachability logic (as explained in the description above). KMIR has little dependence on an SAT solver or SMT solver. Kani's CBMC backend and Goto-transcode (ESBMC) encode the verification problem into an SAT / SMT verification condition to be discharged by the appropriate solver. Kani recently added a Lean backend through Aeneas, however Lean does not support matching or reachability logic currently. Verifast performs symbollic execution of the verification target like KMIR, however reasoning is performed by annotating functions with design-by-contract components in separation logic. +- **Verification Input:** KMIR takes input from Stable MIR JSON, an effort to serialize the internal MIR in a portable way that can be reusable by other projects. +- **K Ecosystem:** Since all tools in the K ecosystem share a common foundation of K, all projects benefit from development done by other K projects. This means that performance and user experience are projected to improve due to the continued development of other semantics. + +## Licenses +KMIR is released under an OSI-approved open source license. It is distributed under the BSD-3 clause license, which is compatible with the Rust standard library licenses. Please refer to the [KMIR GitHub repository](https://github.com/runtimeverification/mir-semantics?tab=BSD-3-Clause-1-ov-file) for full license details. + +## Steps to Use the Tool + +At RV, we generally strives to use [kup](https://github.com/runtimeverification/kup) , a `nix`-based software installer, to distribute our software. +This is future work, at the moment `kmir` requires a local build from source using the `K Framework` (installed with `kup`). + +The future workflow we imagine is to +1. Download [kup](https://github.com/runtimeverification/kup) and install +2. Install KMIR `kup install kmir` +3. Compile the desired verification target with `kmir build module.rs` + The `module.rs` should contain functions that act as property tests and are run with symbolic inputs. +4. Verify the target with `kmir prove module.rs --target target_function` + This executes `target_function` with symbolic arguments. The test is expected to contain assertions about computed values. +5. Inspect KCFG of proof with `kmir view module::target_function` + +At the time of writing, step 3 requires manual work to set up a _claim_ in the K language. +We will automate this process in the frontend code to prevent the user from having to write K code. + +**To see the specifications and run proofs using KMIR, including a subsection of the proofs required in the [Safety of Methods for Numeric Primitive Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) challenges, check this [instructional document](https://github.com/runtimeverification/mir-semantics/blob/sample-challenge-11-proofs/rust-verification-proofs/README.md) in our tool's repository.** + +## Artifacts & Audit Mechanisms + +- **[Matching Logic](http://www.matching-logic.org/)** + Matching Logic is a foundational logic underpinning the K framework, providing a unified approach to specifying, verifying, and reasoning about programming languages and their properties in a correct-by-construction manner. + +- **[K Framework](https://kframework.org/)** + The K Framework is a rewrite-based executable semantic framework designed for defining programming languages, type systems, and formal analysis tools. It automatically generates language analysis tools directly from their formal semantics. + +- **[Kontrol](https://kontrol.runtimeverification.com)** + Kontrol is a formal verification tool built on K's semantics, enabling symbolic execution and proof construction for Ethereum smart contracts. + +- **[KEVM Semantics](https://github.com/kframework/evm-semantics)** + KEVM provides a practical, executable formal specification of the Ethereum Virtual Machine using the K Framework, demonstrating K’s real-world application for verifying blockchain virtual machines. + +## Versioning +KMIR and Stable MIR JSON are both version controlled using git and hosted by Github. Semantic version numbers are used as soon as releases are made. +Stable MIR JSON depends on a nightly Rust compiler of a particular version (which is regularly updated, currently `nightly-2024-11-29`). +Dependencies for K and MIR JSON are tracked as pinned versions in the 'deps' folder and updated as changes are published upstream and tested against mir-semantics. + +## CI +At Runtime Verification, we are regularly releasing and updating our tools using GitHub Actions and publishing our updated tool releases to standardized locations such as [Dockerhub](https://hub.docker.com/u/runtimeverificationinc) / ghcr.io / [cachix](https://app.cachix.org/cache/k-framework-binary). Any changes upstream to [K](https://github.com/runtimeverification/k) or [stable-mir-json](https://github.com/runtimeverification/stable-mir-json/) are immediately propagated to mir-semantics via workflow triggers to ensure the latest release of the tool is getting the latest improvements from K. + +For integrating KMIR into your project's CI pipeline, we recommend using our pre-built packages. You can choose from several installation methods depending on your needs: + +Our current Registries / Caches are: +1. [Binary Cachix cache used by Kup](https://app.cachix.org/cache/k-framework-binary) +2. Source code on GitHub: [mir-semantics]](https://github.com/runtimeverification/mir-semantics) and [stable-mir-json](https://github.com/runtimeverification/stable-mir-json) +3. [Container image on Dockerhub](https://hub.docker.com/u/runtimeverificationinc) From 39bbca790f4e486916310fe66d6c71043824434a Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Tue, 1 Apr 2025 14:48:11 +1100 Subject: [PATCH 02/29] add kmir workflow and proof claim files + source. WIP README.mds need adjustments --- .github/workflows/kmir.yml | 38 + kmir-proofs/maximum-example-proof/README.md | 98 + .../maximum-example-proof/main-max-with-lt.rs | 18 + .../main-max-with-lt.smir.dot | 135 + .../main-max-with-lt.smir.json | 2676 +++++++++++++++++ .../maximum-example-proof/maximum-spec.k | 73 + kmir-proofs/unchecked_arithmetic/README.md | 95 + .../unchecked_arithmetic/unchecked-add-spec.k | 75 + .../unchecked_arithmetic/unchecked-add.rs | 14 + .../unchecked_arithmetic/unchecked-mul-spec.k | 73 + .../unchecked_arithmetic/unchecked-mul.rs | 14 + .../unchecked_arithmetic/unchecked-sub-spec.k | 73 + .../unchecked_arithmetic/unchecked-sub.rs | 14 + 13 files changed, 3396 insertions(+) create mode 100644 .github/workflows/kmir.yml create mode 100644 kmir-proofs/maximum-example-proof/README.md create mode 100644 kmir-proofs/maximum-example-proof/main-max-with-lt.rs create mode 100644 kmir-proofs/maximum-example-proof/main-max-with-lt.smir.dot create mode 100644 kmir-proofs/maximum-example-proof/main-max-with-lt.smir.json create mode 100644 kmir-proofs/maximum-example-proof/maximum-spec.k create mode 100644 kmir-proofs/unchecked_arithmetic/README.md create mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-add-spec.k create mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-add.rs create mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-mul-spec.k create mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-mul.rs create mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-sub-spec.k create mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-sub.rs diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml new file mode 100644 index 0000000000000..9e7b8a3dc8435 --- /dev/null +++ b/.github/workflows/kmir.yml @@ -0,0 +1,38 @@ +name: KMIR + +on: + workflow_dispatch: + merge_group: + pull_request: + branches: [ main ] + push: + paths: + - 'library/**' + - '.github/workflows/kmir.yml' + - 'kmir-proofs/**' + +defaults: + run: + shell: bash + +jobs: + run-kmir-proofs: + name: Run supplied KMIR proofs + runs-on: ubuntu-latest + container: runtimeverificationinc/kmir:ubuntu-jammy-0.3.112_7.1.229-daf5158 + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + path: head + + - name: Run KMIR Verification + run: | + cd kmir-proofs + for k_file in */*-spec.k"; do + echo "Running ${k_file}" + kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs + done + + diff --git a/kmir-proofs/maximum-example-proof/README.md b/kmir-proofs/maximum-example-proof/README.md new file mode 100644 index 0000000000000..8856fe01c87b5 --- /dev/null +++ b/kmir-proofs/maximum-example-proof/README.md @@ -0,0 +1,98 @@ +# Turning the max-with-lt program into a property proof + +## Example program that we start from + +```rust +fn main() { + + let a:usize = 42; + let b:usize = 22; + let c:usize = 0; + + let result = maximum(a, b, c); + + assert!(result >= a && result >= b && result >= c + && (result == a || result == b || result == c ) ); +} + +fn maximum(a: usize, b: usize, c: usize) -> usize { + // max(a, max(b, c)) + let max_ab = if a < b {b} else {a}; + if max_ab < c {c} else {max_ab} +} +``` + +We want to prove a property of `maximum`: +- When called with `a`, `b`, and `c`, +- the `result` will be greater or equal all of the arguments, +- and equal to one (or more) of them. + +The `main` program above states this using some concrete values of `a`, `b`, and `c`. We will run this program to construct a general symbolic claim and prove it. + +In a future version, we will be able to start directly with the `maximum` function call and provide symbolic arguments to it. This will save some manual work setting up the claim file and fits the target of proving based on property tests. + +## Extracting Stable MIR for the program + +Before we can run the program using the MIR semantics, we have to compile it with a special compiler to extract Stable MIR from it. This step differs a bit depending on whether the program has multiple crates, in our case it is just a simple `rustc` invocation. This creates `main-max-with-lt.smir.json`. (Run the below commmands from the `mir-semantics/rust-verification-proofs/maximum-proof/` directory). + +```shell +cargo -Z unstable-options -C ../../deps/stable-mir-json/ run -- -Zno-codegen --out-dir $PWD $PWD/main-max-with-lt.rs +``` +The Stable MIR for the program can also be rendered as a graph, using the `--dot` option. This creates `main-max-with-lt.smir.dot`. + +```shell +cargo -Z unstable-options -C ../../deps/stable-mir-json/ run -- --dot -Zno-codegen --out-dir $PWD $PWD/main-max-with-lt.rs +``` +## Constructing the claim by executing `main` to certain points +Through concrete execution of the parsed K program we can interrupt the execution after a given number of rewrite steps to inspect the intermediate state. This will help us with writing our claim manually until the process is automated. + +1. The program (`main`) reaches the call to `maximum` after 22 steps. + The following command runs it and displays the resulting program state. + + ```shell + poetry -C ../../kmir/ run -- kmir run $PWD/main-max-with-lt.smir.json --depth 22 | less -S + ``` + - Arguments `a`, `b`, and `c` are initialised to `Integer`s as `locals[1]` to `locals[3]` + - A `call` terminator calling function `ty(25)` is executed next (front of the `k` cell) + - The function table contains `ty(25) -> "maximum" code. + - Other state (how `main` continues, its other local variables, and some internal functions) is relevant to the proof we want to perform. +2. The program executes for a total of 92 steps to reach the point where it `return`s from `maximum`. + The following command runs it and displays the resulting program state. + + ```shell + poetry -C ../../kmir/ run -- kmir run $PWD/main-max-with-lt.smir.json --depth 92 | less -S + ``` + - The value `locals[0]` is now set to an `Integer`. This will be the target of our assertions. + - A `return` terminator is executed next (front of the `k` cell), it will return `locals[0]` + - It should be an `Integer` with the desired properties as stated above + +State 1. defines our start state for the claim. Irrelevant parts are elided (replaced by variables). +* The code of the `maximum` function in the `functions` table needs to be kept. We also keep its identifier `ty(25)`. Other functions can be removed (we won't perform a return). +* The `call` terminator is kept, calling `ty(25)` with arguments from `locals[1,2,3]`. `target` is modified to be `noBasicBlockIdx` to force termination of the prover (no block to jump back to). +* The four locals `0` - `3` are required in their original order to provide the function arguments. The values of `a`, `b`, and `c` in locals `1` - `3` are replaced with symbolic variables used in the proof. +* We could keep all other locals but do not have to (however it is important that the list of locals has a known length). +* `main`s other details in `currentFrame` are irrelevant and elided. + + +State 2. is the end state, where all that matters is the returned value. + +* The `locals` list should contain this `?RESULT` value at index `0` +* The `?RESULT` value should have the properties stated (equivalent to the assertion in `main`) +* Because of the modified `target`, the program should end, i.e., have an `#EndProgram` in the `k` cell. + +The above is written as a _claim_ in K framework language into a `maximum-spec.k` file. +Most of the syntax can be copied from the output of the `kmir run` commands above, and irrelevant parts replaced by `_` (LHS) or `?_` (RHS). + +Alternatively, it is possible to construct a claim that the entire rest of the program after initialising the variables will result in the desired `?RESULT`, i.e., the assertion in `main` is executed successfully and the program ends in `#EndProgram` after checking it. This would require more steps. + +## Running the prover on the claim and viewing the proof +Now that we have constructed claim, we can run use the KMIR verifier to perform symbollic execution, and can view the state of proof through the KMIR proof viewer. +```shell +poetry -C ../../kmir/ run -- kmir prove run $PWD/maximum-spec.k --proof-dir $PWD/proof +``` + +The proof steps are saved in the `$PWD/proof` directory for later inspection using `kmir prove view`. This is especially important when the proof does _not_ succeed immediately. + +```shell +poetry -C ../../kmir/ run -- kmir prove view MAXIMUM-SPEC.maximum-spec --proof-dir $PWD/proof +``` diff --git a/kmir-proofs/maximum-example-proof/main-max-with-lt.rs b/kmir-proofs/maximum-example-proof/main-max-with-lt.rs new file mode 100644 index 0000000000000..448c2cb9d4574 --- /dev/null +++ b/kmir-proofs/maximum-example-proof/main-max-with-lt.rs @@ -0,0 +1,18 @@ + +fn main() { + + let a:usize = 42; + let b:usize = 22; + let c:usize = 0; + + let result = maximum(a, b, c); + + assert!(result >= a && result >= b && result >= c + && (result == a || result == b || result == c ) ); +} + +fn maximum(a: usize, b: usize, c: usize) -> usize { + // max(a, max(b, c)) + let max_ab = if a < b {b} else {a}; + if max_ab < c {c} else {max_ab} +} diff --git a/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.dot b/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.dot new file mode 100644 index 0000000000000..6da5f7ff3ac9e --- /dev/null +++ b/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.dot @@ -0,0 +1,135 @@ +digraph { + label="main_max_with_lt"; + node [shape=rectangle]; + X8b0ac2e54b9a91_0 [label="NoOp: ", color=red]; + Xac08878333d72e42_0 [label="_ZN4core9panicking5panic1\n7h941160ead03e2d54E", color=red]; + Xc987e5ecea6cc82b_0 [label="_ZN3std2rt19lang_start_in\nternal17h018b8394ba015d86\nE", color=red]; + X3c6542d96320ad67_0 [label="Intr: \nblack_box", color=red]; + subgraph cluster_0 { + label="main"; + style="filled"; + color=palegreen; + X37252ea5c5b3ce2a_0 -> X37252ea5c5b3ce2a_1 [label="4"]; + X37252ea5c5b3ce2a_0 [label="1 <- Use(const :: usize)\l2 <- Use(const :: usize)\l3 <- Use(const :: usize)\lCall\l"]; + X37252ea5c5b3ce2a_1 -> X37252ea5c5b3ce2a_7 [label="0"]; + X37252ea5c5b3ce2a_1 -> X37252ea5c5b3ce2a_2 [label="other"]; + X37252ea5c5b3ce2a_1 [label="5 <- Ge(cp(4), cp(1))\lSwitchInt mv(5)\l"]; + X37252ea5c5b3ce2a_2 -> X37252ea5c5b3ce2a_7 [label="0"]; + X37252ea5c5b3ce2a_2 -> X37252ea5c5b3ce2a_3 [label="other"]; + X37252ea5c5b3ce2a_2 [label="6 <- Ge(cp(4), cp(2))\lSwitchInt mv(6)\l"]; + X37252ea5c5b3ce2a_3 -> X37252ea5c5b3ce2a_7 [label="0"]; + X37252ea5c5b3ce2a_3 -> X37252ea5c5b3ce2a_4 [label="other"]; + X37252ea5c5b3ce2a_3 [label="7 <- Ge(cp(4), cp(3))\lSwitchInt mv(7)\l"]; + X37252ea5c5b3ce2a_4 -> X37252ea5c5b3ce2a_5 [label="0"]; + X37252ea5c5b3ce2a_4 -> X37252ea5c5b3ce2a_8 [label="other"]; + X37252ea5c5b3ce2a_4 [label="8 <- Eq(cp(4), cp(1))\lSwitchInt mv(8)\l"]; + X37252ea5c5b3ce2a_5 -> X37252ea5c5b3ce2a_6 [label="0"]; + X37252ea5c5b3ce2a_5 -> X37252ea5c5b3ce2a_8 [label="other"]; + X37252ea5c5b3ce2a_5 [label="9 <- Eq(cp(4), cp(2))\lSwitchInt mv(9)\l"]; + X37252ea5c5b3ce2a_6 -> X37252ea5c5b3ce2a_7 [label="0"]; + X37252ea5c5b3ce2a_6 -> X37252ea5c5b3ce2a_8 [label="other"]; + X37252ea5c5b3ce2a_6 [label="10 <- Eq(cp(4), cp(3))\lSwitchInt mv(10)\l"]; + X37252ea5c5b3ce2a_7 [label="Call\l"]; + X37252ea5c5b3ce2a_8 [label="Return\l"]; + } + X37252ea5c5b3ce2a_0 -> X9585eeb1b7d3a83d_0 [label="cp(1),cp(2),cp(3)"]; + X37252ea5c5b3ce2a_7 -> Xac08878333d72e42_0 [label="const :: &str"]; + subgraph cluster_1 { + label="<() \nas \nstd::process::Termination\n>::report"; + style="filled"; + color=lightgray; + X5c233e009f84aa6c_0 [label="0 <- Use(const :: std::process::ExitCode)\lReturn\l"]; + } + subgraph cluster_2 { + label="std::sys::backtrace::__ru\nst_begin_short_backtrace:\n:"; + style="filled"; + color=lightgray; + X83f8b52e3f0ef4c5_0 -> X83f8b52e3f0ef4c5_1 [label="0"]; + X83f8b52e3f0ef4c5_0 [label="Call\l"]; + X83f8b52e3f0ef4c5_1 -> X83f8b52e3f0ef4c5_2 [label="2"]; + X83f8b52e3f0ef4c5_1 [label="Call\l"]; + X83f8b52e3f0ef4c5_2 [label="Return\l"]; + } + X83f8b52e3f0ef4c5_0 -> X5153bc83e282e268_0 [label="mv(1),const :: ()"]; + X83f8b52e3f0ef4c5_1 -> X3c6542d96320ad67_0 [label="const :: ()"]; + subgraph cluster_3 { + label="std::rt::lang_start::<()>"; + style="filled"; + color=lightgray; + X88af70ac7219a434_0 -> X88af70ac7219a434_1 [label="5"]; + X88af70ac7219a434_0 [label="Storage Live _5\lStorage Live _6\lStorage Live _8\l8 <- Closure (cp(1))\l7 <- & 8\l6 <- Cast-PointerCoercion(Unsize) cp(7)\lCall\l"]; + X88af70ac7219a434_1 [label="Storage Dead _6\l0 <- Use(cp(5 as VariantIdx(0).0))\lStorage Dead _8\lStorage Dead _5\lReturn\l"]; + } + X88af70ac7219a434_0 -> Xc987e5ecea6cc82b_0 [label="mv(6),mv(2),mv(3),mv(4)"]; + subgraph cluster_4 { + label="<{closure@std::rt::lang_s\ntart<()>::{closure#0}} \nas \nstd::ops::FnOnce<()>>::ca\nll_once"; + style="filled"; + color=lightgray; + X2aeea2bef42114da_0 -> X2aeea2bef42114da_1 [label="0"]; + X2aeea2bef42114da_0 [label="Call\l"]; + X2aeea2bef42114da_1 [label="Return\l"]; + } + X2aeea2bef42114da_0 -> X58ae416f9afa06ac_0 [label="mv(*1),mv(2)"]; + subgraph cluster_5 { + label=">::ca\nll_once"; + style="filled"; + color=lightgray; + X5153bc83e282e268_0 -> X5153bc83e282e268_1 [label="0"]; + X5153bc83e282e268_0 [label="Call\l"]; + X5153bc83e282e268_1 [label="Return\l"]; + } + X5153bc83e282e268_0 -> X5153bc83e282e268_0: 1 [label=""]; + subgraph cluster_6 { + label="std::rt::lang_start::<()>\n::{closure#0}"; + style="filled"; + color=lightgray; + Xf1c2e3e2362b71b1_0 -> Xf1c2e3e2362b71b1_1 [label="3"]; + Xf1c2e3e2362b71b1_0 [label="Storage Live _2\lStorage Live _3\lStorage Live _4\l4 <- Use(cp(*1.0))\lCall\l"]; + Xf1c2e3e2362b71b1_1 -> Xf1c2e3e2362b71b1_2 [label="2"]; + Xf1c2e3e2362b71b1_1 [label="Storage Dead _4\lCall\l"]; + Xf1c2e3e2362b71b1_2 [label="Storage Dead _3\lStorage Live _5\l5 <- & 2.0\lStorage Live _6\l6 <- Use(cp(2.0.0))\l0 <- Cast-IntToInt mv(6)\lStorage Dead _6\lStorage Dead _5\lStorage Dead _2\lReturn\l"]; + } + Xf1c2e3e2362b71b1_0 -> X83f8b52e3f0ef4c5_0 [label="mv(4)"]; + Xf1c2e3e2362b71b1_1 -> X5c233e009f84aa6c_0 [label="mv(3)"]; + subgraph cluster_7 { + label="maximum"; + style="filled"; + color=palegreen; + X9585eeb1b7d3a83d_0 -> X9585eeb1b7d3a83d_2 [label="0"]; + X9585eeb1b7d3a83d_0 -> X9585eeb1b7d3a83d_1 [label="other"]; + X9585eeb1b7d3a83d_0 [label="5 <- Lt(cp(1), cp(2))\lSwitchInt mv(5)\l"]; + X9585eeb1b7d3a83d_1 -> X9585eeb1b7d3a83d_3; + X9585eeb1b7d3a83d_1 [label="4 <- Use(cp(2))\lGoto\l"]; + X9585eeb1b7d3a83d_2 -> X9585eeb1b7d3a83d_3; + X9585eeb1b7d3a83d_2 [label="4 <- Use(cp(1))\lGoto\l"]; + X9585eeb1b7d3a83d_3 -> X9585eeb1b7d3a83d_5 [label="0"]; + X9585eeb1b7d3a83d_3 -> X9585eeb1b7d3a83d_4 [label="other"]; + X9585eeb1b7d3a83d_3 [label="7 <- Use(cp(4))\l6 <- Lt(mv(7), cp(3))\lSwitchInt mv(6)\l"]; + X9585eeb1b7d3a83d_4 -> X9585eeb1b7d3a83d_6; + X9585eeb1b7d3a83d_4 [label="0 <- Use(cp(3))\lGoto\l"]; + X9585eeb1b7d3a83d_5 -> X9585eeb1b7d3a83d_6; + X9585eeb1b7d3a83d_5 [label="0 <- Use(cp(4))\lGoto\l"]; + X9585eeb1b7d3a83d_6 [label="Return\l"]; + } + subgraph cluster_8 { + label="<{closure@std::rt::lang_s\ntart<()>::{closure#0}} \nas \nstd::ops::FnOnce<()>>::ca\nll_once"; + style="filled"; + color=lightgray; + X58ae416f9afa06ac_0 -> X58ae416f9afa06ac_3 [label="Cleanup"]; + X58ae416f9afa06ac_0 -> X58ae416f9afa06ac_1 [label="0"]; + X58ae416f9afa06ac_0 [label="3 <- &mut 1\lCall\l"]; + X58ae416f9afa06ac_1 -> X58ae416f9afa06ac_2; + X58ae416f9afa06ac_1 [label="Drop 1\l"]; + X58ae416f9afa06ac_2 [label="Return\l"]; + X58ae416f9afa06ac_3 -> X58ae416f9afa06ac_4; + X58ae416f9afa06ac_3 [label="Drop 1\l"]; + X58ae416f9afa06ac_4 [label="Resume\l"]; + } + X58ae416f9afa06ac_0 -> Xf1c2e3e2362b71b1_0 [label="mv(3),mv(2)"]; + subgraph cluster_9 { + label="std::ptr::drop_in_place::\n<{closure@std::rt::lang_s\ntart<()>::{closure#0}}>"; + style="filled"; + color=lightgray; + Xefb68cd7a0d5be14_0 [label="Return\l"]; + } +} diff --git a/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.json b/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.json new file mode 100644 index 0000000000000..02182bb8e2e51 --- /dev/null +++ b/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.json @@ -0,0 +1,2676 @@ +{ + "name": "main_max_with_lt", + "crate_id": 5373935543796547206, + "allocs": [ + [ + 1, + { + "Memory": { + "bytes": [ + 97, + 115, + 115, + 101, + 114, + 116, + 105, + 111, + 110, + 32, + 102, + 97, + 105, + 108, + 101, + 100, + 58, + 32, + 114, + 101, + 115, + 117, + 108, + 116, + 32, + 62, + 61, + 32, + 97, + 32, + 38, + 38, + 32, + 114, + 101, + 115, + 117, + 108, + 116, + 32, + 62, + 61, + 32, + 98, + 32, + 38, + 38, + 32, + 114, + 101, + 115, + 117, + 108, + 116, + 32, + 62, + 61, + 32, + 99, + 32, + 38, + 38, + 10, + 32, + 32, + 32, + 32, + 40, + 114, + 101, + 115, + 117, + 108, + 116, + 32, + 61, + 61, + 32, + 97, + 32, + 124, + 124, + 32, + 114, + 101, + 115, + 117, + 108, + 116, + 32, + 61, + 61, + 32, + 98, + 32, + 124, + 124, + 32, + 114, + 101, + 115, + 117, + 108, + 116, + 32, + 61, + 61, + 32, + 99, + 41 + ], + "provenance": { + "ptrs": [] + }, + "align": 1, + "mutability": "Not" + } + } + ] + ], + "functions": [ + [ + 13, + { + "NormalSym": "_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h3491d8bffa495004E" + } + ], + [ + 23, + { + "NormalSym": "_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hb1d17c99442a8691E" + } + ], + [ + 14, + { + "NormalSym": "_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17hf5d9ff8f37d5cc66E" + } + ], + [ + 0, + { + "NormalSym": "_ZN3std2rt19lang_start_internal17h018b8394ba015d86E" + } + ], + [ + 21, + { + "NormalSym": "_ZN4core3ops8function6FnOnce9call_once17h35ef4f3d7035a7c0E" + } + ], + [ + 19, + { + "NormalSym": "_ZN4core3ops8function6FnOnce9call_once17h68df99d5221b15e2E" + } + ], + [ + 20, + { + "IntrinsicSym": "black_box" + } + ], + [ + 25, + { + "NormalSym": "_ZN16main_max_with_lt7maximum17h5e37abb753494251E" + } + ], + [ + 27, + { + "NormalSym": "_ZN4core9panicking5panic17h941160ead03e2d54E" + } + ], + [ + 33, + { + "NoOpSym": "" + } + ] + ], + "uneval_consts": [], + "items": [ + { + "symbol_name": "_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hb1d17c99442a8691E", + "mono_item_kind": { + "MonoItemFn": { + "name": "std::rt::lang_start::<()>::{closure#0}", + "id": 1, + "body": { + "blocks": [ + { + "statements": [ + { + "kind": { + "StorageLive": 2 + }, + "span": 16 + }, + { + "kind": { + "StorageLive": 3 + }, + "span": 15 + }, + { + "kind": { + "StorageLive": 4 + }, + "span": 17 + }, + { + "kind": { + "Assign": [ + { + "local": 4, + "projection": [] + }, + { + "Use": { + "Copy": { + "local": 1, + "projection": [ + "Deref", + { + "Field": [ + 0, + 7 + ] + } + ] + } + } + } + ] + }, + "span": 17 + } + ], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 14, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 13, + "id": 1 + } + } + }, + "args": [ + { + "Move": { + "local": 4, + "projection": [] + } + } + ], + "destination": { + "local": 3, + "projection": [] + }, + "target": 1, + "unwind": "Continue" + } + }, + "span": 15 + } + }, + { + "statements": [ + { + "kind": { + "StorageDead": 4 + }, + "span": 19 + } + ], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 18, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 14, + "id": 2 + } + } + }, + "args": [ + { + "Move": { + "local": 3, + "projection": [] + } + } + ], + "destination": { + "local": 2, + "projection": [] + }, + "target": 2, + "unwind": "Continue" + } + }, + "span": 16 + } + }, + { + "statements": [ + { + "kind": { + "StorageDead": 3 + }, + "span": 21 + }, + { + "kind": { + "StorageLive": 5 + }, + "span": 22 + }, + { + "kind": { + "Assign": [ + { + "local": 5, + "projection": [] + }, + { + "Ref": [ + { + "kind": "ReErased" + }, + "Shared", + { + "local": 2, + "projection": [ + { + "Field": [ + 0, + 15 + ] + } + ] + } + ] + } + ] + }, + "span": 22 + }, + { + "kind": { + "StorageLive": 6 + }, + "span": 23 + }, + { + "kind": { + "Assign": [ + { + "local": 6, + "projection": [] + }, + { + "Use": { + "Copy": { + "local": 2, + "projection": [ + { + "Field": [ + 0, + 15 + ] + }, + { + "Field": [ + 0, + 9 + ] + } + ] + } + } + } + ] + }, + "span": 23 + }, + { + "kind": { + "Assign": [ + { + "local": 0, + "projection": [] + }, + { + "Cast": [ + "IntToInt", + { + "Move": { + "local": 6, + "projection": [] + } + }, + 16 + ] + } + ] + }, + "span": 24 + }, + { + "kind": { + "StorageDead": 6 + }, + "span": 25 + }, + { + "kind": { + "StorageDead": 5 + }, + "span": 26 + }, + { + "kind": { + "StorageDead": 2 + }, + "span": 27 + } + ], + "terminator": { + "kind": "Return", + "span": 20 + } + } + ], + "locals": [ + { + "ty": 16, + "span": 28, + "mutability": "Mut" + }, + { + "ty": 11, + "span": 3, + "mutability": "Mut" + }, + { + "ty": 17, + "span": 16, + "mutability": "Mut" + }, + { + "ty": 1, + "span": 15, + "mutability": "Mut" + }, + { + "ty": 7, + "span": 17, + "mutability": "Mut" + }, + { + "ty": 18, + "span": 22, + "mutability": "Mut" + }, + { + "ty": 9, + "span": 23, + "mutability": "Mut" + } + ], + "arg_count": 1, + "var_debug_info": [ + { + "name": "main", + "source_info": { + "span": 9, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 1, + "projection": [ + "Deref", + { + "Field": [ + 0, + 7 + ] + } + ] + } + }, + "argument_index": null + }, + { + "name": "self", + "source_info": { + "span": 29, + "scope": 1 + }, + "composite": null, + "value": { + "Place": { + "local": 2, + "projection": [] + } + }, + "argument_index": 1 + }, + { + "name": "self", + "source_info": { + "span": 30, + "scope": 2 + }, + "composite": null, + "value": { + "Place": { + "local": 5, + "projection": [] + } + }, + "argument_index": 1 + } + ], + "spread_arg": null, + "span": 3 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN4core3ops8function6FnOnce9call_once17h68df99d5221b15e2E", + "mono_item_kind": { + "MonoItemFn": { + "name": ">::call_once", + "id": 3, + "body": { + "blocks": [ + { + "statements": [], + "terminator": { + "kind": { + "Call": { + "func": { + "Move": { + "local": 1, + "projection": [] + } + }, + "args": [], + "destination": { + "local": 0, + "projection": [] + }, + "target": 1, + "unwind": "Continue" + } + }, + "span": 43 + } + }, + { + "statements": [], + "terminator": { + "kind": "Return", + "span": 43 + } + } + ], + "locals": [ + { + "ty": 1, + "span": 43, + "mutability": "Mut" + }, + { + "ty": 7, + "span": 43, + "mutability": "Not" + }, + { + "ty": 1, + "span": 43, + "mutability": "Not" + } + ], + "arg_count": 2, + "var_debug_info": [], + "spread_arg": 2, + "span": 43 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN3std2rt10lang_start17h8f98b99a20edb596E", + "mono_item_kind": { + "MonoItemFn": { + "name": "std::rt::lang_start::<()>", + "id": 0, + "body": { + "blocks": [ + { + "statements": [ + { + "kind": { + "StorageLive": 5 + }, + "span": 1 + }, + { + "kind": { + "StorageLive": 6 + }, + "span": 2 + }, + { + "kind": { + "StorageLive": 8 + }, + "span": 3 + }, + { + "kind": { + "Assign": [ + { + "local": 8, + "projection": [] + }, + { + "Aggregate": [ + { + "Closure": [ + 1, + [ + { + "Type": 1 + }, + { + "Type": 2 + }, + { + "Type": 3 + }, + { + "Type": 4 + } + ] + ] + }, + [ + { + "Copy": { + "local": 1, + "projection": [] + } + } + ] + ] + } + ] + }, + "span": 3 + }, + { + "kind": { + "Assign": [ + { + "local": 7, + "projection": [] + }, + { + "Ref": [ + { + "kind": "ReErased" + }, + "Shared", + { + "local": 8, + "projection": [] + } + ] + } + ] + }, + "span": 2 + }, + { + "kind": { + "Assign": [ + { + "local": 6, + "projection": [] + }, + { + "Cast": [ + { + "PointerCoercion": "Unsize" + }, + { + "Copy": { + "local": 7, + "projection": [] + } + }, + 5 + ] + } + ] + }, + "span": 2 + } + ], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 0, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 0, + "id": 0 + } + } + }, + "args": [ + { + "Move": { + "local": 6, + "projection": [] + } + }, + { + "Move": { + "local": 2, + "projection": [] + } + }, + { + "Move": { + "local": 3, + "projection": [] + } + }, + { + "Move": { + "local": 4, + "projection": [] + } + } + ], + "destination": { + "local": 5, + "projection": [] + }, + "target": 1, + "unwind": "Continue" + } + }, + "span": 1 + } + }, + { + "statements": [ + { + "kind": { + "StorageDead": 6 + }, + "span": 5 + }, + { + "kind": { + "Assign": [ + { + "local": 0, + "projection": [] + }, + { + "Use": { + "Copy": { + "local": 5, + "projection": [ + { + "Downcast": 0 + }, + { + "Field": [ + 0, + 6 + ] + } + ] + } + } + } + ] + }, + "span": 6 + }, + { + "kind": { + "StorageDead": 8 + }, + "span": 7 + }, + { + "kind": { + "StorageDead": 5 + }, + "span": 7 + } + ], + "terminator": { + "kind": "Return", + "span": 4 + } + } + ], + "locals": [ + { + "ty": 6, + "span": 8, + "mutability": "Mut" + }, + { + "ty": 7, + "span": 9, + "mutability": "Not" + }, + { + "ty": 6, + "span": 10, + "mutability": "Not" + }, + { + "ty": 8, + "span": 11, + "mutability": "Not" + }, + { + "ty": 9, + "span": 12, + "mutability": "Not" + }, + { + "ty": 10, + "span": 1, + "mutability": "Mut" + }, + { + "ty": 5, + "span": 2, + "mutability": "Mut" + }, + { + "ty": 11, + "span": 2, + "mutability": "Not" + }, + { + "ty": 12, + "span": 3, + "mutability": "Not" + } + ], + "arg_count": 4, + "var_debug_info": [ + { + "name": "main", + "source_info": { + "span": 9, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 1, + "projection": [] + } + }, + "argument_index": 1 + }, + { + "name": "argc", + "source_info": { + "span": 10, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 2, + "projection": [] + } + }, + "argument_index": 2 + }, + { + "name": "argv", + "source_info": { + "span": 11, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 3, + "projection": [] + } + }, + "argument_index": 3 + }, + { + "name": "sigpipe", + "source_info": { + "span": 12, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 4, + "projection": [] + } + }, + "argument_index": 4 + }, + { + "name": "v", + "source_info": { + "span": 6, + "scope": 1 + }, + "composite": null, + "value": { + "Place": { + "local": 0, + "projection": [] + } + }, + "argument_index": null + } + ], + "spread_arg": null, + "span": 13 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN16main_max_with_lt7maximum17h5e37abb753494251E", + "mono_item_kind": { + "MonoItemFn": { + "name": "maximum", + "id": 7, + "body": { + "blocks": [ + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 5, + "projection": [] + }, + { + "BinaryOp": [ + "Lt", + { + "Copy": { + "local": 1, + "projection": [] + } + }, + { + "Copy": { + "local": 2, + "projection": [] + } + } + ] + } + ] + }, + "span": 69 + } + ], + "terminator": { + "kind": { + "SwitchInt": { + "discr": { + "Move": { + "local": 5, + "projection": [] + } + }, + "targets": { + "branches": [ + [ + 0, + 2 + ] + ], + "otherwise": 1 + } + } + }, + "span": 69 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 4, + "projection": [] + }, + { + "Use": { + "Copy": { + "local": 2, + "projection": [] + } + } + } + ] + }, + "span": 71 + } + ], + "terminator": { + "kind": { + "Goto": { + "target": 3 + } + }, + "span": 70 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 4, + "projection": [] + }, + { + "Use": { + "Copy": { + "local": 1, + "projection": [] + } + } + } + ] + }, + "span": 72 + } + ], + "terminator": { + "kind": { + "Goto": { + "target": 3 + } + }, + "span": 70 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 7, + "projection": [] + }, + { + "Use": { + "Copy": { + "local": 4, + "projection": [] + } + } + } + ] + }, + "span": 74 + }, + { + "kind": { + "Assign": [ + { + "local": 6, + "projection": [] + }, + { + "BinaryOp": [ + "Lt", + { + "Move": { + "local": 7, + "projection": [] + } + }, + { + "Copy": { + "local": 3, + "projection": [] + } + } + ] + } + ] + }, + "span": 73 + } + ], + "terminator": { + "kind": { + "SwitchInt": { + "discr": { + "Move": { + "local": 6, + "projection": [] + } + }, + "targets": { + "branches": [ + [ + 0, + 5 + ] + ], + "otherwise": 4 + } + } + }, + "span": 73 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 0, + "projection": [] + }, + { + "Use": { + "Copy": { + "local": 3, + "projection": [] + } + } + } + ] + }, + "span": 76 + } + ], + "terminator": { + "kind": { + "Goto": { + "target": 6 + } + }, + "span": 75 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 0, + "projection": [] + }, + { + "Use": { + "Copy": { + "local": 4, + "projection": [] + } + } + } + ] + }, + "span": 77 + } + ], + "terminator": { + "kind": { + "Goto": { + "target": 6 + } + }, + "span": 75 + } + }, + { + "statements": [], + "terminator": { + "kind": "Return", + "span": 78 + } + } + ], + "locals": [ + { + "ty": 26, + "span": 79, + "mutability": "Mut" + }, + { + "ty": 26, + "span": 80, + "mutability": "Not" + }, + { + "ty": 26, + "span": 81, + "mutability": "Not" + }, + { + "ty": 26, + "span": 82, + "mutability": "Not" + }, + { + "ty": 26, + "span": 83, + "mutability": "Not" + }, + { + "ty": 29, + "span": 69, + "mutability": "Mut" + }, + { + "ty": 29, + "span": 73, + "mutability": "Mut" + }, + { + "ty": 26, + "span": 74, + "mutability": "Mut" + } + ], + "arg_count": 3, + "var_debug_info": [ + { + "name": "a", + "source_info": { + "span": 80, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 1, + "projection": [] + } + }, + "argument_index": 1 + }, + { + "name": "b", + "source_info": { + "span": 81, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 2, + "projection": [] + } + }, + "argument_index": 2 + }, + { + "name": "c", + "source_info": { + "span": 82, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 3, + "projection": [] + } + }, + "argument_index": 3 + }, + { + "name": "max_ab", + "source_info": { + "span": 83, + "scope": 1 + }, + "composite": null, + "value": { + "Place": { + "local": 4, + "projection": [] + } + }, + "argument_index": null + } + ], + "spread_arg": null, + "span": 84 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN4core3ops8function6FnOnce9call_once17h35ef4f3d7035a7c0E", + "mono_item_kind": { + "MonoItemFn": { + "name": "<{closure@std::rt::lang_start<()>::{closure#0}} as std::ops::FnOnce<()>>::call_once", + "id": 3, + "body": { + "blocks": [ + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 3, + "projection": [] + }, + { + "Ref": [ + { + "kind": "ReErased" + }, + { + "Mut": { + "kind": "Default" + } + }, + { + "local": 1, + "projection": [] + } + ] + } + ] + }, + "span": 43 + } + ], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 43, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 23, + "id": 7 + } + } + }, + "args": [ + { + "Move": { + "local": 3, + "projection": [] + } + }, + { + "Move": { + "local": 2, + "projection": [] + } + } + ], + "destination": { + "local": 0, + "projection": [] + }, + "target": 1, + "unwind": { + "Cleanup": 3 + } + } + }, + "span": 43 + } + }, + { + "statements": [], + "terminator": { + "kind": { + "Drop": { + "place": { + "local": 1, + "projection": [] + }, + "target": 2, + "unwind": "Continue" + } + }, + "span": 43 + } + }, + { + "statements": [], + "terminator": { + "kind": "Return", + "span": 43 + } + }, + { + "statements": [], + "terminator": { + "kind": { + "Drop": { + "place": { + "local": 1, + "projection": [] + }, + "target": 4, + "unwind": "Terminate" + } + }, + "span": 43 + } + }, + { + "statements": [], + "terminator": { + "kind": "Resume", + "span": 43 + } + } + ], + "locals": [ + { + "ty": 16, + "span": 43, + "mutability": "Mut" + }, + { + "ty": 12, + "span": 43, + "mutability": "Not" + }, + { + "ty": 1, + "span": 43, + "mutability": "Not" + }, + { + "ty": 24, + "span": 43, + "mutability": "Not" + } + ], + "arg_count": 2, + "var_debug_info": [], + "spread_arg": 2, + "span": 43 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN16main_max_with_lt4main17h96bac61ef98236a2E", + "mono_item_kind": { + "MonoItemFn": { + "name": "main", + "id": 6, + "body": { + "blocks": [ + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 1, + "projection": [] + }, + { + "Use": { + "Constant": { + "span": 52, + "user_ty": null, + "const_": { + "kind": { + "Allocated": { + "bytes": [ + 42, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "provenance": { + "ptrs": [] + }, + "align": 8, + "mutability": "Mut" + } + }, + "ty": 26, + "id": 10 + } + } + } + } + ] + }, + "span": 52 + }, + { + "kind": { + "Assign": [ + { + "local": 2, + "projection": [] + }, + { + "Use": { + "Constant": { + "span": 53, + "user_ty": null, + "const_": { + "kind": { + "Allocated": { + "bytes": [ + 22, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "provenance": { + "ptrs": [] + }, + "align": 8, + "mutability": "Mut" + } + }, + "ty": 26, + "id": 11 + } + } + } + } + ] + }, + "span": 53 + }, + { + "kind": { + "Assign": [ + { + "local": 3, + "projection": [] + }, + { + "Use": { + "Constant": { + "span": 54, + "user_ty": null, + "const_": { + "kind": { + "Allocated": { + "bytes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "provenance": { + "ptrs": [] + }, + "align": 8, + "mutability": "Mut" + } + }, + "ty": 26, + "id": 12 + } + } + } + } + ] + }, + "span": 54 + } + ], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 50, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 25, + "id": 9 + } + } + }, + "args": [ + { + "Copy": { + "local": 1, + "projection": [] + } + }, + { + "Copy": { + "local": 2, + "projection": [] + } + }, + { + "Copy": { + "local": 3, + "projection": [] + } + } + ], + "destination": { + "local": 4, + "projection": [] + }, + "target": 1, + "unwind": "Continue" + } + }, + "span": 51 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 5, + "projection": [] + }, + { + "BinaryOp": [ + "Ge", + { + "Copy": { + "local": 4, + "projection": [] + } + }, + { + "Copy": { + "local": 1, + "projection": [] + } + } + ] + } + ] + }, + "span": 55 + } + ], + "terminator": { + "kind": { + "SwitchInt": { + "discr": { + "Move": { + "local": 5, + "projection": [] + } + }, + "targets": { + "branches": [ + [ + 0, + 7 + ] + ], + "otherwise": 2 + } + } + }, + "span": 55 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 6, + "projection": [] + }, + { + "BinaryOp": [ + "Ge", + { + "Copy": { + "local": 4, + "projection": [] + } + }, + { + "Copy": { + "local": 2, + "projection": [] + } + } + ] + } + ] + }, + "span": 56 + } + ], + "terminator": { + "kind": { + "SwitchInt": { + "discr": { + "Move": { + "local": 6, + "projection": [] + } + }, + "targets": { + "branches": [ + [ + 0, + 7 + ] + ], + "otherwise": 3 + } + } + }, + "span": 56 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 7, + "projection": [] + }, + { + "BinaryOp": [ + "Ge", + { + "Copy": { + "local": 4, + "projection": [] + } + }, + { + "Copy": { + "local": 3, + "projection": [] + } + } + ] + } + ] + }, + "span": 57 + } + ], + "terminator": { + "kind": { + "SwitchInt": { + "discr": { + "Move": { + "local": 7, + "projection": [] + } + }, + "targets": { + "branches": [ + [ + 0, + 7 + ] + ], + "otherwise": 4 + } + } + }, + "span": 57 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 8, + "projection": [] + }, + { + "BinaryOp": [ + "Eq", + { + "Copy": { + "local": 4, + "projection": [] + } + }, + { + "Copy": { + "local": 1, + "projection": [] + } + } + ] + } + ] + }, + "span": 58 + } + ], + "terminator": { + "kind": { + "SwitchInt": { + "discr": { + "Move": { + "local": 8, + "projection": [] + } + }, + "targets": { + "branches": [ + [ + 0, + 5 + ] + ], + "otherwise": 8 + } + } + }, + "span": 58 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 9, + "projection": [] + }, + { + "BinaryOp": [ + "Eq", + { + "Copy": { + "local": 4, + "projection": [] + } + }, + { + "Copy": { + "local": 2, + "projection": [] + } + } + ] + } + ] + }, + "span": 59 + } + ], + "terminator": { + "kind": { + "SwitchInt": { + "discr": { + "Move": { + "local": 9, + "projection": [] + } + }, + "targets": { + "branches": [ + [ + 0, + 6 + ] + ], + "otherwise": 8 + } + } + }, + "span": 59 + } + }, + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 10, + "projection": [] + }, + { + "BinaryOp": [ + "Eq", + { + "Copy": { + "local": 4, + "projection": [] + } + }, + { + "Copy": { + "local": 3, + "projection": [] + } + } + ] + } + ] + }, + "span": 60 + } + ], + "terminator": { + "kind": { + "SwitchInt": { + "discr": { + "Move": { + "local": 10, + "projection": [] + } + }, + "targets": { + "branches": [ + [ + 0, + 7 + ] + ], + "otherwise": 8 + } + } + }, + "span": 60 + } + }, + { + "statements": [], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 61, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 27, + "id": 13 + } + } + }, + "args": [ + { + "Constant": { + "span": 32, + "user_ty": null, + "const_": { + "kind": { + "Allocated": { + "bytes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 110, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "provenance": { + "ptrs": [ + [ + 0, + 0 + ] + ] + }, + "align": 8, + "mutability": "Mut" + } + }, + "ty": 28, + "id": 14 + } + } + } + ], + "destination": { + "local": 11, + "projection": [] + }, + "target": null, + "unwind": "Continue" + } + }, + "span": 61 + } + }, + { + "statements": [], + "terminator": { + "kind": "Return", + "span": 62 + } + } + ], + "locals": [ + { + "ty": 1, + "span": 63, + "mutability": "Mut" + }, + { + "ty": 26, + "span": 64, + "mutability": "Not" + }, + { + "ty": 26, + "span": 65, + "mutability": "Not" + }, + { + "ty": 26, + "span": 66, + "mutability": "Not" + }, + { + "ty": 26, + "span": 67, + "mutability": "Not" + }, + { + "ty": 29, + "span": 55, + "mutability": "Mut" + }, + { + "ty": 29, + "span": 56, + "mutability": "Mut" + }, + { + "ty": 29, + "span": 57, + "mutability": "Mut" + }, + { + "ty": 29, + "span": 58, + "mutability": "Mut" + }, + { + "ty": 29, + "span": 59, + "mutability": "Mut" + }, + { + "ty": 29, + "span": 60, + "mutability": "Mut" + }, + { + "ty": 30, + "span": 61, + "mutability": "Mut" + } + ], + "arg_count": 0, + "var_debug_info": [ + { + "name": "a", + "source_info": { + "span": 64, + "scope": 1 + }, + "composite": null, + "value": { + "Place": { + "local": 1, + "projection": [] + } + }, + "argument_index": null + }, + { + "name": "b", + "source_info": { + "span": 65, + "scope": 2 + }, + "composite": null, + "value": { + "Place": { + "local": 2, + "projection": [] + } + }, + "argument_index": null + }, + { + "name": "c", + "source_info": { + "span": 66, + "scope": 3 + }, + "composite": null, + "value": { + "Place": { + "local": 3, + "projection": [] + } + }, + "argument_index": null + }, + { + "name": "result", + "source_info": { + "span": 67, + "scope": 4 + }, + "composite": null, + "value": { + "Place": { + "local": 4, + "projection": [] + } + }, + "argument_index": null + } + ], + "spread_arg": null, + "span": 68 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h3491d8bffa495004E", + "mono_item_kind": { + "MonoItemFn": { + "name": "std::sys::backtrace::__rust_begin_short_backtrace::", + "id": 2, + "body": { + "blocks": [ + { + "statements": [], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 31, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 19, + "id": 3 + } + } + }, + "args": [ + { + "Move": { + "local": 1, + "projection": [] + } + }, + { + "Constant": { + "span": 32, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 1, + "id": 4 + } + } + } + ], + "destination": { + "local": 0, + "projection": [] + }, + "target": 1, + "unwind": "Continue" + } + }, + "span": 33 + } + }, + { + "statements": [], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 34, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 20, + "id": 5 + } + } + }, + "args": [ + { + "Constant": { + "span": 32, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 1, + "id": 4 + } + } + } + ], + "destination": { + "local": 2, + "projection": [] + }, + "target": 2, + "unwind": "Unreachable" + } + }, + "span": 35 + } + }, + { + "statements": [], + "terminator": { + "kind": "Return", + "span": 36 + } + } + ], + "locals": [ + { + "ty": 1, + "span": 37, + "mutability": "Mut" + }, + { + "ty": 7, + "span": 38, + "mutability": "Not" + }, + { + "ty": 1, + "span": 39, + "mutability": "Not" + } + ], + "arg_count": 1, + "var_debug_info": [ + { + "name": "f", + "source_info": { + "span": 38, + "scope": 0 + }, + "composite": null, + "value": { + "Place": { + "local": 1, + "projection": [] + } + }, + "argument_index": 1 + }, + { + "name": "result", + "source_info": { + "span": 40, + "scope": 1 + }, + "composite": null, + "value": { + "Place": { + "local": 0, + "projection": [] + } + }, + "argument_index": null + }, + { + "name": "dummy", + "source_info": { + "span": 41, + "scope": 2 + }, + "composite": null, + "value": { + "Const": { + "span": 32, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 1, + "id": 4 + } + } + }, + "argument_index": 1 + } + ], + "spread_arg": null, + "span": 42 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17hd99c80c4d0898bdeE", + "mono_item_kind": { + "MonoItemFn": { + "name": "std::ptr::drop_in_place::<{closure@std::rt::lang_start<()>::{closure#0}}>", + "id": 4, + "body": { + "blocks": [ + { + "statements": [], + "terminator": { + "kind": "Return", + "span": 44 + } + } + ], + "locals": [ + { + "ty": 1, + "span": 44, + "mutability": "Mut" + }, + { + "ty": 22, + "span": 44, + "mutability": "Not" + } + ], + "arg_count": 1, + "var_debug_info": [], + "spread_arg": null, + "span": 44 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h739869090782dad0E", + "mono_item_kind": { + "MonoItemFn": { + "name": "<{closure@std::rt::lang_start<()>::{closure#0}} as std::ops::FnOnce<()>>::call_once", + "id": 3, + "body": { + "blocks": [ + { + "statements": [], + "terminator": { + "kind": { + "Call": { + "func": { + "Constant": { + "span": 43, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 21, + "id": 6 + } + } + }, + "args": [ + { + "Move": { + "local": 1, + "projection": [ + "Deref" + ] + } + }, + { + "Move": { + "local": 2, + "projection": [] + } + } + ], + "destination": { + "local": 0, + "projection": [] + }, + "target": 1, + "unwind": "Continue" + } + }, + "span": 43 + } + }, + { + "statements": [], + "terminator": { + "kind": "Return", + "span": 43 + } + } + ], + "locals": [ + { + "ty": 16, + "span": 43, + "mutability": "Mut" + }, + { + "ty": 22, + "span": 43, + "mutability": "Not" + }, + { + "ty": 1, + "span": 43, + "mutability": "Not" + } + ], + "arg_count": 2, + "var_debug_info": [], + "spread_arg": 2, + "span": 43 + } + } + }, + "details": null + }, + { + "symbol_name": "_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17hf5d9ff8f37d5cc66E", + "mono_item_kind": { + "MonoItemFn": { + "name": "<() as std::process::Termination>::report", + "id": 5, + "body": { + "blocks": [ + { + "statements": [ + { + "kind": { + "Assign": [ + { + "local": 0, + "projection": [] + }, + { + "Use": { + "Constant": { + "span": 46, + "user_ty": null, + "const_": { + "kind": { + "Allocated": { + "bytes": [ + 0 + ], + "provenance": { + "ptrs": [] + }, + "align": 1, + "mutability": "Mut" + } + }, + "ty": 17, + "id": 8 + } + } + } + } + ] + }, + "span": 46 + } + ], + "terminator": { + "kind": "Return", + "span": 45 + } + } + ], + "locals": [ + { + "ty": 17, + "span": 47, + "mutability": "Mut" + }, + { + "ty": 1, + "span": 48, + "mutability": "Not" + } + ], + "arg_count": 1, + "var_debug_info": [ + { + "name": "self", + "source_info": { + "span": 48, + "scope": 0 + }, + "composite": null, + "value": { + "Const": { + "span": 32, + "user_ty": null, + "const_": { + "kind": "ZeroSized", + "ty": 1, + "id": 4 + } + } + }, + "argument_index": 1 + } + ], + "spread_arg": null, + "span": 49 + } + } + }, + "details": null + } + ], + "types": [ + [ + 6, + { + "RigidTy": { + "Int": "Isize" + } + } + ], + [ + 2, + { + "RigidTy": { + "Int": "I8" + } + } + ], + [ + 16, + { + "RigidTy": { + "Int": "I32" + } + } + ], + [ + 9, + { + "RigidTy": { + "Uint": "U8" + } + } + ], + [ + 29, + { + "RigidTy": "Bool" + } + ], + [ + 26, + { + "RigidTy": { + "Uint": "Usize" + } + } + ] + ], + "debug": null +} diff --git a/kmir-proofs/maximum-example-proof/maximum-spec.k b/kmir-proofs/maximum-example-proof/maximum-spec.k new file mode 100644 index 0000000000000..ee506eb8f2e82 --- /dev/null +++ b/kmir-proofs/maximum-example-proof/maximum-spec.k @@ -0,0 +1,73 @@ +module MAXIMUM-SPEC + imports KMIR + + claim [maximum-spec]: + + ( // LHS, start state + #execTerminator ( + terminator (... + kind: terminatorKindCall (... + func: operandConstant ( + constOperand (... + span: span ( 50 ) , + userTy: noUserTypeAnnotationIndex , + const: mirConst (... + kind: constantKindZeroSized , + ty: ty ( 25 ) , // <- this is the reference to `maximum` + id: mirConstId ( 9 ) + ) + ) + ) , + args: + operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) + operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) + operandCopy ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ), + destination: DEST, + target: noBasicBlockIdx, + // forcing the proof to stop because there is no caller to return to + unwind: _ + ), + span: _ + ) + ) + => + // RHS: target + // #execTerminator ( terminator (... kind: terminatorKindReturn , span: ?_ ) ) + #EndProgram + ) + ~> .K + + _ + _ => ty ( 25 ) + + _ => ?_ + _ => ?_ + _ => DEST + _ => noBasicBlockIdx + _ => ?_ + + ListItem ( _ ) + ListItem ( typedValue ( Integer ( A , 64 , false ) , ty ( 26 ) , _ ) ) + ListItem ( typedValue ( Integer ( B , 64 , false ) , ty ( 26 ) , _ ) ) + ListItem ( typedValue ( Integer ( C , 64 , false ) , ty ( 26 ) , _ ) ) + // _ // if we keep this we need a lemma for list size predicate simplification + => + ListItem ( typedValue ( Integer ( ?RESULT, 64, false), ty ( 26 ) , ?_ )) + ?_ + + + _ => ?_ + + ty ( 25 ) |-> monoItemFn (... name: symbol ( "maximum" ) , id: defId ( 7 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 5 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpLt , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 69 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 69 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 71 ) ) .Statements , terminator: terminator (... kind: terminatorKindGoto (... target: basicBlockIdx ( 3 ) ) , span: span ( 70 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 72 ) ) .Statements , terminator: terminator (... kind: terminatorKindGoto (... target: basicBlockIdx ( 3 ) ) , span: span ( 70 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 7 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 74 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 6 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpLt , operandMove ( place (... local: local ( 7 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 73 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 6 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 5 ) ) .Branches , otherwise: basicBlockIdx ( 4 ) ) ) , span: span ( 73 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 76 ) ) .Statements , terminator: terminator (... kind: terminatorKindGoto (... target: basicBlockIdx ( 6 ) ) , span: span ( 75 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 77 ) ) .Statements , terminator: terminator (... kind: terminatorKindGoto (... target: basicBlockIdx ( 6 ) ) , span: span ( 75 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 78 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 26 ) , span: span ( 79 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 26 ) , span: span ( 80 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 81 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 82 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 83 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 29 ) , span: span ( 69 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 29 ) , span: span ( 73 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 26 ) , span: span ( 74 ) , mut: mutabilityMut ) .LocalDecls , argCount: 3 , varDebugInfo: varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 80 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 81 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "c" ) , sourceInfo: sourceInfo (... span: span ( 82 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 3 ) ) varDebugInfo (... name: symbol ( "max_ab" ) , sourceInfo: sourceInfo (... span: span ( 83 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 84 ) ) ) ) + + + requires // invariant of the `Integer` constructor + 0 <=Int A + andBool A = a && result >= b && result >= c + && (result == a || result == b || result == c ) ); +} + +fn maximum(a: usize, b: usize, c: usize) -> usize { + // max(a, max(b, c)) + let max_ab = if a < b {b} else {a}; + if max_ab < c {c} else {max_ab} +} +``` + +Notice in this case that `a`, `b`, and `c` are concrete, fixed values. To turn the parameters of `maximum` into symbolic variables, we can obtain the representation of the function call to `maximum` executed using KMIR and then replace the concrete values of these variables with symbolic values. Furthermore, the assertion specified in the code can be manually translated as a requirement that should be met by the symbolic variables, meaning that any value that they can assume must respect the conditions contained in the specification. Following this approach, we can utilize KMIR to give us formal proof that, for any valid `isize` input, the maximum value among the three parameters will be returned. + +Information on how the specification was created can be found in the longer [description of `maximum-proof`](https://github.com/runtimeverification/mir-semantics/tree/sample-challenge-11-proofs/rust-verification-proofs/maximum-proof). + +To run this proof in your terminal from this folder, execute: + +```Bash +cd maximum-proof +poetry -C ../../kmir/ run -- kmir prove run $PWD/maximum-spec.k --proof-dir $PWD/proof +``` + +## Proof 2: Proving Unsafe Arithmetic Operations + +The proofs in this section concern a section of the challenge of securing [Safety of Methods for Numeric Primitive Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) of the Verify Rust Standard Library Effort. Here, we implement proof of concepts of how KMIR can be used to prove the following unsafe methods according to their undefined behaviors: `unchecked_add`, `unchecked_sub`, `unchecked_mul`, `unchecked_shl`, `unchecked_shr`, and `unchecked_neg`. + +For these functions, the proofs were carried out using variables of the `i16` integer type, and the criteria for triggering undefined behaviors for these methods were obtained in the [i16 type documentation page](https://doc.rust-lang.org/std/primitive.i16.html). + +To obtain the specifications that prove the presence/absence of undefined behavior for these functions, analogous processes to the ones discussed in [Proof 1](#proof-1-proving-a-maximum-finding-function-that-only-uses-lower-than) were performed. + +For an illustration of how we specify the requirements of the proof, which, in this case, are the assertions that would help us detect the presence/absence of undefined behavior in the unsafe methods, let's explore how we can prove safety conditions for the `unchecked_add` operation. Consider the following function: + +https://github.com/runtimeverification/mir-semantics/blob/e2de329d009cde25f505819d7c8c9815571db9e7/rust-verification-proofs/unchecked_add/unchecked-add.rs#L11-L14 + +`unchecked_op` is a function that receives two `i16` arguments and executes an `unchecked_add` of the first parameter by the second, returning an `i16` value resulting from this operation. According to the [documentation of the unchecked_add function for the i16 primitive type](https://doc.rust-lang.org/std/primitive.i16.html#method.unchecked_add), considering the safety of this function "This results in undefined behavior when `self + rhs > i16::MAX or self + rhs < i16::MIN`, i.e. when `checked_add` would return `None`". By the process further disclosed in Proof 1, we can obtain a valid representation of a function call for `unchecked_op` and modify the variable values to be symbolic. The next step is to define the conditions these values should meet to verify safety conditions elaborated for `unchecked_add`. To this goal, see the following code snippet: + +https://github.com/runtimeverification/mir-semantics/blob/e2de329d009cde25f505819d7c8c9815571db9e7/rust-verification-proofs/unchecked_add/unchecked-op-spec.k#L66-L73 + +The parameters for `unchecked_add` in this specification for KMIR are represented as A and B, which now are symbolic values. To specify the goal of our verification process, we implemented the above code snippet into the specification, which adds a requirement to the execution of our symbolic execution engine. In other words, our proof will only be successful if the specified requirements above are respected. + +In this `requires` clause, first, we use the semantics of K to specify A and B's boundaries, as `i16`s: `0 -Int (1 < + ( // LHS, start state + #execTerminator ( + terminator (... + kind: terminatorKindCall (... + func: operandConstant ( + constOperand (... + span: span ( 76 ) , + userTy: noUserTypeAnnotationIndex , + const: mirConst (... + kind: constantKindZeroSized , + ty: ty ( 32 ) , // <- this is the reference to `unchecked_op` + id: mirConstId ( 12 ) + ) + ) + ) , + args: + operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) + operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ), + destination: DEST, + target: noBasicBlockIdx, + // forcing the proof to stop because there is no caller to return to + unwind: _ + ), + span: _ + ) + ) + => + // RHS: target + // #execTerminator ( terminator (... kind: terminatorKindReturn , span: ?_ ) ) + #EndProgram + ) + ~> .K + + _ + _ => ty ( 32 ) + + _ => ?_ + _ => ?_ + _ => DEST + _ => noBasicBlockIdx + _ => ?_ + + ListItem ( _ ) + ListItem ( typedValue ( Integer ( A , 16 , true ) , ty ( 23 ) , _ ) ) + ListItem ( typedValue ( Integer ( B , 16 , true ) , ty ( 23 ) , _ ) ) + // _ // if we keep this we need a lemma for list size predicate simplification + => + ListItem ( typedValue ( Integer ( ?RESULT, 16, true), ty ( 23 ) , ?_ )) + ?_ + + + _ => ?_ + + ( + ty ( 32 ) |-> monoItemFn (... name: symbol ( "unchecked_op" ) , id: defId ( 9 ) , body: someBody ( body (... blocks: basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 93 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 34 ) , id: mirConstId ( 19 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 0 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 1 ) ) , unwind: unwindActionContinue ) , span: span ( 94 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 95 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 96 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 97 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 98 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 97 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 98 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "unchecked_sum" ) , sourceInfo: sourceInfo (... span: span ( 99 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 0 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 100 ) ) ) ) + ty ( 34 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_add" ) , id: defId ( 3 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 3 ) ) , span: span ( 43 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 3 ) , projection: .ProjectionElems ) , rvalue: rvalueNullaryOp ( nullOpUbChecks , ty ( 21 ) ) ) , span: span ( 44 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 43 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 45 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 22 ) , id: mirConstId ( 6 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 4 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 2 ) ) , unwind: unwindActionUnreachable ) , span: span ( 46 ) ) ) basicBlock (... statements: statement (... kind: statementKindStorageDead ( local ( 3 ) ) , span: span ( 48 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpAddUnchecked , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 49 ) ) .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 47 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 50 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 51 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 52 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 43 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 1 ) , span: span ( 46 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 51 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 52 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) .VarDebugInfos , spreadArg: noLocal , span: span ( 53 ) ) ) ) + ty ( 22 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_add::precondition_check" ) , id: defId ( 4 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 4 ) ) , span: span ( 55 ) ) statement (... kind: statementKindStorageLive ( local ( 6 ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 6 ) , projection: .ProjectionElems ) , rvalue: rvalueCheckedBinaryOp ( binOpAdd , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 0 ) , ty ( 23 ) ) .ProjectionElems ) ) ) ) , span: span ( 57 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 5 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 1 ) , ty ( 21 ) ) .ProjectionElems ) ) ) ) , span: span ( 58 ) ) statement (... kind: statementKindStorageDead ( local ( 6 ) ) , span: span ( 59 ) ) statement (... kind: statementKindStorageDead ( local ( 4 ) ) , span: span ( 55 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandCopy ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 54 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 60 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 24 ) , id: mirConstId ( 7 ) ) ) ) , args: operandConstant ( constOperand (... span: span ( 61 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindAllocated ( allocation (... bytes: b"\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00" , provenance: provenanceMap (... ptrs: provenanceMapEntry (... provSize: 0 , allocId: allocId ( 0 ) ) .ProvenanceMapEntries ) , align: align ( 8 ) , mutability: mutabilityMut ) ) , ty: ty ( 25 ) , id: mirConstId ( 8 ) ) ) ) .Operands , destination: place (... local: local ( 3 ) , projection: .ProjectionElems ) , target: noBasicBlockIdx , unwind: unwindActionUnreachable ) , span: span ( 62 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 63 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 1 ) , span: span ( 64 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 62 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 57 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 58 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 27 ) , span: span ( 56 ) , mut: mutabilityMut ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "lhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 66 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 67 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 57 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 58 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 68 ) ) ) ) + ) + + + requires // i16 invariants + 0 -Int (1 < i16::MIN)); +} + +fn unchecked_op(a: i16, b: i16) -> i16 { + let unchecked_res = unsafe { a.unchecked_add(b) }; + unchecked_res +} diff --git a/kmir-proofs/unchecked_arithmetic/unchecked-mul-spec.k b/kmir-proofs/unchecked_arithmetic/unchecked-mul-spec.k new file mode 100644 index 0000000000000..13b5a7d99afad --- /dev/null +++ b/kmir-proofs/unchecked_arithmetic/unchecked-mul-spec.k @@ -0,0 +1,73 @@ +module UNCHECKED-MUL-SPEC + imports KMIR + + claim [unchecked-mul-spec]: + + ( // LHS, start state + #execTerminator ( + terminator (... + kind: terminatorKindCall (... + func: operandConstant ( + constOperand (... + span: span ( 76 ) , + userTy: noUserTypeAnnotationIndex , + const: mirConst (... + kind: constantKindZeroSized , + ty: ty ( 32 ) , // <- this is the reference to `unchecked_op` + id: mirConstId ( 12 ) + ) + ) + ) , + args: + operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) + operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ), + destination: DEST, + target: noBasicBlockIdx, + // forcing the proof to stop because there is no caller to return to + unwind: _ + ), + span: _ + ) + ) + => + // RHS: target + // #execTerminator ( terminator (... kind: terminatorKindReturn , span: ?_ ) ) + #EndProgram + ) + ~> .K + + _ + _ => ty ( 32 ) + + _ => ?_ + _ => ?_ + _ => DEST + _ => noBasicBlockIdx + _ => ?_ + + ListItem ( _ ) + ListItem ( typedValue ( Integer ( A , 16 , true ) , ty ( 23 ) , _ ) ) + ListItem ( typedValue ( Integer ( B , 16 , true ) , ty ( 23 ) , _ ) ) + // _ // if we keep this we need a lemma for list size predicate simplification + => + ListItem ( typedValue ( Integer ( ?RESULT, 16, true), ty ( 23 ) , ?_ )) + ?_ + + + _ => ?_ + + ( + ty ( 32 ) |-> monoItemFn (... name: symbol ( "unchecked_op" ) , id: defId ( 9 ) , body: someBody ( body (... blocks: basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 93 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 34 ) , id: mirConstId ( 19 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 0 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 1 ) ) , unwind: unwindActionContinue ) , span: span ( 94 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 95 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 96 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 97 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 98 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 97 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 98 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "unchecked_res" ) , sourceInfo: sourceInfo (... span: span ( 99 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 0 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 100 ) ) ) ) + ty ( 34 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_mul" ) , id: defId ( 3 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 3 ) ) , span: span ( 43 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 3 ) , projection: .ProjectionElems ) , rvalue: rvalueNullaryOp ( nullOpUbChecks , ty ( 21 ) ) ) , span: span ( 44 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 43 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 45 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 22 ) , id: mirConstId ( 6 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 4 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 2 ) ) , unwind: unwindActionUnreachable ) , span: span ( 46 ) ) ) basicBlock (... statements: statement (... kind: statementKindStorageDead ( local ( 3 ) ) , span: span ( 48 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpMulUnchecked , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 49 ) ) .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 47 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 50 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 51 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 52 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 43 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 1 ) , span: span ( 46 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 51 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 52 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) .VarDebugInfos , spreadArg: noLocal , span: span ( 53 ) ) ) ) + ty ( 22 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_mul::precondition_check" ) , id: defId ( 4 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 4 ) ) , span: span ( 55 ) ) statement (... kind: statementKindStorageLive ( local ( 6 ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 6 ) , projection: .ProjectionElems ) , rvalue: rvalueCheckedBinaryOp ( binOpMul , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 0 ) , ty ( 23 ) ) .ProjectionElems ) ) ) ) , span: span ( 57 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 5 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 1 ) , ty ( 21 ) ) .ProjectionElems ) ) ) ) , span: span ( 58 ) ) statement (... kind: statementKindStorageDead ( local ( 6 ) ) , span: span ( 59 ) ) statement (... kind: statementKindStorageDead ( local ( 4 ) ) , span: span ( 55 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandCopy ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 54 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 60 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 24 ) , id: mirConstId ( 7 ) ) ) ) , args: operandConstant ( constOperand (... span: span ( 61 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindAllocated ( allocation (... bytes: b"\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00" , provenance: provenanceMap (... ptrs: provenanceMapEntry (... provSize: 0 , allocId: allocId ( 0 ) ) .ProvenanceMapEntries ) , align: align ( 8 ) , mutability: mutabilityMut ) ) , ty: ty ( 25 ) , id: mirConstId ( 8 ) ) ) ) .Operands , destination: place (... local: local ( 3 ) , projection: .ProjectionElems ) , target: noBasicBlockIdx , unwind: unwindActionUnreachable ) , span: span ( 62 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 63 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 1 ) , span: span ( 64 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 62 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 57 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 58 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 27 ) , span: span ( 56 ) , mut: mutabilityMut ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "lhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 66 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 67 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 57 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 58 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 68 ) ) ) ) + ) + + requires // i16 invariants + 0 -Int (1 < i16::MAX) && (a * b < i16::MIN)); +} + +fn unchecked_op(a: i16, b: i16) -> i16 { + let unchecked_res = unsafe { a.unchecked_mul(b) }; + unchecked_res +} diff --git a/kmir-proofs/unchecked_arithmetic/unchecked-sub-spec.k b/kmir-proofs/unchecked_arithmetic/unchecked-sub-spec.k new file mode 100644 index 0000000000000..f0597337dea36 --- /dev/null +++ b/kmir-proofs/unchecked_arithmetic/unchecked-sub-spec.k @@ -0,0 +1,73 @@ +module UNCHECKED-SUB-SPEC + imports KMIR + + claim [unchecked-sub-spec]: + + ( // LHS, start state + #execTerminator ( + terminator (... + kind: terminatorKindCall (... + func: operandConstant ( + constOperand (... + span: span ( 76 ) , + userTy: noUserTypeAnnotationIndex , + const: mirConst (... + kind: constantKindZeroSized , + ty: ty ( 32 ) , // <- this is the reference to `unchecked_op` + id: mirConstId ( 12 ) + ) + ) + ) , + args: + operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) + operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ), + destination: DEST, + target: noBasicBlockIdx, + // forcing the proof to stop because there is no caller to return to + unwind: _ + ), + span: _ + ) + ) + => + // RHS: target + // #execTerminator ( terminator (... kind: terminatorKindReturn , span: ?_ ) ) + #EndProgram + ) + ~> .K + + _ + _ => ty ( 32 ) + + _ => ?_ + _ => ?_ + _ => DEST + _ => noBasicBlockIdx + _ => ?_ + + ListItem ( _ ) + ListItem ( typedValue ( Integer ( A , 16 , true ) , ty ( 23 ) , _ ) ) + ListItem ( typedValue ( Integer ( B , 16 , true ) , ty ( 23 ) , _ ) ) + // _ // if we keep this we need a lemma for list size predicate simplification + => + ListItem ( typedValue ( Integer ( ?RESULT, 16, true), ty ( 23 ) , ?_ )) + ?_ + + + _ => ?_ + + ( + ty ( 32 ) |-> monoItemFn (... name: symbol ( "unchecked_op" ) , id: defId ( 9 ) , body: someBody ( body (... blocks: basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 93 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 34 ) , id: mirConstId ( 19 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 0 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 1 ) ) , unwind: unwindActionContinue ) , span: span ( 94 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 95 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 96 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 97 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 98 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 97 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 98 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "unchecked_sum" ) , sourceInfo: sourceInfo (... span: span ( 99 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 0 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 100 ) ) ) ) + ty ( 34 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_sub" ) , id: defId ( 3 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 3 ) ) , span: span ( 43 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 3 ) , projection: .ProjectionElems ) , rvalue: rvalueNullaryOp ( nullOpUbChecks , ty ( 21 ) ) ) , span: span ( 44 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 43 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 45 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 22 ) , id: mirConstId ( 6 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 4 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 2 ) ) , unwind: unwindActionUnreachable ) , span: span ( 46 ) ) ) basicBlock (... statements: statement (... kind: statementKindStorageDead ( local ( 3 ) ) , span: span ( 48 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpSubUnchecked , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 49 ) ) .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 47 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 50 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 51 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 52 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 43 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 1 ) , span: span ( 46 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 51 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 52 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) .VarDebugInfos , spreadArg: noLocal , span: span ( 53 ) ) ) ) + ty ( 22 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_sub::precondition_check" ) , id: defId ( 4 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 4 ) ) , span: span ( 55 ) ) statement (... kind: statementKindStorageLive ( local ( 6 ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 6 ) , projection: .ProjectionElems ) , rvalue: rvalueCheckedBinaryOp ( binOpSub , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 0 ) , ty ( 23 ) ) .ProjectionElems ) ) ) ) , span: span ( 57 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 5 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 1 ) , ty ( 21 ) ) .ProjectionElems ) ) ) ) , span: span ( 58 ) ) statement (... kind: statementKindStorageDead ( local ( 6 ) ) , span: span ( 59 ) ) statement (... kind: statementKindStorageDead ( local ( 4 ) ) , span: span ( 55 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandCopy ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 54 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 60 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 24 ) , id: mirConstId ( 7 ) ) ) ) , args: operandConstant ( constOperand (... span: span ( 61 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindAllocated ( allocation (... bytes: b"\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00" , provenance: provenanceMap (... ptrs: provenanceMapEntry (... provSize: 0 , allocId: allocId ( 0 ) ) .ProvenanceMapEntries ) , align: align ( 8 ) , mutability: mutabilityMut ) ) , ty: ty ( 25 ) , id: mirConstId ( 8 ) ) ) ) .Operands , destination: place (... local: local ( 3 ) , projection: .ProjectionElems ) , target: noBasicBlockIdx , unwind: unwindActionUnreachable ) , span: span ( 62 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 63 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 1 ) , span: span ( 64 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 62 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 57 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 58 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 27 ) , span: span ( 56 ) , mut: mutabilityMut ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "lhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 66 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 67 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 57 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 58 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 68 ) ) ) ) + ) + + requires // i16 invariants + 0 -Int (1 < i16::MAX) && (a - b < i16::MIN)); +} + +fn unchecked_op(a: i16, b: i16) -> i16 { + let unchecked_res = unsafe { a.unchecked_sub(b) }; + unchecked_res +} From 4082f8a96b55d6bf16007ac08ee6be0d1b72385c Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Tue, 1 Apr 2025 16:30:52 +1100 Subject: [PATCH 03/29] remove stray quotes --- .github/workflows/kmir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index 9e7b8a3dc8435..6e546671dc47b 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -30,7 +30,7 @@ jobs: - name: Run KMIR Verification run: | cd kmir-proofs - for k_file in */*-spec.k"; do + for k_file in */*-spec.k; do echo "Running ${k_file}" kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs done From 572425b894f8c8e3cb8a7b0234b7e6d9d9351503 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Tue, 1 Apr 2025 17:14:07 +1100 Subject: [PATCH 04/29] New README.md for the KMIR proof top directory --- kmir-proofs/README.md | 87 ++++++++++++++++++++ kmir-proofs/unchecked_arithmetic/README.md | 95 ---------------------- 2 files changed, 87 insertions(+), 95 deletions(-) create mode 100644 kmir-proofs/README.md delete mode 100644 kmir-proofs/unchecked_arithmetic/README.md diff --git a/kmir-proofs/README.md b/kmir-proofs/README.md new file mode 100644 index 0000000000000..cd32ecc5d7a28 --- /dev/null +++ b/kmir-proofs/README.md @@ -0,0 +1,87 @@ +# Formal Rust Code Verification Using KMIR + +This directory contains a collection of programs and specifications to illustrate how KMIR can validate the properties of Rust programs and standard library functions. + + +## Setup + +KMIR verification can either be run from [docker images provided under `runtimeverificationinc/kmir`](https://hub.docker.com/r/runtimeverificationinc/kmir), or using a local installation of [`mir-semantics`](https://github.com/runtimeverification/mir-semantics/) with its dependency [`stable-mir-json`](https://github.com/runtimeverification/stable-mir-json). The installation is described in the repository's `README.md` files. + +## Example Proof: Proving a Maximum Finding Function That only Uses `lower-than` + +Considering a function that receives three integer arguments, this function should return the highest value among them. Assertions can be used to enforce this condition, and an example code that tests this function can be seen below: + +```Rust +fn main() { + + let a:usize = 42; + let b:usize = 43; + let c:usize = 0; + + let result = maximum(a, b, c); + + assert!(result >= a && result >= b && result >= c + && (result == a || result == b || result == c ) ); +} + +fn maximum(a: usize, b: usize, c: usize) -> usize { + // max(max(a,b), c) + let max_ab = if a < b {b} else {a}; + if max_ab < c {c} else {max_ab} +} +``` + +Notice in this case that `a`, `b`, and `c` are concrete, fixed values. To turn the parameters of `maximum` into symbolic variables, we can obtain the representation of the function call to `maximum` executed using KMIR and then replace the concrete values of these variables with symbolic values. Furthermore, the assertion specified in the code can be manually translated as a requirement that should be met by the symbolic variables, meaning that any value that they can assume must respect the conditions contained in the specification. Following this approach, we can utilize KMIR to give us formal proof that, for any valid `isize` input, the maximum value among the three parameters will be returned. + +Work on KMIR is in progress and the way a Rust program is turned into a K claim will be automated in the near future, but is currently a manual process described in the longer [description of `maximum-example-proof`](./maximum-example-proof/README.md). + +To run this proof in your terminal from this folder, execute: + +```sh +cd maximum-proof +kmir prove run $PWD/maximum-spec.k --proof-dir $PWD/proof +``` + +If option `--proof-dir` was used, the finished (or an unfinished) proof can be inspected using the following command: + +```sh +kmir prove view MAXIMUM-SPEC.maximum-spec --proof-dir $PWD/proof +``` + +## Example Proofs: Safety of Unsafe Arithmetic Operations + +The proofs in subdirectory `unchecked_arithmetic` concern a section of the challenge of securing [Safety of Methods for Numeric Primitive Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) of the Verify Rust Standard Library Effort. +The `*-spec.k` files set up a proof of concept of how KMIR can be used to prove unsafe methods according to their undefined behaviors. Proofs were set up using the same method as described for the `maximum-example-proof`. + +All K claim files follow the same pattern (illustrated using `unchecked_add` on `i16` as an example): + +1) For a given unsafe operation, a calling wrapper function `unchecked_op` is written and translated to Stable MIR + +```rust + fn unchecked_op(a: i16, b: i16) -> i16 { + let unchecked_res = unsafe { a.unchecked_add(b) }; + unchecked_res + } +``` + +2) A K configuration for a Rust program that calls this function with symbolic `i16` arguments `A` and `B` is constructed (currently in a manual fashion). The `i16` arguments are represented as `Integer(A, 16, true)`. + +3) According to the [documentation of the unchecked_add function for the i16 primitive type](https://doc.rust-lang.org/std/primitive.i16.html#method.unchecked_add), + +> This results in undefined behavior when `self + rhs > i16::MAX or self + rhs < i16::MIN`, i.e. when `checked_add` would return `None`" + + This safety condition is translated into a `requires` clause in the K claim. In addition, the invariants for `A`'s and `B`'s representation as `i16` can be assumed, giving: + +``` + requires // i16 invariants + 0 -Int (1 <= a && result >= b && result >= c - && (result == a || result == b || result == c ) ); -} - -fn maximum(a: usize, b: usize, c: usize) -> usize { - // max(a, max(b, c)) - let max_ab = if a < b {b} else {a}; - if max_ab < c {c} else {max_ab} -} -``` - -Notice in this case that `a`, `b`, and `c` are concrete, fixed values. To turn the parameters of `maximum` into symbolic variables, we can obtain the representation of the function call to `maximum` executed using KMIR and then replace the concrete values of these variables with symbolic values. Furthermore, the assertion specified in the code can be manually translated as a requirement that should be met by the symbolic variables, meaning that any value that they can assume must respect the conditions contained in the specification. Following this approach, we can utilize KMIR to give us formal proof that, for any valid `isize` input, the maximum value among the three parameters will be returned. - -Information on how the specification was created can be found in the longer [description of `maximum-proof`](https://github.com/runtimeverification/mir-semantics/tree/sample-challenge-11-proofs/rust-verification-proofs/maximum-proof). - -To run this proof in your terminal from this folder, execute: - -```Bash -cd maximum-proof -poetry -C ../../kmir/ run -- kmir prove run $PWD/maximum-spec.k --proof-dir $PWD/proof -``` - -## Proof 2: Proving Unsafe Arithmetic Operations - -The proofs in this section concern a section of the challenge of securing [Safety of Methods for Numeric Primitive Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) of the Verify Rust Standard Library Effort. Here, we implement proof of concepts of how KMIR can be used to prove the following unsafe methods according to their undefined behaviors: `unchecked_add`, `unchecked_sub`, `unchecked_mul`, `unchecked_shl`, `unchecked_shr`, and `unchecked_neg`. - -For these functions, the proofs were carried out using variables of the `i16` integer type, and the criteria for triggering undefined behaviors for these methods were obtained in the [i16 type documentation page](https://doc.rust-lang.org/std/primitive.i16.html). - -To obtain the specifications that prove the presence/absence of undefined behavior for these functions, analogous processes to the ones discussed in [Proof 1](#proof-1-proving-a-maximum-finding-function-that-only-uses-lower-than) were performed. - -For an illustration of how we specify the requirements of the proof, which, in this case, are the assertions that would help us detect the presence/absence of undefined behavior in the unsafe methods, let's explore how we can prove safety conditions for the `unchecked_add` operation. Consider the following function: - -https://github.com/runtimeverification/mir-semantics/blob/e2de329d009cde25f505819d7c8c9815571db9e7/rust-verification-proofs/unchecked_add/unchecked-add.rs#L11-L14 - -`unchecked_op` is a function that receives two `i16` arguments and executes an `unchecked_add` of the first parameter by the second, returning an `i16` value resulting from this operation. According to the [documentation of the unchecked_add function for the i16 primitive type](https://doc.rust-lang.org/std/primitive.i16.html#method.unchecked_add), considering the safety of this function "This results in undefined behavior when `self + rhs > i16::MAX or self + rhs < i16::MIN`, i.e. when `checked_add` would return `None`". By the process further disclosed in Proof 1, we can obtain a valid representation of a function call for `unchecked_op` and modify the variable values to be symbolic. The next step is to define the conditions these values should meet to verify safety conditions elaborated for `unchecked_add`. To this goal, see the following code snippet: - -https://github.com/runtimeverification/mir-semantics/blob/e2de329d009cde25f505819d7c8c9815571db9e7/rust-verification-proofs/unchecked_add/unchecked-op-spec.k#L66-L73 - -The parameters for `unchecked_add` in this specification for KMIR are represented as A and B, which now are symbolic values. To specify the goal of our verification process, we implemented the above code snippet into the specification, which adds a requirement to the execution of our symbolic execution engine. In other words, our proof will only be successful if the specified requirements above are respected. - -In this `requires` clause, first, we use the semantics of K to specify A and B's boundaries, as `i16`s: `0 -Int (1 < Date: Tue, 1 Apr 2025 17:22:36 +1100 Subject: [PATCH 05/29] Adjust README for maximum-example-proof --- kmir-proofs/maximum-example-proof/README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/kmir-proofs/maximum-example-proof/README.md b/kmir-proofs/maximum-example-proof/README.md index 8856fe01c87b5..3758ff94fbe20 100644 --- a/kmir-proofs/maximum-example-proof/README.md +++ b/kmir-proofs/maximum-example-proof/README.md @@ -6,7 +6,7 @@ fn main() { let a:usize = 42; - let b:usize = 22; + let b:usize = 43; let c:usize = 0; let result = maximum(a, b, c); @@ -33,15 +33,17 @@ In a future version, we will be able to start directly with the `maximum` functi ## Extracting Stable MIR for the program -Before we can run the program using the MIR semantics, we have to compile it with a special compiler to extract Stable MIR from it. This step differs a bit depending on whether the program has multiple crates, in our case it is just a simple `rustc` invocation. This creates `main-max-with-lt.smir.json`. (Run the below commmands from the `mir-semantics/rust-verification-proofs/maximum-proof/` directory). +Before we can run the program using the MIR semantics, we have to compile it with a special compiler to extract Stable MIR from it. This step differs a bit depending on whether the program has multiple crates, in our case it is just a simple invocation of the extraction tool. Assuming the tool is installed as `stable-mir-json` (as in the docker image provided): ```shell -cargo -Z unstable-options -C ../../deps/stable-mir-json/ run -- -Zno-codegen --out-dir $PWD $PWD/main-max-with-lt.rs +stable-mir-json -Zno-codegen --out-dir . main-max-with-lt.rs ``` -The Stable MIR for the program can also be rendered as a graph, using the `--dot` option. This creates `main-max-with-lt.smir.dot`. +This creates `main-max-with-lt.smir.json`. The `-Zno-codegen` and `--out-dir` options are passed through to the underlying `rustc` so no executable is generated. + +The Stable MIR for the program can also be rendered as a graph, using `--dot` as the first option. This creates `main-max-with-lt.smir.dot`. ```shell -cargo -Z unstable-options -C ../../deps/stable-mir-json/ run -- --dot -Zno-codegen --out-dir $PWD $PWD/main-max-with-lt.rs +stable-mir-json --dot -Zno-codegen --out-dir . main-max-with-lt.rs ``` ## Constructing the claim by executing `main` to certain points Through concrete execution of the parsed K program we can interrupt the execution after a given number of rewrite steps to inspect the intermediate state. This will help us with writing our claim manually until the process is automated. @@ -50,7 +52,7 @@ Through concrete execution of the parsed K program we can interrupt the executio The following command runs it and displays the resulting program state. ```shell - poetry -C ../../kmir/ run -- kmir run $PWD/main-max-with-lt.smir.json --depth 22 | less -S + kmir run main-max-with-lt.smir.json --depth 22 | less -S ``` - Arguments `a`, `b`, and `c` are initialised to `Integer`s as `locals[1]` to `locals[3]` - A `call` terminator calling function `ty(25)` is executed next (front of the `k` cell) @@ -60,7 +62,7 @@ Through concrete execution of the parsed K program we can interrupt the executio The following command runs it and displays the resulting program state. ```shell - poetry -C ../../kmir/ run -- kmir run $PWD/main-max-with-lt.smir.json --depth 92 | less -S + kmir run main-max-with-lt.smir.json --depth 92 | less -S ``` - The value `locals[0]` is now set to an `Integer`. This will be the target of our assertions. - A `return` terminator is executed next (front of the `k` cell), it will return `locals[0]` @@ -88,11 +90,11 @@ Alternatively, it is possible to construct a claim that the entire rest of the p ## Running the prover on the claim and viewing the proof Now that we have constructed claim, we can run use the KMIR verifier to perform symbollic execution, and can view the state of proof through the KMIR proof viewer. ```shell -poetry -C ../../kmir/ run -- kmir prove run $PWD/maximum-spec.k --proof-dir $PWD/proof +kmir prove run maximum-spec.k --proof-dir ./proof ``` The proof steps are saved in the `$PWD/proof` directory for later inspection using `kmir prove view`. This is especially important when the proof does _not_ succeed immediately. ```shell -poetry -C ../../kmir/ run -- kmir prove view MAXIMUM-SPEC.maximum-spec --proof-dir $PWD/proof +kmir prove view MAXIMUM-SPEC.maximum-spec --proof-dir ./proof ``` From 822f8ece37c7f4cafd194ddd37bb9a770b01cddd Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Tue, 1 Apr 2025 17:33:29 +1100 Subject: [PATCH 06/29] adjust steps in maximum proof, give an example of the configuration --- kmir-proofs/maximum-example-proof/README.md | 62 +++++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/kmir-proofs/maximum-example-proof/README.md b/kmir-proofs/maximum-example-proof/README.md index 3758ff94fbe20..ab22aab3765e0 100644 --- a/kmir-proofs/maximum-example-proof/README.md +++ b/kmir-proofs/maximum-example-proof/README.md @@ -48,21 +48,71 @@ stable-mir-json --dot -Zno-codegen --out-dir . main-max-with-lt.rs ## Constructing the claim by executing `main` to certain points Through concrete execution of the parsed K program we can interrupt the execution after a given number of rewrite steps to inspect the intermediate state. This will help us with writing our claim manually until the process is automated. -1. The program (`main`) reaches the call to `maximum` after 22 steps. +1. The program (`main`) reaches the call to `maximum` after 25 steps. The following command runs it and displays the resulting program state. ```shell - kmir run main-max-with-lt.smir.json --depth 22 | less -S + kmir run main-max-with-lt.smir.json --depth 25 | less -S ``` - Arguments `a`, `b`, and `c` are initialised to `Integer`s as `locals[1]` to `locals[3]` - A `call` terminator calling function `ty(25)` is executed next (front of the `k` cell) - - The function table contains `ty(25) -> "maximum" code. - - Other state (how `main` continues, its other local variables, and some internal functions) is relevant to the proof we want to perform. -2. The program executes for a total of 92 steps to reach the point where it `return`s from `maximum`. + - The function table contains `ty(25) -> ... code of "maximum"`. + - Other state (how `main` continues, its other local variables, and some internal functions) is not relevant to the proof we want to perform. + +``` + + + #execTerminator ( terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... (truncated) + + + noReturn + + + ty ( -1 ) + + + + ListItem ( basicBlock (... statements: ...(truncated) + ... (truncated) + + + ty ( -1 ) + + + place (... local: local ( -1 ) , projection: .ProjectionElems ) + + + noBasicBlockIdx + + + unwindActionUnreachable + + + ListItem ( newLocal ( ty ( 1 ) , mutabilityMut ) ) + ListItem ( typedValue ( Integer ( 42 , 64 , false ) , ty ( 26 ) , mutabilityNot ) ) + ListItem ( typedValue ( Integer ( 22 , 64 , false ) , ty ( 26 ) , mutabilityNot ) ) + ListItem ( typedValue ( Integer ( 0 , 64 , false ) , ty ( 26 ) , mutabilityNot ) ) + ListItem ( newLocal ( ty ( 26 ) , mutabilityNot ) ) + ...(truncated) + + + + .List + + + ty ( 13 ) |-> monoItemFn (... (truncated) + ... + ty ( 25 ) |-> monoItemFn (... name: symbol ( "maximum" ) , id: defId ( 7 ) , body: someBody ( body (... (truncated) + ... + + ...(truncated) +``` + +2. The program executes for a total of 110 steps to reach the point where it `return`s from `maximum`. The following command runs it and displays the resulting program state. ```shell - kmir run main-max-with-lt.smir.json --depth 92 | less -S + kmir run main-max-with-lt.smir.json --depth 110 | less -S ``` - The value `locals[0]` is now set to an `Integer`. This will be the target of our assertions. - A `return` terminator is executed next (front of the `k` cell), it will return `locals[0]` From 8d9de92bb8fc84c62440793ad38fed342930ac99 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Tue, 1 Apr 2025 22:33:38 +1100 Subject: [PATCH 07/29] use demon container in workflow --- .github/workflows/kmir.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index 6e546671dc47b..76777b641b520 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -19,20 +19,28 @@ jobs: run-kmir-proofs: name: Run supplied KMIR proofs runs-on: ubuntu-latest - container: runtimeverificationinc/kmir:ubuntu-jammy-0.3.112_7.1.229-daf5158 + env: + container_name: "kmir-${{ github.run_id }}" steps: - name: Checkout Repository uses: actions/checkout@v4 - with: - path: head - - name: Run KMIR Verification + - name: Start Tool Container run: | - cd kmir-proofs - for k_file in */*-spec.k; do + docker run --detach --rm -t \ + --name ${{ env.container_name }} \ + -v $PWD:/workspace -w /workspace \ + -u $(id -u):$(id -g) \ + runtimeverificationinc/kmir:ubuntu-jammy-0.3.112_7.1.229-daf5158 /bin/bash + + - name: Run KMIR Verification for all proofs provided + run: | + for k_file in kmir-proofs/*/*-spec.k; do echo "Running ${k_file}" - kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs + docker exec ${{ env.container_name }} kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs done - + - name: Stop Tool Container + if: always() + run: docker stop ${{ env.container_name }} From a6c1708365b480f631e6b96486a38fb35645c730 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Wed, 2 Apr 2025 22:21:25 +1100 Subject: [PATCH 08/29] Do not set a default shell in the CI workflow When setting `shell: bash`, the internal command used in the github job is different from when nothing is set, see https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_iddefaultsrunshell --- .github/workflows/kmir.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index 76777b641b520..76a8eea20840a 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -11,17 +11,13 @@ on: - '.github/workflows/kmir.yml' - 'kmir-proofs/**' -defaults: - run: - shell: bash - jobs: run-kmir-proofs: name: Run supplied KMIR proofs runs-on: ubuntu-latest env: container_name: "kmir-${{ github.run_id }}" - + steps: - name: Checkout Repository uses: actions/checkout@v4 From 5ce3c31548387c02f50206d69ecff583b6d9801b Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Wed, 2 Apr 2025 22:42:25 +1100 Subject: [PATCH 09/29] Do not set uid, do not bind-mount (copy kmir-proofs directory) --- .github/workflows/kmir.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index 76a8eea20840a..7ee9a37c591a8 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -26,15 +26,18 @@ jobs: run: | docker run --detach --rm -t \ --name ${{ env.container_name }} \ - -v $PWD:/workspace -w /workspace \ - -u $(id -u):$(id -g) \ + -w /workspace \ runtimeverificationinc/kmir:ubuntu-jammy-0.3.112_7.1.229-daf5158 /bin/bash + - name: Copy KMIR proofs into container + run: | + docker cp kmir-proofs ${{ env.container_name }}:/workspace/kmir-proofs + - name: Run KMIR Verification for all proofs provided run: | for k_file in kmir-proofs/*/*-spec.k; do echo "Running ${k_file}" - docker exec ${{ env.container_name }} kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs + docker exec -t ${{ env.container_name }} kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs done - name: Stop Tool Container From c6de927edaaa9ec49f7e96343d9dd12246f6e132 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Wed, 2 Apr 2025 22:48:01 +1100 Subject: [PATCH 10/29] do not write proof kcfg in CI (works around a permission problem) --- .github/workflows/kmir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index 7ee9a37c591a8..3b7503b582639 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -37,7 +37,7 @@ jobs: run: | for k_file in kmir-proofs/*/*-spec.k; do echo "Running ${k_file}" - docker exec -t ${{ env.container_name }} kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs + docker exec -t ${{ env.container_name }} kmir prove run ${k_file} done - name: Stop Tool Container From 89d8f06cb4fe5d2e687e9386a47e3725dddb59cc Mon Sep 17 00:00:00 2001 From: Freeman <105403280+F-WRunTime@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:12:18 -0600 Subject: [PATCH 11/29] Update CI Image w/ new environment controls - Focused on fixing read/write permission issues in previous image - New image introduces new env and bash profile for additional exec calls used in CI. --- .github/workflows/kmir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index 3b7503b582639..7021d8435bdf5 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -27,7 +27,7 @@ jobs: docker run --detach --rm -t \ --name ${{ env.container_name }} \ -w /workspace \ - runtimeverificationinc/kmir:ubuntu-jammy-0.3.112_7.1.229-daf5158 /bin/bash + runtimeverificationinc/kmir:ubuntu-jammy-0.3.112_7.1.229-9e17ccd /bin/bash - name: Copy KMIR proofs into container run: | From 347fd4cc1d54afaf306dd7d18fbb0f741de2335f Mon Sep 17 00:00:00 2001 From: Freeman <105403280+F-WRunTime@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:19:18 -0600 Subject: [PATCH 12/29] Adding back in proof generation on container - Still requires some additional steps to fetch the results from the running container but this is a starting point. --- .github/workflows/kmir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index 7021d8435bdf5..a55384f1d3543 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -37,7 +37,7 @@ jobs: run: | for k_file in kmir-proofs/*/*-spec.k; do echo "Running ${k_file}" - docker exec -t ${{ env.container_name }} kmir prove run ${k_file} + docker exec -t ${{ env.container_name }} /bin/bash -l -c "kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs" done - name: Stop Tool Container From b346e034323bcf7a9db047836683c0205c045fe8 Mon Sep 17 00:00:00 2001 From: Freeman <105403280+F-WRunTime@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:43:57 -0600 Subject: [PATCH 13/29] Revert previous change to workflow. Further investigation needed between CI environments --- .github/workflows/kmir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index a55384f1d3543..7021d8435bdf5 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -37,7 +37,7 @@ jobs: run: | for k_file in kmir-proofs/*/*-spec.k; do echo "Running ${k_file}" - docker exec -t ${{ env.container_name }} /bin/bash -l -c "kmir prove run ${k_file} --proof-dir $(dirname ${k_file})/proofs" + docker exec -t ${{ env.container_name }} kmir prove run ${k_file} done - name: Stop Tool Container From 5746c4db8a6705311fdd3fa4a025c28fbb18fa01 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Sat, 5 Apr 2025 19:44:18 +1100 Subject: [PATCH 14/29] Format Markdown files to 80-column line breaks (content unchanged) --- doc/src/tools/kmir.md | 171 +++++++++++++++----- kmir-proofs/README.md | 106 ++++++++---- kmir-proofs/maximum-example-proof/README.md | 94 ++++++++--- 3 files changed, 276 insertions(+), 95 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index ae1967561fadc..36c58f93ccb81 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -2,89 +2,178 @@ **KMIR** ## Description -[KMIR](https://github.com/runtimeverification/mir-semantics) is a formal verification tool for Rust that defines the operational semantics of Rust’s Middle Intermediate Representation (MIR) in K (through Stable MIR). By leveraging the [K framework](https://kframework.org/), KMIR provides a parser, interpreter, and symbolic execution engine for MIR programs. This tool enables direct execution of concrete and symbolic input, with step-by-step inspection of the internal state of the MIR program's execution, serving as a foundational step toward full formal verification of Rust programs. Through the dependency [Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/), KMIR allows developers to extract serialized Stable MIR from Rust’s compilation process, execute it, and eventually prove critical properties of their code. Soon, KMIR will be available via our package manager, [kup](https://github.com/runtimeverification/kup), which will make it easily installable and integrable into various workflows. + +[KMIR](https://github.com/runtimeverification/mir-semantics) is a formal +verification tool for Rust that defines the operational semantics of Rust’s +Middle Intermediate Representation (MIR) in K (through Stable MIR). By +leveraging the [K framework](https://kframework.org/), KMIR provides a parser, +interpreter, and symbolic execution engine for MIR programs. This tool enables +direct execution of concrete and symbolic input, with step-by-step inspection of +the internal state of the MIR program's execution, serving as a foundational +step toward full formal verification of Rust programs. Through the dependency +[Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/), KMIR +allows developers to extract serialized Stable MIR from Rust’s compilation +process, execute it, and eventually prove critical properties of their +code. Soon, KMIR will be available via our package manager, +[kup](https://github.com/runtimeverification/kup), which will make it easily +installable and integrable into various workflows. This diagram describes the extraction and verification workflow for KMIR: ![kmir_env_diagram_march_2025](https://github.com/user-attachments/assets/bf426c8d-f241-4ad6-8cb2-86ca06d8d15b) -Particular to this challenge, KMIR verifies program correctness using the correct-by-construction symbolic execution engine and verifier derived from the K encoding of the Stable MIR semantics. The K semantics framework is based on reachability logic, which is a theory describing transition systems in [matching logic](http://www.matching-logic.org/). Transition rules of the semantics are rewriting steps that match patterns and transform the current continuation and state accordingly. An all-path-reachability proof in this system verifies that a particular _target_ end state is _always_ reachable from a given starting state. The rewrite rules branch on symbolic inputs covering the possible transitions, creating a model that is provably complete, and requiring unification on every leaf state. A one-path-reachability proof is similar to the above, but the proof requirement is that at least one leaf state unifies with the target state. One feature of such a system is that the requirement for an SMT is minimized to determining completeness on path conditions when branching would occur. - -KMIR also prioritizes UI with interactive proof exploration available out-of-the-box through the terminal KCFG (K Control Flow Graph) viewer, allowing users to inspect intermediate states of the proof to get feedback on the successful path conditions and failing at unifying with the target state. An example of a KMIR proof being analyzed using the KCFG viewer can be seen below: +Particular to this challenge, KMIR verifies program correctness using the +correct-by-construction symbolic execution engine and verifier derived from the +K encoding of the Stable MIR semantics. The K semantics framework is based on +reachability logic, which is a theory describing transition systems in [matching +logic](http://www.matching-logic.org/). Transition rules of the semantics are +rewriting steps that match patterns and transform the current continuation and +state accordingly. An all-path-reachability proof in this system verifies that a +particular _target_ end state is _always_ reachable from a given starting +state. The rewrite rules branch on symbolic inputs covering the possible +transitions, creating a model that is provably complete, and requiring +unification on every leaf state. A one-path-reachability proof is similar to the +above, but the proof requirement is that at least one leaf state unifies with +the target state. One feature of such a system is that the requirement for an +SMT is minimized to determining completeness on path conditions when branching +would occur. + +KMIR also prioritizes UI with interactive proof exploration available +out-of-the-box through the terminal KCFG (K Control Flow Graph) viewer, allowing +users to inspect intermediate states of the proof to get feedback on the +successful path conditions and failing at unifying with the target state. An +example of a KMIR proof being analyzed using the KCFG viewer can be seen below: image ## Tool Information -* [x] Does the tool perform Rust verification? - *Yes – It performs verification at the MIR level, which is a critical intermediate representation of Rust programs.* -* [x] Does the tool deal with *unsafe* Rust code? +* [x] Does the tool perform Rust verification? + *Yes – It performs verification at the MIR level, which is a critical + intermediate representation of Rust programs.* +* [x] Does the tool deal with *unsafe* Rust code? *Yes – By operating on MIR, KMIR can analyze both safe and unsafe Rust code.* -* [x] Does the tool run independently in CI? - *Yes – KMIR can be integrated into CI workflows via our package manager and Nix-based build system.* +* [x] Does the tool run independently in CI? + *Yes – KMIR can be integrated into CI workflows via our package manager and + Nix-based build system.* * [x] Is the tool open source? *Yes – KMIR is open source and available on GitHub.* -* [x] Is the tool under development? - *Yes – KMIR is actively under development, with ongoing improvements to MIR syntax coverage and verification capabilities.* +* [x] Is the tool under development? + *Yes – KMIR is actively under development, with ongoing improvements to MIR + syntax coverage and verification capabilities.* * [x] Will you or your team be able to provide support for the tool? - *Yes – The Runtime Verification team is committed to supporting KMIR and will provide ongoing maintenance and community support.* + *Yes – The Runtime Verification team is committed to supporting KMIR and will + provide ongoing maintenance and community support.* ## Comparison to Other Approved Tools -The other tools approved at the time of writing are Kani, Verifast, and Goto-transcoder (ESBMC). - -- **Verification Backend:** KMIR primarily differs from all of these tools by utilizing a unique verification backend through the K framework and reachability logic (as explained in the description above). KMIR has little dependence on an SAT solver or SMT solver. Kani's CBMC backend and Goto-transcode (ESBMC) encode the verification problem into an SAT / SMT verification condition to be discharged by the appropriate solver. Kani recently added a Lean backend through Aeneas, however Lean does not support matching or reachability logic currently. Verifast performs symbollic execution of the verification target like KMIR, however reasoning is performed by annotating functions with design-by-contract components in separation logic. -- **Verification Input:** KMIR takes input from Stable MIR JSON, an effort to serialize the internal MIR in a portable way that can be reusable by other projects. -- **K Ecosystem:** Since all tools in the K ecosystem share a common foundation of K, all projects benefit from development done by other K projects. This means that performance and user experience are projected to improve due to the continued development of other semantics. +The other tools approved at the time of writing are Kani, Verifast, and +Goto-transcoder (ESBMC). + +- **Verification Backend:** KMIR primarily differs from all of these tools by + utilizing a unique verification backend through the K framework and + reachability logic (as explained in the description above). KMIR has little + dependence on an SAT solver or SMT solver. Kani's CBMC backend and + Goto-transcode (ESBMC) encode the verification problem into an SAT / SMT + verification condition to be discharged by the appropriate solver. Kani + recently added a Lean backend through Aeneas, however Lean does not support + matching or reachability logic currently. Verifast performs symbollic + execution of the verification target like KMIR, however reasoning is performed + by annotating functions with design-by-contract components in separation + logic. +- **Verification Input:** KMIR takes input from Stable MIR JSON, an effort to + serialize the internal MIR in a portable way that can be reusable by other + projects. +- **K Ecosystem:** Since all tools in the K ecosystem share a common foundation + of K, all projects benefit from development done by other K projects. This + means that performance and user experience are projected to improve due to the + continued development of other semantics. ## Licenses -KMIR is released under an OSI-approved open source license. It is distributed under the BSD-3 clause license, which is compatible with the Rust standard library licenses. Please refer to the [KMIR GitHub repository](https://github.com/runtimeverification/mir-semantics?tab=BSD-3-Clause-1-ov-file) for full license details. +KMIR is released under an OSI-approved open source license. It is distributed +under the BSD-3 clause license, which is compatible with the Rust standard +library licenses. Please refer to the [KMIR GitHub +repository](https://github.com/runtimeverification/mir-semantics?tab=BSD-3-Clause-1-ov-file) +for full license details. ## Steps to Use the Tool -At RV, we generally strives to use [kup](https://github.com/runtimeverification/kup) , a `nix`-based software installer, to distribute our software. -This is future work, at the moment `kmir` requires a local build from source using the `K Framework` (installed with `kup`). +At RV, we generally strives to use +[kup](https://github.com/runtimeverification/kup) , a `nix`-based software +installer, to distribute our software. This is future work, at the moment +`kmir` requires a local build from source using the `K Framework` (installed +with `kup`). The future workflow we imagine is to 1. Download [kup](https://github.com/runtimeverification/kup) and install 2. Install KMIR `kup install kmir` -3. Compile the desired verification target with `kmir build module.rs` - The `module.rs` should contain functions that act as property tests and are run with symbolic inputs. -4. Verify the target with `kmir prove module.rs --target target_function` - This executes `target_function` with symbolic arguments. The test is expected to contain assertions about computed values. +3. Compile the desired verification target with `kmir build module.rs`. + The `module.rs` should contain functions that act as property tests and are + run with symbolic inputs. +4. Verify the target with `kmir prove module.rs --target target_function`. + This executes `target_function` with symbolic arguments. The test is expected + to contain assertions about computed values. 5. Inspect KCFG of proof with `kmir view module::target_function` -At the time of writing, step 3 requires manual work to set up a _claim_ in the K language. -We will automate this process in the frontend code to prevent the user from having to write K code. +At the time of writing, step 3 requires manual work to set up a _claim_ in the K +language. We will automate this process in the frontend code to prevent the +user from having to write K code. -**To see the specifications and run proofs using KMIR, including a subsection of the proofs required in the [Safety of Methods for Numeric Primitive Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) challenges, check this [instructional document](https://github.com/runtimeverification/mir-semantics/blob/sample-challenge-11-proofs/rust-verification-proofs/README.md) in our tool's repository.** +**To see the specifications and run proofs using KMIR, including a subsection of +the proofs required in the [Safety of Methods for Numeric Primitive +Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) +challenges, check this [instructional +document](https://github.com/runtimeverification/mir-semantics/blob/sample-challenge-11-proofs/rust-verification-proofs/README.md) +in our tool's repository.** ## Artifacts & Audit Mechanisms -- **[Matching Logic](http://www.matching-logic.org/)** - Matching Logic is a foundational logic underpinning the K framework, providing a unified approach to specifying, verifying, and reasoning about programming languages and their properties in a correct-by-construction manner. +- **[Matching Logic](http://www.matching-logic.org/)** + Matching Logic is a foundational logic underpinning the K framework, providing + a unified approach to specifying, verifying, and reasoning about programming + languages and their properties in a correct-by-construction manner. -- **[K Framework](https://kframework.org/)** - The K Framework is a rewrite-based executable semantic framework designed for defining programming languages, type systems, and formal analysis tools. It automatically generates language analysis tools directly from their formal semantics. +- **[K Framework](https://kframework.org/)** + The K Framework is a rewrite-based executable semantic framework designed for + defining programming languages, type systems, and formal analysis tools. It + automatically generates language analysis tools directly from their formal + semantics. -- **[Kontrol](https://kontrol.runtimeverification.com)** - Kontrol is a formal verification tool built on K's semantics, enabling symbolic execution and proof construction for Ethereum smart contracts. +- **[Kontrol](https://kontrol.runtimeverification.com)** + Kontrol is a formal verification tool built on K's semantics, enabling + symbolic execution and proof construction for Ethereum smart contracts. -- **[KEVM Semantics](https://github.com/kframework/evm-semantics)** - KEVM provides a practical, executable formal specification of the Ethereum Virtual Machine using the K Framework, demonstrating K’s real-world application for verifying blockchain virtual machines. +- **[KEVM Semantics](https://github.com/kframework/evm-semantics)** + KEVM provides a practical, executable formal specification of the Ethereum + Virtual Machine using the K Framework, demonstrating K’s real-world + application for verifying blockchain virtual machines. ## Versioning -KMIR and Stable MIR JSON are both version controlled using git and hosted by Github. Semantic version numbers are used as soon as releases are made. -Stable MIR JSON depends on a nightly Rust compiler of a particular version (which is regularly updated, currently `nightly-2024-11-29`). -Dependencies for K and MIR JSON are tracked as pinned versions in the 'deps' folder and updated as changes are published upstream and tested against mir-semantics. +KMIR and Stable MIR JSON are both version controlled using git and hosted by +Github. Semantic version numbers are used as soon as releases are made. +Stable MIR JSON depends on a nightly Rust compiler of a particular version +(which is regularly updated, currently `nightly-2024-11-29`). +Dependencies for K and MIR JSON are tracked as pinned versions in the 'deps' +folder and updated as changes are published upstream and tested against +mir-semantics. ## CI -At Runtime Verification, we are regularly releasing and updating our tools using GitHub Actions and publishing our updated tool releases to standardized locations such as [Dockerhub](https://hub.docker.com/u/runtimeverificationinc) / ghcr.io / [cachix](https://app.cachix.org/cache/k-framework-binary). Any changes upstream to [K](https://github.com/runtimeverification/k) or [stable-mir-json](https://github.com/runtimeverification/stable-mir-json/) are immediately propagated to mir-semantics via workflow triggers to ensure the latest release of the tool is getting the latest improvements from K. - -For integrating KMIR into your project's CI pipeline, we recommend using our pre-built packages. You can choose from several installation methods depending on your needs: +At Runtime Verification, we are regularly releasing and updating our tools using +GitHub Actions and publishing our updated tool releases to standardized +locations such as [Dockerhub](https://hub.docker.com/u/runtimeverificationinc) / +ghcr.io / [cachix](https://app.cachix.org/cache/k-framework-binary). Any changes +upstream to [K](https://github.com/runtimeverification/k) or +[stable-mir-json](https://github.com/runtimeverification/stable-mir-json/) are +immediately propagated to mir-semantics via workflow triggers to ensure the +latest release of the tool is getting the latest improvements from K. + +For integrating KMIR into your project's CI pipeline, we recommend using our +pre-built packages. You can choose from several installation methods depending +on your needs: Our current Registries / Caches are: + 1. [Binary Cachix cache used by Kup](https://app.cachix.org/cache/k-framework-binary) 2. Source code on GitHub: [mir-semantics]](https://github.com/runtimeverification/mir-semantics) and [stable-mir-json](https://github.com/runtimeverification/stable-mir-json) 3. [Container image on Dockerhub](https://hub.docker.com/u/runtimeverificationinc) diff --git a/kmir-proofs/README.md b/kmir-proofs/README.md index cd32ecc5d7a28..e2ba12e5522d9 100644 --- a/kmir-proofs/README.md +++ b/kmir-proofs/README.md @@ -1,15 +1,26 @@ -# Formal Rust Code Verification Using KMIR +# Formal Rust Code Verification Using KMIR -This directory contains a collection of programs and specifications to illustrate how KMIR can validate the properties of Rust programs and standard library functions. +This directory contains a collection of programs and specifications to +illustrate how KMIR can validate the properties of Rust programs and +standard library functions. ## Setup -KMIR verification can either be run from [docker images provided under `runtimeverificationinc/kmir`](https://hub.docker.com/r/runtimeverificationinc/kmir), or using a local installation of [`mir-semantics`](https://github.com/runtimeverification/mir-semantics/) with its dependency [`stable-mir-json`](https://github.com/runtimeverification/stable-mir-json). The installation is described in the repository's `README.md` files. +KMIR verification can either be run from [docker images provided under +`runtimeverificationinc/kmir`](https://hub.docker.com/r/runtimeverificationinc/kmir), +or using a local installation of +[`mir-semantics`](https://github.com/runtimeverification/mir-semantics/) +with its dependency +[`stable-mir-json`](https://github.com/runtimeverification/stable-mir-json). The +installation is described in the repository's `README.md` files. ## Example Proof: Proving a Maximum Finding Function That only Uses `lower-than` -Considering a function that receives three integer arguments, this function should return the highest value among them. Assertions can be used to enforce this condition, and an example code that tests this function can be seen below: +Considering a function that receives three integer arguments, this +function should return the highest value among them. Assertions can be +used to enforce this condition, and an example code that tests this +function can be seen below: ```Rust fn main() { @@ -31,9 +42,22 @@ fn maximum(a: usize, b: usize, c: usize) -> usize { } ``` -Notice in this case that `a`, `b`, and `c` are concrete, fixed values. To turn the parameters of `maximum` into symbolic variables, we can obtain the representation of the function call to `maximum` executed using KMIR and then replace the concrete values of these variables with symbolic values. Furthermore, the assertion specified in the code can be manually translated as a requirement that should be met by the symbolic variables, meaning that any value that they can assume must respect the conditions contained in the specification. Following this approach, we can utilize KMIR to give us formal proof that, for any valid `isize` input, the maximum value among the three parameters will be returned. - -Work on KMIR is in progress and the way a Rust program is turned into a K claim will be automated in the near future, but is currently a manual process described in the longer [description of `maximum-example-proof`](./maximum-example-proof/README.md). +Notice in this case that `a`, `b`, and `c` are concrete, fixed +values. To turn the parameters of `maximum` into symbolic variables, +we can obtain the representation of the function call to `maximum` +executed using KMIR and then replace the concrete values of these +variables with symbolic values. Furthermore, the assertion specified +in the code can be manually translated as a requirement that should be +met by the symbolic variables, meaning that any value that they can +assume must respect the conditions contained in the +specification. Following this approach, we can utilize KMIR to give us +formal proof that, for any valid `isize` input, the maximum value +among the three parameters will be returned. + +Work on KMIR is in progress and the way a Rust program is turned into +a K claim will be automated in the near future, but is currently a +manual process described in the longer [description of +`maximum-example-proof`](./maximum-example-proof/README.md). To run this proof in your terminal from this folder, execute: @@ -50,38 +74,62 @@ kmir prove view MAXIMUM-SPEC.maximum-spec --proof-dir $PWD/proof ## Example Proofs: Safety of Unsafe Arithmetic Operations -The proofs in subdirectory `unchecked_arithmetic` concern a section of the challenge of securing [Safety of Methods for Numeric Primitive Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) of the Verify Rust Standard Library Effort. -The `*-spec.k` files set up a proof of concept of how KMIR can be used to prove unsafe methods according to their undefined behaviors. Proofs were set up using the same method as described for the `maximum-example-proof`. +The proofs in subdirectory `unchecked_arithmetic` concern a section of +the challenge of securing [Safety of Methods for Numeric Primitive +Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) +of the Verify Rust Standard Library Effort. + +The `*-spec.k` files set up a proof of concept of how KMIR can be used +to prove unsafe methods according to their undefined behaviors. Proofs +were set up using the same method as described for the +`maximum-example-proof`. -All K claim files follow the same pattern (illustrated using `unchecked_add` on `i16` as an example): +All K claim files follow the same pattern (illustrated using +`unchecked_add` on `i16` as an example): -1) For a given unsafe operation, a calling wrapper function `unchecked_op` is written and translated to Stable MIR +1) For a given unsafe operation, a calling wrapper function + `unchecked_op` is written and translated to Stable MIR ```rust - fn unchecked_op(a: i16, b: i16) -> i16 { - let unchecked_res = unsafe { a.unchecked_add(b) }; - unchecked_res - } + fn unchecked_op(a: i16, b: i16) -> i16 { + let unchecked_res = unsafe { a.unchecked_add(b) }; + unchecked_res + } ``` -2) A K configuration for a Rust program that calls this function with symbolic `i16` arguments `A` and `B` is constructed (currently in a manual fashion). The `i16` arguments are represented as `Integer(A, 16, true)`. +2) A K configuration for a Rust program that calls this function with + symbolic `i16` arguments `A` and `B` is constructed (currently in a + manual fashion). The `i16` arguments are represented as `Integer(A, + 16, true)`. -3) According to the [documentation of the unchecked_add function for the i16 primitive type](https://doc.rust-lang.org/std/primitive.i16.html#method.unchecked_add), +3) According to the [documentation of the unchecked_add function for + the i16 primitive + type](https://doc.rust-lang.org/std/primitive.i16.html#method.unchecked_add), -> This results in undefined behavior when `self + rhs > i16::MAX or self + rhs < i16::MIN`, i.e. when `checked_add` would return `None`" +> "This results in undefined behavior when `self + rhs > i16::MAX` or +> `self + rhs < i16::MIN`, i.e. when `checked_add` would return `None`" - This safety condition is translated into a `requires` clause in the K claim. In addition, the invariants for `A`'s and `B`'s representation as `i16` can be assumed, giving: + This safety condition is translated into a `requires` clause in the + K claim. In addition, the invariants for `A`'s and `B`'s + representation as `i16` can be assumed, giving: ``` - requires // i16 invariants - 0 -Int (1 < usize { ``` We want to prove a property of `maximum`: -- When called with `a`, `b`, and `c`, +- When called with `a`, `b`, and `c`, - the `result` will be greater or equal all of the arguments, - and equal to one (or more) of them. -The `main` program above states this using some concrete values of `a`, `b`, and `c`. We will run this program to construct a general symbolic claim and prove it. +The `main` program above states this using some concrete values of +`a`, `b`, and `c`. We will run this program to construct a general +symbolic claim and prove it. -In a future version, we will be able to start directly with the `maximum` function call and provide symbolic arguments to it. This will save some manual work setting up the claim file and fits the target of proving based on property tests. +In a future version, we will be able to start directly with the +`maximum` function call and provide symbolic arguments to it. This +will save some manual work setting up the claim file and fits the +target of proving based on property tests. ## Extracting Stable MIR for the program -Before we can run the program using the MIR semantics, we have to compile it with a special compiler to extract Stable MIR from it. This step differs a bit depending on whether the program has multiple crates, in our case it is just a simple invocation of the extraction tool. Assuming the tool is installed as `stable-mir-json` (as in the docker image provided): +Before we can run the program using the MIR semantics, we have to +compile it with a special compiler to extract Stable MIR from it. This +step differs a bit depending on whether the program has multiple +crates, in our case it is just a simple invocation of the extraction +tool. Assuming the tool is installed as `stable-mir-json` (as in the +docker image provided): ```shell stable-mir-json -Zno-codegen --out-dir . main-max-with-lt.rs ``` -This creates `main-max-with-lt.smir.json`. The `-Zno-codegen` and `--out-dir` options are passed through to the underlying `rustc` so no executable is generated. -The Stable MIR for the program can also be rendered as a graph, using `--dot` as the first option. This creates `main-max-with-lt.smir.dot`. +This creates `main-max-with-lt.smir.json`. The `-Zno-codegen` and +`--out-dir` options are passed through to the underlying `rustc` so no +executable is generated. + +The Stable MIR for the program can also be rendered as a graph, using +`--dot` as the first option. This creates `main-max-with-lt.smir.dot`. ```shell stable-mir-json --dot -Zno-codegen --out-dir . main-max-with-lt.rs ``` ## Constructing the claim by executing `main` to certain points -Through concrete execution of the parsed K program we can interrupt the execution after a given number of rewrite steps to inspect the intermediate state. This will help us with writing our claim manually until the process is automated. +Through concrete execution of the parsed K program we can interrupt +the execution after a given number of rewrite steps to inspect the +intermediate state. This will help us with writing our claim manually +until the process is automated. -1. The program (`main`) reaches the call to `maximum` after 25 steps. +1. The program (`main`) reaches the call to `maximum` after 25 steps. The following command runs it and displays the resulting program state. ```shell @@ -57,7 +74,9 @@ Through concrete execution of the parsed K program we can interrupt the executio - Arguments `a`, `b`, and `c` are initialised to `Integer`s as `locals[1]` to `locals[3]` - A `call` terminator calling function `ty(25)` is executed next (front of the `k` cell) - The function table contains `ty(25) -> ... code of "maximum"`. - - Other state (how `main` continues, its other local variables, and some internal functions) is not relevant to the proof we want to perform. + - Other state (how `main` continues, its other local variables, + and some internal functions) is not relevant to the proof we + want to perform. ``` @@ -108,8 +127,9 @@ Through concrete execution of the parsed K program we can interrupt the executio ...(truncated) ``` -2. The program executes for a total of 110 steps to reach the point where it `return`s from `maximum`. - The following command runs it and displays the resulting program state. +2. The program executes for a total of 110 steps to reach the point + where it `return`s from `maximum`. The following command runs it + and displays the resulting program state. ```shell kmir run main-max-with-lt.smir.json --depth 110 | less -S @@ -118,32 +138,56 @@ Through concrete execution of the parsed K program we can interrupt the executio - A `return` terminator is executed next (front of the `k` cell), it will return `locals[0]` - It should be an `Integer` with the desired properties as stated above -State 1. defines our start state for the claim. Irrelevant parts are elided (replaced by variables). -* The code of the `maximum` function in the `functions` table needs to be kept. We also keep its identifier `ty(25)`. Other functions can be removed (we won't perform a return). -* The `call` terminator is kept, calling `ty(25)` with arguments from `locals[1,2,3]`. `target` is modified to be `noBasicBlockIdx` to force termination of the prover (no block to jump back to). -* The four locals `0` - `3` are required in their original order to provide the function arguments. The values of `a`, `b`, and `c` in locals `1` - `3` are replaced with symbolic variables used in the proof. -* We could keep all other locals but do not have to (however it is important that the list of locals has a known length). +State 1. defines our start state for the claim. Irrelevant parts are +elided (replaced by variables). + +* The code of the `maximum` function in the `functions` table needs to + be kept. We also keep its identifier `ty(25)`. Other functions can + be removed (we won't perform a return). +* The `call` terminator is kept, calling `ty(25)` with arguments from + `locals[1,2,3]`. `target` is modified to be `noBasicBlockIdx` to + force termination of the prover (no block to jump back to). +* The four locals `0` - `3` are required in their original order to + provide the function arguments. The values of `a`, `b`, and `c` in + locals `1` - `3` are replaced with symbolic variables used in the + proof. +* We could keep all other locals but do not have to (however it is + important that the list of locals has a known length). * `main`s other details in `currentFrame` are irrelevant and elided. State 2. is the end state, where all that matters is the returned value. * The `locals` list should contain this `?RESULT` value at index `0` -* The `?RESULT` value should have the properties stated (equivalent to the assertion in `main`) -* Because of the modified `target`, the program should end, i.e., have an `#EndProgram` in the `k` cell. +* The `?RESULT` value should have the properties stated (equivalent to + the assertion in `main`) +* Because of the modified `target`, the program should end, i.e., have + an `#EndProgram` in the `k` cell. + +The above is written as a _claim_ in K framework language into a +`maximum-spec.k` file. Most of the syntax can be copied from the +output of the `kmir run` commands above, and irrelevant parts replaced +by `_` (LHS) or `?_` (RHS). + +Alternatively, it is possible to construct a claim that the entire +rest of the program after initialising the variables will result in +the desired `?RESULT`, i.e., the assertion in `main` is executed +successfully and the program ends in `#EndProgram` after checking +it. This would require more steps. -The above is written as a _claim_ in K framework language into a `maximum-spec.k` file. -Most of the syntax can be copied from the output of the `kmir run` commands above, and irrelevant parts replaced by `_` (LHS) or `?_` (RHS). +## Running the prover on the claim and viewing the proof -Alternatively, it is possible to construct a claim that the entire rest of the program after initialising the variables will result in the desired `?RESULT`, i.e., the assertion in `main` is executed successfully and the program ends in `#EndProgram` after checking it. This would require more steps. +Now that we have constructed claim, we can run use the KMIR verifier +to perform symbolic execution, and can view the state of proof +through the KMIR proof viewer. -## Running the prover on the claim and viewing the proof -Now that we have constructed claim, we can run use the KMIR verifier to perform symbollic execution, and can view the state of proof through the KMIR proof viewer. ```shell kmir prove run maximum-spec.k --proof-dir ./proof ``` -The proof steps are saved in the `$PWD/proof` directory for later inspection using `kmir prove view`. This is especially important when the proof does _not_ succeed immediately. +The proof steps are saved in the `$PWD/proof` directory for later +inspection using `kmir prove view`. This is especially important when +the proof does _not_ succeed immediately. ```shell kmir prove view MAXIMUM-SPEC.maximum-spec --proof-dir ./proof From 3acce27d48830c09d9859076e1e6a20a542df669 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Sat, 5 Apr 2025 20:44:37 +1100 Subject: [PATCH 15/29] Edits round 1, see related issue --- doc/src/tools/kmir.md | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index 36c58f93ccb81..dc4987028ba68 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -51,15 +51,15 @@ example of a KMIR proof being analyzed using the KCFG viewer can be seen below: ## Tool Information * [x] Does the tool perform Rust verification? - *Yes – It performs verification at the MIR level, which is a critical - intermediate representation of Rust programs.* + *Yes – It performs verification at the MIR level, an intermediate + representation of Rust programs in the Rust compiler `rustc`.* * [x] Does the tool deal with *unsafe* Rust code? *Yes – By operating on MIR, KMIR can analyze both safe and unsafe Rust code.* * [x] Does the tool run independently in CI? *Yes – KMIR can be integrated into CI workflows via our package manager and - Nix-based build system.* + Nix-based build system or through a docker image provided.* * [x] Is the tool open source? - *Yes – KMIR is open source and available on GitHub.* + *Yes – KMIR is [open source and available on GitHub](https://github.com/runtimeverification/mir-semantics).* * [x] Is the tool under development? *Yes – KMIR is actively under development, with ongoing improvements to MIR syntax coverage and verification capabilities.* @@ -75,7 +75,7 @@ Goto-transcoder (ESBMC). utilizing a unique verification backend through the K framework and reachability logic (as explained in the description above). KMIR has little dependence on an SAT solver or SMT solver. Kani's CBMC backend and - Goto-transcode (ESBMC) encode the verification problem into an SAT / SMT + Goto-transcoder (ESBMC) encode the verification problem into an SAT / SMT verification condition to be discharged by the appropriate solver. Kani recently added a Lean backend through Aeneas, however Lean does not support matching or reachability logic currently. Verifast performs symbollic @@ -99,6 +99,8 @@ for full license details. ## Steps to Use the Tool +**TODO Edit this to avoid mentioning `kup` and describe the high-level workflow only** + At RV, we generally strives to use [kup](https://github.com/runtimeverification/kup) , a `nix`-based software installer, to distribute our software. This is future work, at the moment @@ -120,14 +122,10 @@ At the time of writing, step 3 requires manual work to set up a _claim_ in the K language. We will automate this process in the frontend code to prevent the user from having to write K code. -**To see the specifications and run proofs using KMIR, including a subsection of -the proofs required in the [Safety of Methods for Numeric Primitive -Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) -challenges, check this [instructional -document](https://github.com/runtimeverification/mir-semantics/blob/sample-challenge-11-proofs/rust-verification-proofs/README.md) -in our tool's repository.** +Small examples of proofs using KMIR, and how to derive them from a +Rust program manually, are [provided in the `kmir-proofs` directory](). -## Artifacts & Audit Mechanisms +## Background Reading - **[Matching Logic](http://www.matching-logic.org/)** Matching Logic is a foundational logic underpinning the K framework, providing @@ -140,15 +138,6 @@ in our tool's repository.** automatically generates language analysis tools directly from their formal semantics. -- **[Kontrol](https://kontrol.runtimeverification.com)** - Kontrol is a formal verification tool built on K's semantics, enabling - symbolic execution and proof construction for Ethereum smart contracts. - -- **[KEVM Semantics](https://github.com/kframework/evm-semantics)** - KEVM provides a practical, executable formal specification of the Ethereum - Virtual Machine using the K Framework, demonstrating K’s real-world - application for verifying blockchain virtual machines. - ## Versioning KMIR and Stable MIR JSON are both version controlled using git and hosted by Github. Semantic version numbers are used as soon as releases are made. @@ -177,3 +166,11 @@ Our current Registries / Caches are: 1. [Binary Cachix cache used by Kup](https://app.cachix.org/cache/k-framework-binary) 2. Source code on GitHub: [mir-semantics]](https://github.com/runtimeverification/mir-semantics) and [stable-mir-json](https://github.com/runtimeverification/stable-mir-json) 3. [Container image on Dockerhub](https://hub.docker.com/u/runtimeverificationinc) + +The [KMIR docker +image](https://github.com/runtimeverification/mir-semantics/blob/c221c81d73a56c48692a087a2ced29917de99246/Dockerfile.kmir) +is the best option for both casual users of KMIR and CI. It contains +an installation of K Framework, the tool `kmir`, and the +`stable-mir-json` extraction tool, which is a custom version of +`rustc` which extracts Stable MIR as JSON or as graphviz' *.dot when +compiling a crate. From 90c2df207b1d304149a5f116e9381467602f4b84 Mon Sep 17 00:00:00 2001 From: dkcumming Date: Mon, 7 Apr 2025 01:42:16 +0700 Subject: [PATCH 16/29] Usage instructions list current build steps only --- doc/src/tools/kmir.md | 63 ++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index dc4987028ba68..b6c8ba66810a6 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -99,31 +99,50 @@ for full license details. ## Steps to Use the Tool -**TODO Edit this to avoid mentioning `kup` and describe the high-level workflow only** - -At RV, we generally strives to use -[kup](https://github.com/runtimeverification/kup) , a `nix`-based software -installer, to distribute our software. This is future work, at the moment -`kmir` requires a local build from source using the `K Framework` (installed -with `kup`). - -The future workflow we imagine is to -1. Download [kup](https://github.com/runtimeverification/kup) and install -2. Install KMIR `kup install kmir` -3. Compile the desired verification target with `kmir build module.rs`. - The `module.rs` should contain functions that act as property tests and are - run with symbolic inputs. -4. Verify the target with `kmir prove module.rs --target target_function`. - This executes `target_function` with symbolic arguments. The test is expected - to contain assertions about computed values. -5. Inspect KCFG of proof with `kmir view module::target_function` - -At the time of writing, step 3 requires manual work to set up a _claim_ in the K -language. We will automate this process in the frontend code to prevent the -user from having to write K code. +**TODO Is there a better way to do this with docker?** + +Currently, `kmir` requires a local build from source using the `K Framework` (installed with +`kup`). The steps to install `kup` and `k` are taken from the +[kup documentation](https://github.com/runtimeverification/kup/README.md) + +1. To install `kup`: +``` +bash <(curl https://kframework.org/install) +``` + +2. To install `k`: +``` +kup install k +``` + +Then after cloning the [kmir repository](https://github.com/runtimeverification/mir-semantics/) +the instructions from the kmir documentation can be followed to build and run `kmir`: + +3. To build `kmir`: +``` +make build +``` + +4. To run a `kmir` command (e.g. `kmir prove run`, `kmir prove view`): +``` +poetry run -C kmir/ kmir +``` Small examples of proofs using KMIR, and how to derive them from a Rust program manually, are [provided in the `kmir-proofs` directory](). +To follow some of the steps listed the submodule dependency +[Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/) will need to +be installed with the following commands: + +5. Update the submodule +``` +git submodule update --init --recursive +``` + +6. Build `stable-mir-json`: +``` +make stable-mir-json +``` ## Background Reading From ef146683bfafdea2ccd50756faf421c0b48a54eb Mon Sep 17 00:00:00 2001 From: dkcumming Date: Mon, 7 Apr 2025 01:51:17 +0700 Subject: [PATCH 17/29] added missing link to proofs --- doc/src/tools/kmir.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index b6c8ba66810a6..96d344a3dc6c2 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -129,7 +129,7 @@ poetry run -C kmir/ kmir ``` Small examples of proofs using KMIR, and how to derive them from a -Rust program manually, are [provided in the `kmir-proofs` directory](). +Rust program manually, are [provided in the `kmir-proofs` directory](https://github.com/runtimeverification/mir-semantics/tree/sample-challenge-11-proofs/rust-verification-proofs). To follow some of the steps listed the submodule dependency [Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/) will need to be installed with the following commands: From 08de8017ebf7efe4e4aad8c35a0dddd37161fba0 Mon Sep 17 00:00:00 2001 From: dkcumming Date: Mon, 7 Apr 2025 01:52:23 +0700 Subject: [PATCH 18/29] removed speculation about kup integration --- doc/src/tools/kmir.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index 96d344a3dc6c2..a5bb5645ec7a6 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -14,9 +14,7 @@ step toward full formal verification of Rust programs. Through the dependency [Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/), KMIR allows developers to extract serialized Stable MIR from Rust’s compilation process, execute it, and eventually prove critical properties of their -code. Soon, KMIR will be available via our package manager, -[kup](https://github.com/runtimeverification/kup), which will make it easily -installable and integrable into various workflows. +code. This diagram describes the extraction and verification workflow for KMIR: From d68fec5ddcb869622fd50585958ed9548477220a Mon Sep 17 00:00:00 2001 From: dkcumming Date: Mon, 7 Apr 2025 02:26:01 +0700 Subject: [PATCH 19/29] Added quote of what K Framewwork is --- doc/src/tools/kmir.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index a5bb5645ec7a6..ca921b1e2f6a7 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -21,9 +21,14 @@ This diagram describes the extraction and verification workflow for KMIR: ![kmir_env_diagram_march_2025](https://github.com/user-attachments/assets/bf426c8d-f241-4ad6-8cb2-86ca06d8d15b) -Particular to this challenge, KMIR verifies program correctness using the +To understand how KMIR works the K Framework must first be understood, and the best description +can be found at [kframework.org](https://kframework.org/): +> K is a rewrite-based executable semantic framework in which programming languages, type systems and formal analysis tools can be defined using configurations and rules. Configurations organize the state in units called cells, which are labeled and can be nested. K rewrite rules make it explicit which parts of the term are read-only, write-only, read-write, or unused. This makes K suitable for defining truly concurrent languages even in the presence of sharing. Computations are represented as syntactic extensions of the original language abstract syntax, using a nested list structure which sequentializes computational tasks, such as program fragments. Computations are like any other terms in a rewriting environment: they can be matched, moved from one place to another, modified, or deleted. This makes K suitable for defining control-intensive features such as abrupt termination, exceptions, or call/cc. + +K (and thus KMIR) verifies program correctness using the correct-by-construction symbolic execution engine and verifier derived from the -K encoding of the Stable MIR semantics. The K semantics framework is based on +K encoding of the languages operational semantics, in this case the Stable MIR semantics. +The K semantics framework is based on reachability logic, which is a theory describing transition systems in [matching logic](http://www.matching-logic.org/). Transition rules of the semantics are rewriting steps that match patterns and transform the current continuation and From 9feeb14d694286f8669567ccbf1368137ebb7420 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Mon, 7 Apr 2025 10:07:05 +1000 Subject: [PATCH 20/29] break new text at column 80, remove trailing whitespace --- doc/src/tools/kmir.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index ca921b1e2f6a7..00569c9e32c61 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -21,13 +21,26 @@ This diagram describes the extraction and verification workflow for KMIR: ![kmir_env_diagram_march_2025](https://github.com/user-attachments/assets/bf426c8d-f241-4ad6-8cb2-86ca06d8d15b) -To understand how KMIR works the K Framework must first be understood, and the best description -can be found at [kframework.org](https://kframework.org/): -> K is a rewrite-based executable semantic framework in which programming languages, type systems and formal analysis tools can be defined using configurations and rules. Configurations organize the state in units called cells, which are labeled and can be nested. K rewrite rules make it explicit which parts of the term are read-only, write-only, read-write, or unused. This makes K suitable for defining truly concurrent languages even in the presence of sharing. Computations are represented as syntactic extensions of the original language abstract syntax, using a nested list structure which sequentializes computational tasks, such as program fragments. Computations are like any other terms in a rewriting environment: they can be matched, moved from one place to another, modified, or deleted. This makes K suitable for defining control-intensive features such as abrupt termination, exceptions, or call/cc. +To understand how KMIR works the K Framework must first be understood, and the +best description can be found at [kframework.org](https://kframework.org/): + +> K is a rewrite-based executable semantic framework in which programming +> languages, type systems and formal analysis tools can be defined using +> configurations and rules. Configurations organize the state in units called +> cells, which are labeled and can be nested. K rewrite rules make it explicit +> which parts of the term are read-only, write-only, read-write, or unused. This +> makes K suitable for defining truly concurrent languages even in the presence +> of sharing. Computations are represented as syntactic extensions of the +> original language abstract syntax, using a nested list structure which +> sequentializes computational tasks, such as program fragments. Computations +> are like any other terms in a rewriting environment: they can be matched, +> moved from one place to another, modified, or deleted. This makes K suitable +> for defining control-intensive features such as abrupt termination, +> exceptions, or call/cc. K (and thus KMIR) verifies program correctness using the -correct-by-construction symbolic execution engine and verifier derived from the -K encoding of the languages operational semantics, in this case the Stable MIR semantics. +symbolic execution engine and verifier derived from the +K encoding of the languages operational semantics, in this case the Stable MIR semantics. The K semantics framework is based on reachability logic, which is a theory describing transition systems in [matching logic](http://www.matching-logic.org/). Transition rules of the semantics are @@ -104,9 +117,9 @@ for full license details. **TODO Is there a better way to do this with docker?** -Currently, `kmir` requires a local build from source using the `K Framework` (installed with -`kup`). The steps to install `kup` and `k` are taken from the -[kup documentation](https://github.com/runtimeverification/kup/README.md) +Currently, `kmir` requires a local build from source using the `K Framework` (installed with +`kup`). The steps to install `kup` and `k` are taken from the +[kup documentation](https://github.com/runtimeverification/kup/README.md) 1. To install `kup`: ``` @@ -118,7 +131,7 @@ bash <(curl https://kframework.org/install) kup install k ``` -Then after cloning the [kmir repository](https://github.com/runtimeverification/mir-semantics/) +Then after cloning the [kmir repository](https://github.com/runtimeverification/mir-semantics/) the instructions from the kmir documentation can be followed to build and run `kmir`: 3. To build `kmir`: @@ -133,8 +146,8 @@ poetry run -C kmir/ kmir Small examples of proofs using KMIR, and how to derive them from a Rust program manually, are [provided in the `kmir-proofs` directory](https://github.com/runtimeverification/mir-semantics/tree/sample-challenge-11-proofs/rust-verification-proofs). -To follow some of the steps listed the submodule dependency -[Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/) will need to +To follow some of the steps listed the submodule dependency +[Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/) will need to be installed with the following commands: 5. Update the submodule From 2e0bfd89bf75beda6bcfe6d35d5f0a4060c3a444 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Mon, 7 Apr 2025 10:45:42 +1000 Subject: [PATCH 21/29] Rewrite usage section, delete CI and Versioning parts, move Licensing --- doc/src/tools/kmir.md | 134 +++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 87 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index 00569c9e32c61..bced7022daa40 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -83,6 +83,13 @@ example of a KMIR proof being analyzed using the KCFG viewer can be seen below: *Yes – The Runtime Verification team is committed to supporting KMIR and will provide ongoing maintenance and community support.* +## Licenses +KMIR is released under an OSI-approved open source license. It is distributed +under the BSD-3 clause license, which is compatible with the Rust standard +library licenses. Please refer to the [KMIR GitHub +repository](https://github.com/runtimeverification/mir-semantics?tab=BSD-3-Clause-1-ov-file) +for full license details. + ## Comparison to Other Approved Tools The other tools approved at the time of writing are Kani, Verifast, and Goto-transcoder (ESBMC). @@ -106,59 +113,49 @@ Goto-transcoder (ESBMC). means that performance and user experience are projected to improve due to the continued development of other semantics. -## Licenses -KMIR is released under an OSI-approved open source license. It is distributed -under the BSD-3 clause license, which is compatible with the Rust standard -library licenses. Please refer to the [KMIR GitHub -repository](https://github.com/runtimeverification/mir-semantics?tab=BSD-3-Clause-1-ov-file) -for full license details. - ## Steps to Use the Tool -**TODO Is there a better way to do this with docker?** - -Currently, `kmir` requires a local build from source using the `K Framework` (installed with -`kup`). The steps to install `kup` and `k` are taken from the -[kup documentation](https://github.com/runtimeverification/kup/README.md) - -1. To install `kup`: -``` -bash <(curl https://kframework.org/install) -``` - -2. To install `k`: -``` -kup install k -``` - -Then after cloning the [kmir repository](https://github.com/runtimeverification/mir-semantics/) -the instructions from the kmir documentation can be followed to build and run `kmir`: - -3. To build `kmir`: -``` -make build -``` - -4. To run a `kmir` command (e.g. `kmir prove run`, `kmir prove view`): -``` -poetry run -C kmir/ kmir -``` - -Small examples of proofs using KMIR, and how to derive them from a -Rust program manually, are [provided in the `kmir-proofs` directory](https://github.com/runtimeverification/mir-semantics/tree/sample-challenge-11-proofs/rust-verification-proofs). -To follow some of the steps listed the submodule dependency -[Stable MIR JSON](https://github.com/runtimeverification/stable-mir-json/) will need to -be installed with the following commands: - -5. Update the submodule -``` -git submodule update --init --recursive -``` +The [KMIR docker +image](https://github.com/runtimeverification/mir-semantics/blob/c221c81d73a56c48692a087a2ced29917de99246/Dockerfile.kmir) +is currently the best option for casual users of KMIR. It contains an +installation of K Framework, the tool `kmir`, and the `stable-mir-json` +extraction tool, which is a custom version of `rustc` which extracts Stable MIR +as JSON or as graphviz' *.dot when compiling a crate. +The image is [available on Docker Hub](https://hub.docker.com/r/runtimeverificationinc/kmir/tags). + +Alternatively, the tools for `kmir` can be built from source as [described in +the `mir-semantics` repository's +`README.md`](https://github.com/runtimeverification/mir-semantics). This +requires an installation of `K Framework`, best done [using the `kup` +tool](https://github.com/runtimeverification/kup/README.md), and includes a +git submodule dependency on `stable-mir-json`. + +The `stable-mir-json` tool is a custom version of `rustc` which, while compiling +Rust code, writes the code's Stable MIR, represented in a JSON format, to a +file. Just like `rustc` itself, `stable-mir-json` extracts MIR of a single +crate and must be invoked via `cargo` for multi-crate programs. Besides the JSON +extraction, `stable-mir-json` can also write a graphviz `dot` file representing +the Stable MIR structure of all functions and their call graph within the crate. + +The `kmir` tool provides commands to work with the Stable MIR representation of +Rust programs that `stable-mir-json` extracts. + +* Run Stable MIR code extracted from Rust programs (`kmir run my-program.smir.json`); +* Prove a property about a Rust program, which is given as a K "claim" and + proven using an all-path reachability proof in K (`kmir prove run my-program-spec.k`); +* Inspect the control flow graph of a program's proof constructed by the `kmir + prove run` command (`kmir prove view Module.Proof-Identifier`). + +Examples of proofs using KMIR, and how to derive them from a Rust program +manually, are [provided in the `kmir-proofs` +directory](https://github.com/model-checking/verify-rust-std/tree/main/kmir-proofs). + +The `kmir` tool is under active development at the time of writing. +Constructing a K claim from a given Rust program is currently a manual process +but will be automated in a future version. Likewise, at the time of writing, the +`kmir` tool does not automatically extract Stable MIR from a Rust program, the +Stable MIR must be extracted by invoking `stable-mir-json` manually. -6. Build `stable-mir-json`: -``` -make stable-mir-json -``` ## Background Reading @@ -172,40 +169,3 @@ make stable-mir-json defining programming languages, type systems, and formal analysis tools. It automatically generates language analysis tools directly from their formal semantics. - -## Versioning -KMIR and Stable MIR JSON are both version controlled using git and hosted by -Github. Semantic version numbers are used as soon as releases are made. -Stable MIR JSON depends on a nightly Rust compiler of a particular version -(which is regularly updated, currently `nightly-2024-11-29`). -Dependencies for K and MIR JSON are tracked as pinned versions in the 'deps' -folder and updated as changes are published upstream and tested against -mir-semantics. - -## CI -At Runtime Verification, we are regularly releasing and updating our tools using -GitHub Actions and publishing our updated tool releases to standardized -locations such as [Dockerhub](https://hub.docker.com/u/runtimeverificationinc) / -ghcr.io / [cachix](https://app.cachix.org/cache/k-framework-binary). Any changes -upstream to [K](https://github.com/runtimeverification/k) or -[stable-mir-json](https://github.com/runtimeverification/stable-mir-json/) are -immediately propagated to mir-semantics via workflow triggers to ensure the -latest release of the tool is getting the latest improvements from K. - -For integrating KMIR into your project's CI pipeline, we recommend using our -pre-built packages. You can choose from several installation methods depending -on your needs: - -Our current Registries / Caches are: - -1. [Binary Cachix cache used by Kup](https://app.cachix.org/cache/k-framework-binary) -2. Source code on GitHub: [mir-semantics]](https://github.com/runtimeverification/mir-semantics) and [stable-mir-json](https://github.com/runtimeverification/stable-mir-json) -3. [Container image on Dockerhub](https://hub.docker.com/u/runtimeverificationinc) - -The [KMIR docker -image](https://github.com/runtimeverification/mir-semantics/blob/c221c81d73a56c48692a087a2ced29917de99246/Dockerfile.kmir) -is the best option for both casual users of KMIR and CI. It contains -an installation of K Framework, the tool `kmir`, and the -`stable-mir-json` extraction tool, which is a custom version of -`rustc` which extracts Stable MIR as JSON or as graphviz' *.dot when -compiling a crate. From e62f890dffc58728312d05c0b6165e193011e46a Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Mon, 7 Apr 2025 11:42:57 +1000 Subject: [PATCH 22/29] expand K framework explanation to explain reachability proofs --- doc/src/tools/kmir.md | 73 ++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index bced7022daa40..2af22f8b7053a 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -21,39 +21,46 @@ This diagram describes the extraction and verification workflow for KMIR: ![kmir_env_diagram_march_2025](https://github.com/user-attachments/assets/bf426c8d-f241-4ad6-8cb2-86ca06d8d15b) -To understand how KMIR works the K Framework must first be understood, and the -best description can be found at [kframework.org](https://kframework.org/): - -> K is a rewrite-based executable semantic framework in which programming -> languages, type systems and formal analysis tools can be defined using -> configurations and rules. Configurations organize the state in units called -> cells, which are labeled and can be nested. K rewrite rules make it explicit -> which parts of the term are read-only, write-only, read-write, or unused. This -> makes K suitable for defining truly concurrent languages even in the presence -> of sharing. Computations are represented as syntactic extensions of the -> original language abstract syntax, using a nested list structure which -> sequentializes computational tasks, such as program fragments. Computations -> are like any other terms in a rewriting environment: they can be matched, -> moved from one place to another, modified, or deleted. This makes K suitable -> for defining control-intensive features such as abrupt termination, -> exceptions, or call/cc. - -K (and thus KMIR) verifies program correctness using the -symbolic execution engine and verifier derived from the -K encoding of the languages operational semantics, in this case the Stable MIR semantics. -The K semantics framework is based on -reachability logic, which is a theory describing transition systems in [matching -logic](http://www.matching-logic.org/). Transition rules of the semantics are -rewriting steps that match patterns and transform the current continuation and -state accordingly. An all-path-reachability proof in this system verifies that a -particular _target_ end state is _always_ reachable from a given starting -state. The rewrite rules branch on symbolic inputs covering the possible -transitions, creating a model that is provably complete, and requiring -unification on every leaf state. A one-path-reachability proof is similar to the -above, but the proof requirement is that at least one leaf state unifies with -the target state. One feature of such a system is that the requirement for an -SMT is minimized to determining completeness on path conditions when branching -would occur. +The K Framework ([kframework.org](https://kframework.org/) is the basis of how +KMIR operates to guarantee properties of Rust programs. K is a rewrite-based +semantic framework based on [matching logic](http://www.matching-logic.org/) in +which programming languages, their operational semantics and type systems, and +formal analysis tools can be defined through syntax, configurations, and rules. +The _syntax_ definitions in KMIR model the AST of Stable MIR (e.g., the +statements and terminator of a basic block in a function body) and configuration +data that exists at runtime (e.g., the stack frame structure of a function +call). +The _configuration_ of a KMIR program organizes the state of an executed program in +nested configuration units called cells (e.g., a stack frame is part of a stack +stored in the configuration). +_K Framework transition rules_ of the KMIR semantics are rewriting steps that +match patterns and transform the current continuation and state accordingly. +They describe how program configuration and its contained data changes when +particular program statements or terminators are executed (e.g., a returning +function modifies the call stack and writes a return value into the caller's +local variables). + +Using the K semantics of Stable MIR, the KMIR execution of an entire Rust +program represented as Stable MIR breaks down to a series of configuration +rewrites that compute data held in local variables, and the program may either +terminate normally or reach an exception or construct with undefined behaviour, +which terminates the execution abnormally. Programs modelled in K Framework can +be executed _symbolically_, i.e., operating on abstract input which is not fully +specified but characterised by _path conditions_ (e.g., that an integer variable +holds an unknown but non-negative value). + +K (and thus KMIR) verifies program correctness by performing an +_all-path-reachability proof_ using the symbolic execution engine and verifier +derived from the K encoding of the Stable MIR operational semantics. +The K semantics framework is based on reachability logic, which is a theory +describing transition systems in [matching logic](http://www.matching-logic.org/). +An all-path-reachability proof in this system verifies that a particular +_target_ end state is _always_ reached from a given starting state. +The rewrite rules branch on symbolic inputs covering the possible transitions, +creating a model that is provably complete. For all-path reachability, every +leaf state is required to unify with the target state. +A one-path-reachability proof is similar to the above, but the proof requirement +is that _at least one_ leaf state unifies with the target state. KMIR also prioritizes UI with interactive proof exploration available out-of-the-box through the terminal KCFG (K Control Flow Graph) viewer, allowing From b7d54d9b72c67b7e1c8c5fbc7711710a51dd064b Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Tue, 15 Apr 2025 15:50:51 +1000 Subject: [PATCH 23/29] add a section explaining how loops and recursion can be addressed by K --- doc/src/tools/kmir.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index 2af22f8b7053a..aef6e52a9780e 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -62,6 +62,26 @@ leaf state is required to unify with the target state. A one-path-reachability proof is similar to the above, but the proof requirement is that _at least one_ leaf state unifies with the target state. +When performing a proof of a program that involves recursion or a loop construct, +one of several possible techniques can be used: + +1) K (and thus KMIR) are capable of unbounded verification via allowing the + user to write loop invariants. However, these loop invariants would then + need to be written in K's native language. +2) As potential future work, it would be possible to implement an annotation + language to provide the required context for loop invariant directly in + source code (as done in the past using natspec for Solitidy code). +3) In general, K also supports bounded loop unrolling, by way of identifying + loop heads and counting how many times the same loop head has been observed. + This technique is managed by the all-path reachability prover library for + K and works out of the box with suitable arguments, without requiring any + special support from the back-end. + +Our front-end, at the time of writing, does not have either of these +options turned on. As soon as larger programs will require it, we will decide +whether the preference is to implement support for user-supplied K-level +loop invariants, or bounded loop unrolling support, or (probably) offer both. + KMIR also prioritizes UI with interactive proof exploration available out-of-the-box through the terminal KCFG (K Control Flow Graph) viewer, allowing users to inspect intermediate states of the proof to get feedback on the From 7b857e9a98843b4e1206ef1c6161c7fdbac07245 Mon Sep 17 00:00:00 2001 From: Gregory Makodzeba <73550323+gregorymakodzeba@users.noreply.github.com> Date: Fri, 18 Apr 2025 11:53:31 -0400 Subject: [PATCH 24/29] Update kmir.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added description clarifying KMIR's approach to handling undefined behavior (UB) in Rust’s MIR. This section explains KMIR’s refusal-to-execute strategy and how UB is handled during symbolic execution to ensure soundness guarantees. --- doc/src/tools/kmir.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index aef6e52a9780e..9245187d41e4b 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -44,10 +44,23 @@ Using the K semantics of Stable MIR, the KMIR execution of an entire Rust program represented as Stable MIR breaks down to a series of configuration rewrites that compute data held in local variables, and the program may either terminate normally or reach an exception or construct with undefined behaviour, -which terminates the execution abnormally. Programs modelled in K Framework can -be executed _symbolically_, i.e., operating on abstract input which is not fully -specified but characterised by _path conditions_ (e.g., that an integer variable -holds an unknown but non-negative value). +which terminates the execution abnormally. KMIR is designed to provide sound +assurances about undefined behavior (UB) in Rust’s MIR. Rather than statically +over‑approximating or flagging UB at every unsafe block, KMIR models the full +MIR semantics, including UB transitions, use a **refusal-to-execute** strategy. +This means that if symbolic execution reaches a MIR instruction and cannot prove +that executing it would not result in UB (e.g., an out-of-bounds pointer dereference +or an unchecked arithmetic overflow), execution halts in a `UB DETECTED` state. +This state cannot be unified with a valid target state in the proof, so the proof +fails. KMIR systematically explores all feasible paths under the user-supplied +preconditions. Only when **every** path terminates without hitting UB *and* +satisfies the target property does KMIR declare the program UB-free (and correct +for the property). This ensures that any “no UB” claim holds under the sole assumption +that KMIR’s implementation is correct. + +Programs modelled in K Framework can be executed _symbolically_, i.e., operating +on abstract input which is not fully specified but characterized by _path conditions_ +(e.g., that an integer variable holds an unknown but non-negative value). K (and thus KMIR) verifies program correctness by performing an _all-path-reachability proof_ using the symbolic execution engine and verifier From bc50f5af7817f013bcf2c7cd67caf12222f5ca40 Mon Sep 17 00:00:00 2001 From: Daniel Cumming <124537596+dkcumming@users.noreply.github.com> Date: Tue, 29 Apr 2025 04:14:39 +0900 Subject: [PATCH 25/29] Update kmir-proofs/maximum-example-proof/README.md Co-authored-by: Michael Tautschnig --- kmir-proofs/maximum-example-proof/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmir-proofs/maximum-example-proof/README.md b/kmir-proofs/maximum-example-proof/README.md index 4545b261ecd63..05cfdfe03c77e 100644 --- a/kmir-proofs/maximum-example-proof/README.md +++ b/kmir-proofs/maximum-example-proof/README.md @@ -177,7 +177,7 @@ it. This would require more steps. ## Running the prover on the claim and viewing the proof -Now that we have constructed claim, we can run use the KMIR verifier +Now that we have constructed claim, we can use the KMIR verifier to perform symbolic execution, and can view the state of proof through the KMIR proof viewer. From 75e80c042a7baa7619abdce03e44fa220dff4a59 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Fri, 9 May 2025 13:05:09 +1000 Subject: [PATCH 26/29] update unchecked_arithmetic proofs (new runner, new test code, new workflow steps) --- .github/workflows/kmir.yml | 26 +--- .../unchecked_arithmetic/run-proofs.sh | 28 ++++ .../unchecked_arithmetic/unchecked-add-spec.k | 75 ----------- .../unchecked_arithmetic/unchecked-add.rs | 14 -- .../unchecked_arithmetic/unchecked-mul-spec.k | 73 ---------- .../unchecked_arithmetic/unchecked-mul.rs | 14 -- .../unchecked_arithmetic/unchecked-sub-spec.k | 73 ---------- .../unchecked_arithmetic/unchecked-sub.rs | 14 -- .../unchecked_arithmetic.rs | 125 ++++++++++++++++++ 9 files changed, 160 insertions(+), 282 deletions(-) create mode 100755 kmir-proofs/unchecked_arithmetic/run-proofs.sh delete mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-add-spec.k delete mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-add.rs delete mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-mul-spec.k delete mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-mul.rs delete mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-sub-spec.k delete mode 100644 kmir-proofs/unchecked_arithmetic/unchecked-sub.rs create mode 100644 kmir-proofs/unchecked_arithmetic/unchecked_arithmetic.rs diff --git a/.github/workflows/kmir.yml b/.github/workflows/kmir.yml index 7021d8435bdf5..ab70092167c78 100644 --- a/.github/workflows/kmir.yml +++ b/.github/workflows/kmir.yml @@ -22,24 +22,12 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - - name: Start Tool Container + - name: Run Proofs in Tool Container run: | - docker run --detach --rm -t \ + docker run --rm -t \ --name ${{ env.container_name }} \ - -w /workspace \ - runtimeverificationinc/kmir:ubuntu-jammy-0.3.112_7.1.229-9e17ccd /bin/bash - - - name: Copy KMIR proofs into container - run: | - docker cp kmir-proofs ${{ env.container_name }}:/workspace/kmir-proofs - - - name: Run KMIR Verification for all proofs provided - run: | - for k_file in kmir-proofs/*/*-spec.k; do - echo "Running ${k_file}" - docker exec -t ${{ env.container_name }} kmir prove run ${k_file} - done - - - name: Stop Tool Container - if: always() - run: docker stop ${{ env.container_name }} + -w /home/kmir/workspace \ + -u $(id -u):$(id -g) \ + -v $PWD:/home/kmir/workspace \ + runtimeverificationinc/kmir:ubuntu-jammy-0.3.152 \ + kmir-proofs/unchecked_arithmetic/run-proofs.sh diff --git a/kmir-proofs/unchecked_arithmetic/run-proofs.sh b/kmir-proofs/unchecked_arithmetic/run-proofs.sh new file mode 100755 index 0000000000000..bb9642a1aaffe --- /dev/null +++ b/kmir-proofs/unchecked_arithmetic/run-proofs.sh @@ -0,0 +1,28 @@ +#! /bin/bash + +set -eu + +DIR=$(realpath $(dirname $0)) + +cd $DIR + +start_symbols=$(sed -n -e 's,// @kmir prove-rs: ,,p' unchecked_arithmetic.rs) +# By default: unchecked_add_i32 unchecked_sub_usize unchecked_mul_isize +# See `main` in program. start symbols need to be reachable from `main`. + +echo "Running proofs for start symbols ${start_symbols} in unchecked_arithmetic.rs" + +for sym in ${start_symbols}; do + echo "#########################################" + echo "Running proof for $sym" + echo "#########################################" + + kmir prove-rs --start-symbol $sym --verbose --proof-dir proofs --reload unchecked_arithmetic.rs + + echo "#########################################" + echo "Proof finished, with the following graph:" + echo "#########################################" + kmir show --proof-dir proofs unchecked_arithmetic.${sym} --no-full-printer + + echo "#########################################" +done diff --git a/kmir-proofs/unchecked_arithmetic/unchecked-add-spec.k b/kmir-proofs/unchecked_arithmetic/unchecked-add-spec.k deleted file mode 100644 index 02720c5974919..0000000000000 --- a/kmir-proofs/unchecked_arithmetic/unchecked-add-spec.k +++ /dev/null @@ -1,75 +0,0 @@ -module UNCHECKED-ADD-SPEC - imports KMIR - - claim [unchecked-add-spec]: - - ( // LHS, start state - #execTerminator ( - terminator (... - kind: terminatorKindCall (... - func: operandConstant ( - constOperand (... - span: span ( 76 ) , - userTy: noUserTypeAnnotationIndex , - const: mirConst (... - kind: constantKindZeroSized , - ty: ty ( 32 ) , // <- this is the reference to `unchecked_op` - id: mirConstId ( 12 ) - ) - ) - ) , - args: - operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) - operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ), - destination: DEST, - target: noBasicBlockIdx, - // forcing the proof to stop because there is no caller to return to - unwind: _ - ), - span: _ - ) - ) - => - // RHS: target - // #execTerminator ( terminator (... kind: terminatorKindReturn , span: ?_ ) ) - #EndProgram - ) - ~> .K - - _ - _ => ty ( 32 ) - - _ => ?_ - _ => ?_ - _ => DEST - _ => noBasicBlockIdx - _ => ?_ - - ListItem ( _ ) - ListItem ( typedValue ( Integer ( A , 16 , true ) , ty ( 23 ) , _ ) ) - ListItem ( typedValue ( Integer ( B , 16 , true ) , ty ( 23 ) , _ ) ) - // _ // if we keep this we need a lemma for list size predicate simplification - => - ListItem ( typedValue ( Integer ( ?RESULT, 16, true), ty ( 23 ) , ?_ )) - ?_ - - - _ => ?_ - - ( - ty ( 32 ) |-> monoItemFn (... name: symbol ( "unchecked_op" ) , id: defId ( 9 ) , body: someBody ( body (... blocks: basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 93 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 34 ) , id: mirConstId ( 19 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 0 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 1 ) ) , unwind: unwindActionContinue ) , span: span ( 94 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 95 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 96 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 97 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 98 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 97 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 98 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "unchecked_sum" ) , sourceInfo: sourceInfo (... span: span ( 99 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 0 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 100 ) ) ) ) - ty ( 34 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_add" ) , id: defId ( 3 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 3 ) ) , span: span ( 43 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 3 ) , projection: .ProjectionElems ) , rvalue: rvalueNullaryOp ( nullOpUbChecks , ty ( 21 ) ) ) , span: span ( 44 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 43 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 45 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 22 ) , id: mirConstId ( 6 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 4 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 2 ) ) , unwind: unwindActionUnreachable ) , span: span ( 46 ) ) ) basicBlock (... statements: statement (... kind: statementKindStorageDead ( local ( 3 ) ) , span: span ( 48 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpAddUnchecked , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 49 ) ) .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 47 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 50 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 51 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 52 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 43 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 1 ) , span: span ( 46 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 51 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 52 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) .VarDebugInfos , spreadArg: noLocal , span: span ( 53 ) ) ) ) - ty ( 22 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_add::precondition_check" ) , id: defId ( 4 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 4 ) ) , span: span ( 55 ) ) statement (... kind: statementKindStorageLive ( local ( 6 ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 6 ) , projection: .ProjectionElems ) , rvalue: rvalueCheckedBinaryOp ( binOpAdd , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 0 ) , ty ( 23 ) ) .ProjectionElems ) ) ) ) , span: span ( 57 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 5 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 1 ) , ty ( 21 ) ) .ProjectionElems ) ) ) ) , span: span ( 58 ) ) statement (... kind: statementKindStorageDead ( local ( 6 ) ) , span: span ( 59 ) ) statement (... kind: statementKindStorageDead ( local ( 4 ) ) , span: span ( 55 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandCopy ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 54 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 60 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 24 ) , id: mirConstId ( 7 ) ) ) ) , args: operandConstant ( constOperand (... span: span ( 61 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindAllocated ( allocation (... bytes: b"\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00" , provenance: provenanceMap (... ptrs: provenanceMapEntry (... provSize: 0 , allocId: allocId ( 0 ) ) .ProvenanceMapEntries ) , align: align ( 8 ) , mutability: mutabilityMut ) ) , ty: ty ( 25 ) , id: mirConstId ( 8 ) ) ) ) .Operands , destination: place (... local: local ( 3 ) , projection: .ProjectionElems ) , target: noBasicBlockIdx , unwind: unwindActionUnreachable ) , span: span ( 62 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 63 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 1 ) , span: span ( 64 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 62 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 57 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 58 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 27 ) , span: span ( 56 ) , mut: mutabilityMut ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "lhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 66 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 67 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 57 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 58 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 68 ) ) ) ) - ) - - - requires // i16 invariants - 0 -Int (1 < i16::MIN)); -} - -fn unchecked_op(a: i16, b: i16) -> i16 { - let unchecked_res = unsafe { a.unchecked_add(b) }; - unchecked_res -} diff --git a/kmir-proofs/unchecked_arithmetic/unchecked-mul-spec.k b/kmir-proofs/unchecked_arithmetic/unchecked-mul-spec.k deleted file mode 100644 index 13b5a7d99afad..0000000000000 --- a/kmir-proofs/unchecked_arithmetic/unchecked-mul-spec.k +++ /dev/null @@ -1,73 +0,0 @@ -module UNCHECKED-MUL-SPEC - imports KMIR - - claim [unchecked-mul-spec]: - - ( // LHS, start state - #execTerminator ( - terminator (... - kind: terminatorKindCall (... - func: operandConstant ( - constOperand (... - span: span ( 76 ) , - userTy: noUserTypeAnnotationIndex , - const: mirConst (... - kind: constantKindZeroSized , - ty: ty ( 32 ) , // <- this is the reference to `unchecked_op` - id: mirConstId ( 12 ) - ) - ) - ) , - args: - operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) - operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ), - destination: DEST, - target: noBasicBlockIdx, - // forcing the proof to stop because there is no caller to return to - unwind: _ - ), - span: _ - ) - ) - => - // RHS: target - // #execTerminator ( terminator (... kind: terminatorKindReturn , span: ?_ ) ) - #EndProgram - ) - ~> .K - - _ - _ => ty ( 32 ) - - _ => ?_ - _ => ?_ - _ => DEST - _ => noBasicBlockIdx - _ => ?_ - - ListItem ( _ ) - ListItem ( typedValue ( Integer ( A , 16 , true ) , ty ( 23 ) , _ ) ) - ListItem ( typedValue ( Integer ( B , 16 , true ) , ty ( 23 ) , _ ) ) - // _ // if we keep this we need a lemma for list size predicate simplification - => - ListItem ( typedValue ( Integer ( ?RESULT, 16, true), ty ( 23 ) , ?_ )) - ?_ - - - _ => ?_ - - ( - ty ( 32 ) |-> monoItemFn (... name: symbol ( "unchecked_op" ) , id: defId ( 9 ) , body: someBody ( body (... blocks: basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 93 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 34 ) , id: mirConstId ( 19 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 0 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 1 ) ) , unwind: unwindActionContinue ) , span: span ( 94 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 95 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 96 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 97 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 98 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 97 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 98 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "unchecked_res" ) , sourceInfo: sourceInfo (... span: span ( 99 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 0 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 100 ) ) ) ) - ty ( 34 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_mul" ) , id: defId ( 3 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 3 ) ) , span: span ( 43 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 3 ) , projection: .ProjectionElems ) , rvalue: rvalueNullaryOp ( nullOpUbChecks , ty ( 21 ) ) ) , span: span ( 44 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 43 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 45 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 22 ) , id: mirConstId ( 6 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 4 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 2 ) ) , unwind: unwindActionUnreachable ) , span: span ( 46 ) ) ) basicBlock (... statements: statement (... kind: statementKindStorageDead ( local ( 3 ) ) , span: span ( 48 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpMulUnchecked , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 49 ) ) .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 47 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 50 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 51 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 52 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 43 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 1 ) , span: span ( 46 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 51 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 52 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) .VarDebugInfos , spreadArg: noLocal , span: span ( 53 ) ) ) ) - ty ( 22 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_mul::precondition_check" ) , id: defId ( 4 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 4 ) ) , span: span ( 55 ) ) statement (... kind: statementKindStorageLive ( local ( 6 ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 6 ) , projection: .ProjectionElems ) , rvalue: rvalueCheckedBinaryOp ( binOpMul , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 0 ) , ty ( 23 ) ) .ProjectionElems ) ) ) ) , span: span ( 57 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 5 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 1 ) , ty ( 21 ) ) .ProjectionElems ) ) ) ) , span: span ( 58 ) ) statement (... kind: statementKindStorageDead ( local ( 6 ) ) , span: span ( 59 ) ) statement (... kind: statementKindStorageDead ( local ( 4 ) ) , span: span ( 55 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandCopy ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 54 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 60 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 24 ) , id: mirConstId ( 7 ) ) ) ) , args: operandConstant ( constOperand (... span: span ( 61 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindAllocated ( allocation (... bytes: b"\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00" , provenance: provenanceMap (... ptrs: provenanceMapEntry (... provSize: 0 , allocId: allocId ( 0 ) ) .ProvenanceMapEntries ) , align: align ( 8 ) , mutability: mutabilityMut ) ) , ty: ty ( 25 ) , id: mirConstId ( 8 ) ) ) ) .Operands , destination: place (... local: local ( 3 ) , projection: .ProjectionElems ) , target: noBasicBlockIdx , unwind: unwindActionUnreachable ) , span: span ( 62 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 63 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 1 ) , span: span ( 64 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 62 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 57 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 58 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 27 ) , span: span ( 56 ) , mut: mutabilityMut ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "lhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 66 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 67 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 57 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 58 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 68 ) ) ) ) - ) - - requires // i16 invariants - 0 -Int (1 < i16::MAX) && (a * b < i16::MIN)); -} - -fn unchecked_op(a: i16, b: i16) -> i16 { - let unchecked_res = unsafe { a.unchecked_mul(b) }; - unchecked_res -} diff --git a/kmir-proofs/unchecked_arithmetic/unchecked-sub-spec.k b/kmir-proofs/unchecked_arithmetic/unchecked-sub-spec.k deleted file mode 100644 index f0597337dea36..0000000000000 --- a/kmir-proofs/unchecked_arithmetic/unchecked-sub-spec.k +++ /dev/null @@ -1,73 +0,0 @@ -module UNCHECKED-SUB-SPEC - imports KMIR - - claim [unchecked-sub-spec]: - - ( // LHS, start state - #execTerminator ( - terminator (... - kind: terminatorKindCall (... - func: operandConstant ( - constOperand (... - span: span ( 76 ) , - userTy: noUserTypeAnnotationIndex , - const: mirConst (... - kind: constantKindZeroSized , - ty: ty ( 32 ) , // <- this is the reference to `unchecked_op` - id: mirConstId ( 12 ) - ) - ) - ) , - args: - operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) - operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ), - destination: DEST, - target: noBasicBlockIdx, - // forcing the proof to stop because there is no caller to return to - unwind: _ - ), - span: _ - ) - ) - => - // RHS: target - // #execTerminator ( terminator (... kind: terminatorKindReturn , span: ?_ ) ) - #EndProgram - ) - ~> .K - - _ - _ => ty ( 32 ) - - _ => ?_ - _ => ?_ - _ => DEST - _ => noBasicBlockIdx - _ => ?_ - - ListItem ( _ ) - ListItem ( typedValue ( Integer ( A , 16 , true ) , ty ( 23 ) , _ ) ) - ListItem ( typedValue ( Integer ( B , 16 , true ) , ty ( 23 ) , _ ) ) - // _ // if we keep this we need a lemma for list size predicate simplification - => - ListItem ( typedValue ( Integer ( ?RESULT, 16, true), ty ( 23 ) , ?_ )) - ?_ - - - _ => ?_ - - ( - ty ( 32 ) |-> monoItemFn (... name: symbol ( "unchecked_op" ) , id: defId ( 9 ) , body: someBody ( body (... blocks: basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 93 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 34 ) , id: mirConstId ( 19 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 0 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 1 ) ) , unwind: unwindActionContinue ) , span: span ( 94 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 95 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 96 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 97 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 98 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 97 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 98 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "unchecked_sum" ) , sourceInfo: sourceInfo (... span: span ( 99 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 0 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 100 ) ) ) ) - ty ( 34 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_sub" ) , id: defId ( 3 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 3 ) ) , span: span ( 43 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 3 ) , projection: .ProjectionElems ) , rvalue: rvalueNullaryOp ( nullOpUbChecks , ty ( 21 ) ) ) , span: span ( 44 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 43 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 45 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 22 ) , id: mirConstId ( 6 ) ) ) ) , args: operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) .Operands , destination: place (... local: local ( 4 ) , projection: .ProjectionElems ) , target: someBasicBlockIdx ( basicBlockIdx ( 2 ) ) , unwind: unwindActionUnreachable ) , span: span ( 46 ) ) ) basicBlock (... statements: statement (... kind: statementKindStorageDead ( local ( 3 ) ) , span: span ( 48 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpSubUnchecked , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 49 ) ) .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 47 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 23 ) , span: span ( 50 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 51 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 52 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 43 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 1 ) , span: span ( 46 ) , mut: mutabilityNot ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 51 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 52 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) .VarDebugInfos , spreadArg: noLocal , span: span ( 53 ) ) ) ) - ty ( 22 ) |-> monoItemFn (... name: symbol ( "core::num::::unchecked_sub::precondition_check" ) , id: defId ( 4 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindStorageLive ( local ( 4 ) ) , span: span ( 55 ) ) statement (... kind: statementKindStorageLive ( local ( 6 ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 6 ) , projection: .ProjectionElems ) , rvalue: rvalueCheckedBinaryOp ( binOpSub , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 56 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 0 ) , ty ( 23 ) ) .ProjectionElems ) ) ) ) , span: span ( 57 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 5 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 6 ) , projection: projectionElemField ( fieldIdx ( 1 ) , ty ( 21 ) ) .ProjectionElems ) ) ) ) , span: span ( 58 ) ) statement (... kind: statementKindStorageDead ( local ( 6 ) ) , span: span ( 59 ) ) statement (... kind: statementKindStorageDead ( local ( 4 ) ) , span: span ( 55 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandCopy ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 54 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... span: span ( 60 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindZeroSized , ty: ty ( 24 ) , id: mirConstId ( 7 ) ) ) ) , args: operandConstant ( constOperand (... span: span ( 61 ) , userTy: noUserTypeAnnotationIndex , const: mirConst (... kind: constantKindAllocated ( allocation (... bytes: b"\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00" , provenance: provenanceMap (... ptrs: provenanceMapEntry (... provSize: 0 , allocId: allocId ( 0 ) ) .ProvenanceMapEntries ) , align: align ( 8 ) , mutability: mutabilityMut ) ) , ty: ty ( 25 ) , id: mirConstId ( 8 ) ) ) ) .Operands , destination: place (... local: local ( 3 ) , projection: .ProjectionElems ) , target: noBasicBlockIdx , unwind: unwindActionUnreachable ) , span: span ( 62 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 63 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 1 ) , span: span ( 64 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 65 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 62 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 23 ) , span: span ( 57 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 21 ) , span: span ( 58 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 27 ) , span: span ( 56 ) , mut: mutabilityMut ) .LocalDecls , argCount: 2 , varDebugInfo: varDebugInfo (... name: symbol ( "lhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 65 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "self" ) , sourceInfo: sourceInfo (... span: span ( 66 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "rhs" ) , sourceInfo: sourceInfo (... span: span ( 67 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 57 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 58 ) , scope: sourceScope ( 2 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 68 ) ) ) ) - ) - - requires // i16 invariants - 0 -Int (1 < i16::MAX) && (a - b < i16::MIN)); -} - -fn unchecked_op(a: i16, b: i16) -> i16 { - let unchecked_res = unsafe { a.unchecked_sub(b) }; - unchecked_res -} diff --git a/kmir-proofs/unchecked_arithmetic/unchecked_arithmetic.rs b/kmir-proofs/unchecked_arithmetic/unchecked_arithmetic.rs new file mode 100644 index 0000000000000..333ab6169bcca --- /dev/null +++ b/kmir-proofs/unchecked_arithmetic/unchecked_arithmetic.rs @@ -0,0 +1,125 @@ +// @kmir prove-rs: unchecked_add_i32 unchecked_sub_usize unchecked_mul_isize + +fn main() { + unchecked_add_i32(1,2); + unchecked_sub_usize(1, 2); + unchecked_mul_isize(1, 2); +} + +/// If the precondition is not met, the program is not executed (exits cleanly, ex falso quodlibet) +macro_rules! precondition { + ($pre:expr, $block:expr) => { + if $pre { $block } + }; +} + +///////////////////////////////////////////////////////////////////// + +fn unchecked_add_i32(a: i32, b: i32) { + + precondition!( + ((a as i64 + b as i64) <= i32::MAX as i64) && + ( a as i64 + b as i64 >= i32::MIN as i64), + // ========================================= + unsafe { + let result = a.unchecked_add(b); + assert!(result as i64 == a as i64 + b as i64) + } + ); +} + + +macro_rules! unchecked_add_claim_for { + ($name:ident, $ty:ident) => { + #[allow(unused)] + fn $name(a: $ty, b: $ty) + { + use std::$ty; + precondition!( + ((a as i128 + b as i128) <= $ty::MAX as i128) && + ( a as i128 + b as i128 >= $ty::MIN as i128), + // ========================================= + unsafe { + let result = a.unchecked_add(b); + assert!(result as i128 == a as i128 + b as i128) + } + ); + } + } +} + +unchecked_add_claim_for!(unchecked_add_i8 , i8 ); +unchecked_add_claim_for!(unchecked_add_i16 , i16 ); +// unchecked_add_claim_for!(unchecked_add_i32 , i32 ); // already above +unchecked_add_claim_for!(unchecked_add_i64 , i64 ); +unchecked_add_claim_for!(unchecked_add_isize, isize); +unchecked_add_claim_for!(unchecked_add_u8 , u8 ); +unchecked_add_claim_for!(unchecked_add_u16 , u16 ); +unchecked_add_claim_for!(unchecked_add_u32 , u32 ); +unchecked_add_claim_for!(unchecked_add_u64 , u64 ); +unchecked_add_claim_for!(unchecked_add_usize, usize); + +///////////////////////////////////////////////////////////////////// + +macro_rules! unchecked_sub_claim_for { + ($name:ident, $ty:ident) => { + #[allow(unused)] + fn $name(a: $ty, b: $ty) + { + use std::$ty; + precondition!( + ((a as i128 - b as i128) <= $ty::MAX as i128) && + ( a as i128 - b as i128 >= $ty::MIN as i128), + // ========================================= + unsafe { + let result = a.unchecked_sub(b); + assert!(result as i128 == a as i128 - b as i128) + } + ); + } + } +} + +unchecked_sub_claim_for!(unchecked_sub_i8 , i8 ); +unchecked_sub_claim_for!(unchecked_sub_i16 , i16 ); +unchecked_sub_claim_for!(unchecked_sub_i32 , i32 ); +unchecked_sub_claim_for!(unchecked_sub_i64 , i64 ); +unchecked_sub_claim_for!(unchecked_sub_isize, isize); +unchecked_sub_claim_for!(unchecked_sub_u8 , u8 ); +unchecked_sub_claim_for!(unchecked_sub_u16 , u16 ); +unchecked_sub_claim_for!(unchecked_sub_u32 , u32 ); +unchecked_sub_claim_for!(unchecked_sub_u64 , u64 ); +unchecked_sub_claim_for!(unchecked_sub_usize, usize); + +///////////////////////////////////////////////////////////////////// + +macro_rules! unchecked_mul_claim_for { + ($name:ident, $ty:ident) => { + #[allow(unused)] + fn $name(a: $ty, b: $ty) + { + use std::$ty; + precondition!( + ((a as i128 * b as i128) <= $ty::MAX as i128) && + ( a as i128 * b as i128 >= $ty::MIN as i128), + // ========================================= + unsafe { + let result = a.unchecked_mul(b); + assert!(result as i128 == a as i128 * b as i128) + } + ); + } + } +} + +unchecked_mul_claim_for!(unchecked_mul_i8 , i8 ); +unchecked_mul_claim_for!(unchecked_mul_i16 , i16 ); +unchecked_mul_claim_for!(unchecked_mul_i32 , i32 ); +unchecked_mul_claim_for!(unchecked_mul_i64 , i64 ); +unchecked_mul_claim_for!(unchecked_mul_isize, isize); +unchecked_mul_claim_for!(unchecked_mul_u8 , u8 ); +unchecked_mul_claim_for!(unchecked_mul_u16 , u16 ); +unchecked_mul_claim_for!(unchecked_mul_u32 , u32 ); +// insufficient bit size for precondition +// unchecked_mul_claim_for!(unchecked_mul_u64 , u64 ); +// unchecked_mul_claim_for!(unchecked_mul_usize, usize); From 0ce0bbba780e0f14c949221729fbf0601c1c25ac Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Fri, 9 May 2025 16:20:13 +1000 Subject: [PATCH 27/29] remove maximum-example-proof (outdated) --- kmir-proofs/maximum-example-proof/README.md | 194 -- .../maximum-example-proof/main-max-with-lt.rs | 18 - .../main-max-with-lt.smir.dot | 135 - .../main-max-with-lt.smir.json | 2676 ----------------- .../maximum-example-proof/maximum-spec.k | 73 - 5 files changed, 3096 deletions(-) delete mode 100644 kmir-proofs/maximum-example-proof/README.md delete mode 100644 kmir-proofs/maximum-example-proof/main-max-with-lt.rs delete mode 100644 kmir-proofs/maximum-example-proof/main-max-with-lt.smir.dot delete mode 100644 kmir-proofs/maximum-example-proof/main-max-with-lt.smir.json delete mode 100644 kmir-proofs/maximum-example-proof/maximum-spec.k diff --git a/kmir-proofs/maximum-example-proof/README.md b/kmir-proofs/maximum-example-proof/README.md deleted file mode 100644 index 05cfdfe03c77e..0000000000000 --- a/kmir-proofs/maximum-example-proof/README.md +++ /dev/null @@ -1,194 +0,0 @@ -# Turning the max-with-lt program into a property proof - -## Example program that we start from - -```rust -fn main() { - - let a:usize = 42; - let b:usize = 43; - let c:usize = 0; - - let result = maximum(a, b, c); - - assert!(result >= a && result >= b && result >= c - && (result == a || result == b || result == c ) ); -} - -fn maximum(a: usize, b: usize, c: usize) -> usize { - // max(a, max(b, c)) - let max_ab = if a < b {b} else {a}; - if max_ab < c {c} else {max_ab} -} -``` - -We want to prove a property of `maximum`: -- When called with `a`, `b`, and `c`, -- the `result` will be greater or equal all of the arguments, -- and equal to one (or more) of them. - -The `main` program above states this using some concrete values of -`a`, `b`, and `c`. We will run this program to construct a general -symbolic claim and prove it. - -In a future version, we will be able to start directly with the -`maximum` function call and provide symbolic arguments to it. This -will save some manual work setting up the claim file and fits the -target of proving based on property tests. - -## Extracting Stable MIR for the program - -Before we can run the program using the MIR semantics, we have to -compile it with a special compiler to extract Stable MIR from it. This -step differs a bit depending on whether the program has multiple -crates, in our case it is just a simple invocation of the extraction -tool. Assuming the tool is installed as `stable-mir-json` (as in the -docker image provided): - -```shell -stable-mir-json -Zno-codegen --out-dir . main-max-with-lt.rs -``` - -This creates `main-max-with-lt.smir.json`. The `-Zno-codegen` and -`--out-dir` options are passed through to the underlying `rustc` so no -executable is generated. - -The Stable MIR for the program can also be rendered as a graph, using -`--dot` as the first option. This creates `main-max-with-lt.smir.dot`. - -```shell -stable-mir-json --dot -Zno-codegen --out-dir . main-max-with-lt.rs -``` -## Constructing the claim by executing `main` to certain points -Through concrete execution of the parsed K program we can interrupt -the execution after a given number of rewrite steps to inspect the -intermediate state. This will help us with writing our claim manually -until the process is automated. - -1. The program (`main`) reaches the call to `maximum` after 25 steps. - The following command runs it and displays the resulting program state. - - ```shell - kmir run main-max-with-lt.smir.json --depth 25 | less -S - ``` - - Arguments `a`, `b`, and `c` are initialised to `Integer`s as `locals[1]` to `locals[3]` - - A `call` terminator calling function `ty(25)` is executed next (front of the `k` cell) - - The function table contains `ty(25) -> ... code of "maximum"`. - - Other state (how `main` continues, its other local variables, - and some internal functions) is not relevant to the proof we - want to perform. - -``` - - - #execTerminator ( terminator (... kind: terminatorKindCall (... func: operandConstant ( constOperand (... (truncated) - - - noReturn - - - ty ( -1 ) - - - - ListItem ( basicBlock (... statements: ...(truncated) - ... (truncated) - - - ty ( -1 ) - - - place (... local: local ( -1 ) , projection: .ProjectionElems ) - - - noBasicBlockIdx - - - unwindActionUnreachable - - - ListItem ( newLocal ( ty ( 1 ) , mutabilityMut ) ) - ListItem ( typedValue ( Integer ( 42 , 64 , false ) , ty ( 26 ) , mutabilityNot ) ) - ListItem ( typedValue ( Integer ( 22 , 64 , false ) , ty ( 26 ) , mutabilityNot ) ) - ListItem ( typedValue ( Integer ( 0 , 64 , false ) , ty ( 26 ) , mutabilityNot ) ) - ListItem ( newLocal ( ty ( 26 ) , mutabilityNot ) ) - ...(truncated) - - - - .List - - - ty ( 13 ) |-> monoItemFn (... (truncated) - ... - ty ( 25 ) |-> monoItemFn (... name: symbol ( "maximum" ) , id: defId ( 7 ) , body: someBody ( body (... (truncated) - ... - - ...(truncated) -``` - -2. The program executes for a total of 110 steps to reach the point - where it `return`s from `maximum`. The following command runs it - and displays the resulting program state. - - ```shell - kmir run main-max-with-lt.smir.json --depth 110 | less -S - ``` - - The value `locals[0]` is now set to an `Integer`. This will be the target of our assertions. - - A `return` terminator is executed next (front of the `k` cell), it will return `locals[0]` - - It should be an `Integer` with the desired properties as stated above - -State 1. defines our start state for the claim. Irrelevant parts are -elided (replaced by variables). - -* The code of the `maximum` function in the `functions` table needs to - be kept. We also keep its identifier `ty(25)`. Other functions can - be removed (we won't perform a return). -* The `call` terminator is kept, calling `ty(25)` with arguments from - `locals[1,2,3]`. `target` is modified to be `noBasicBlockIdx` to - force termination of the prover (no block to jump back to). -* The four locals `0` - `3` are required in their original order to - provide the function arguments. The values of `a`, `b`, and `c` in - locals `1` - `3` are replaced with symbolic variables used in the - proof. -* We could keep all other locals but do not have to (however it is - important that the list of locals has a known length). -* `main`s other details in `currentFrame` are irrelevant and elided. - - -State 2. is the end state, where all that matters is the returned value. - -* The `locals` list should contain this `?RESULT` value at index `0` -* The `?RESULT` value should have the properties stated (equivalent to - the assertion in `main`) -* Because of the modified `target`, the program should end, i.e., have - an `#EndProgram` in the `k` cell. - -The above is written as a _claim_ in K framework language into a -`maximum-spec.k` file. Most of the syntax can be copied from the -output of the `kmir run` commands above, and irrelevant parts replaced -by `_` (LHS) or `?_` (RHS). - -Alternatively, it is possible to construct a claim that the entire -rest of the program after initialising the variables will result in -the desired `?RESULT`, i.e., the assertion in `main` is executed -successfully and the program ends in `#EndProgram` after checking -it. This would require more steps. - -## Running the prover on the claim and viewing the proof - -Now that we have constructed claim, we can use the KMIR verifier -to perform symbolic execution, and can view the state of proof -through the KMIR proof viewer. - -```shell -kmir prove run maximum-spec.k --proof-dir ./proof -``` - -The proof steps are saved in the `$PWD/proof` directory for later -inspection using `kmir prove view`. This is especially important when -the proof does _not_ succeed immediately. - -```shell -kmir prove view MAXIMUM-SPEC.maximum-spec --proof-dir ./proof -``` diff --git a/kmir-proofs/maximum-example-proof/main-max-with-lt.rs b/kmir-proofs/maximum-example-proof/main-max-with-lt.rs deleted file mode 100644 index 448c2cb9d4574..0000000000000 --- a/kmir-proofs/maximum-example-proof/main-max-with-lt.rs +++ /dev/null @@ -1,18 +0,0 @@ - -fn main() { - - let a:usize = 42; - let b:usize = 22; - let c:usize = 0; - - let result = maximum(a, b, c); - - assert!(result >= a && result >= b && result >= c - && (result == a || result == b || result == c ) ); -} - -fn maximum(a: usize, b: usize, c: usize) -> usize { - // max(a, max(b, c)) - let max_ab = if a < b {b} else {a}; - if max_ab < c {c} else {max_ab} -} diff --git a/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.dot b/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.dot deleted file mode 100644 index 6da5f7ff3ac9e..0000000000000 --- a/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.dot +++ /dev/null @@ -1,135 +0,0 @@ -digraph { - label="main_max_with_lt"; - node [shape=rectangle]; - X8b0ac2e54b9a91_0 [label="NoOp: ", color=red]; - Xac08878333d72e42_0 [label="_ZN4core9panicking5panic1\n7h941160ead03e2d54E", color=red]; - Xc987e5ecea6cc82b_0 [label="_ZN3std2rt19lang_start_in\nternal17h018b8394ba015d86\nE", color=red]; - X3c6542d96320ad67_0 [label="Intr: \nblack_box", color=red]; - subgraph cluster_0 { - label="main"; - style="filled"; - color=palegreen; - X37252ea5c5b3ce2a_0 -> X37252ea5c5b3ce2a_1 [label="4"]; - X37252ea5c5b3ce2a_0 [label="1 <- Use(const :: usize)\l2 <- Use(const :: usize)\l3 <- Use(const :: usize)\lCall\l"]; - X37252ea5c5b3ce2a_1 -> X37252ea5c5b3ce2a_7 [label="0"]; - X37252ea5c5b3ce2a_1 -> X37252ea5c5b3ce2a_2 [label="other"]; - X37252ea5c5b3ce2a_1 [label="5 <- Ge(cp(4), cp(1))\lSwitchInt mv(5)\l"]; - X37252ea5c5b3ce2a_2 -> X37252ea5c5b3ce2a_7 [label="0"]; - X37252ea5c5b3ce2a_2 -> X37252ea5c5b3ce2a_3 [label="other"]; - X37252ea5c5b3ce2a_2 [label="6 <- Ge(cp(4), cp(2))\lSwitchInt mv(6)\l"]; - X37252ea5c5b3ce2a_3 -> X37252ea5c5b3ce2a_7 [label="0"]; - X37252ea5c5b3ce2a_3 -> X37252ea5c5b3ce2a_4 [label="other"]; - X37252ea5c5b3ce2a_3 [label="7 <- Ge(cp(4), cp(3))\lSwitchInt mv(7)\l"]; - X37252ea5c5b3ce2a_4 -> X37252ea5c5b3ce2a_5 [label="0"]; - X37252ea5c5b3ce2a_4 -> X37252ea5c5b3ce2a_8 [label="other"]; - X37252ea5c5b3ce2a_4 [label="8 <- Eq(cp(4), cp(1))\lSwitchInt mv(8)\l"]; - X37252ea5c5b3ce2a_5 -> X37252ea5c5b3ce2a_6 [label="0"]; - X37252ea5c5b3ce2a_5 -> X37252ea5c5b3ce2a_8 [label="other"]; - X37252ea5c5b3ce2a_5 [label="9 <- Eq(cp(4), cp(2))\lSwitchInt mv(9)\l"]; - X37252ea5c5b3ce2a_6 -> X37252ea5c5b3ce2a_7 [label="0"]; - X37252ea5c5b3ce2a_6 -> X37252ea5c5b3ce2a_8 [label="other"]; - X37252ea5c5b3ce2a_6 [label="10 <- Eq(cp(4), cp(3))\lSwitchInt mv(10)\l"]; - X37252ea5c5b3ce2a_7 [label="Call\l"]; - X37252ea5c5b3ce2a_8 [label="Return\l"]; - } - X37252ea5c5b3ce2a_0 -> X9585eeb1b7d3a83d_0 [label="cp(1),cp(2),cp(3)"]; - X37252ea5c5b3ce2a_7 -> Xac08878333d72e42_0 [label="const :: &str"]; - subgraph cluster_1 { - label="<() \nas \nstd::process::Termination\n>::report"; - style="filled"; - color=lightgray; - X5c233e009f84aa6c_0 [label="0 <- Use(const :: std::process::ExitCode)\lReturn\l"]; - } - subgraph cluster_2 { - label="std::sys::backtrace::__ru\nst_begin_short_backtrace:\n:"; - style="filled"; - color=lightgray; - X83f8b52e3f0ef4c5_0 -> X83f8b52e3f0ef4c5_1 [label="0"]; - X83f8b52e3f0ef4c5_0 [label="Call\l"]; - X83f8b52e3f0ef4c5_1 -> X83f8b52e3f0ef4c5_2 [label="2"]; - X83f8b52e3f0ef4c5_1 [label="Call\l"]; - X83f8b52e3f0ef4c5_2 [label="Return\l"]; - } - X83f8b52e3f0ef4c5_0 -> X5153bc83e282e268_0 [label="mv(1),const :: ()"]; - X83f8b52e3f0ef4c5_1 -> X3c6542d96320ad67_0 [label="const :: ()"]; - subgraph cluster_3 { - label="std::rt::lang_start::<()>"; - style="filled"; - color=lightgray; - X88af70ac7219a434_0 -> X88af70ac7219a434_1 [label="5"]; - X88af70ac7219a434_0 [label="Storage Live _5\lStorage Live _6\lStorage Live _8\l8 <- Closure (cp(1))\l7 <- & 8\l6 <- Cast-PointerCoercion(Unsize) cp(7)\lCall\l"]; - X88af70ac7219a434_1 [label="Storage Dead _6\l0 <- Use(cp(5 as VariantIdx(0).0))\lStorage Dead _8\lStorage Dead _5\lReturn\l"]; - } - X88af70ac7219a434_0 -> Xc987e5ecea6cc82b_0 [label="mv(6),mv(2),mv(3),mv(4)"]; - subgraph cluster_4 { - label="<{closure@std::rt::lang_s\ntart<()>::{closure#0}} \nas \nstd::ops::FnOnce<()>>::ca\nll_once"; - style="filled"; - color=lightgray; - X2aeea2bef42114da_0 -> X2aeea2bef42114da_1 [label="0"]; - X2aeea2bef42114da_0 [label="Call\l"]; - X2aeea2bef42114da_1 [label="Return\l"]; - } - X2aeea2bef42114da_0 -> X58ae416f9afa06ac_0 [label="mv(*1),mv(2)"]; - subgraph cluster_5 { - label=">::ca\nll_once"; - style="filled"; - color=lightgray; - X5153bc83e282e268_0 -> X5153bc83e282e268_1 [label="0"]; - X5153bc83e282e268_0 [label="Call\l"]; - X5153bc83e282e268_1 [label="Return\l"]; - } - X5153bc83e282e268_0 -> X5153bc83e282e268_0: 1 [label=""]; - subgraph cluster_6 { - label="std::rt::lang_start::<()>\n::{closure#0}"; - style="filled"; - color=lightgray; - Xf1c2e3e2362b71b1_0 -> Xf1c2e3e2362b71b1_1 [label="3"]; - Xf1c2e3e2362b71b1_0 [label="Storage Live _2\lStorage Live _3\lStorage Live _4\l4 <- Use(cp(*1.0))\lCall\l"]; - Xf1c2e3e2362b71b1_1 -> Xf1c2e3e2362b71b1_2 [label="2"]; - Xf1c2e3e2362b71b1_1 [label="Storage Dead _4\lCall\l"]; - Xf1c2e3e2362b71b1_2 [label="Storage Dead _3\lStorage Live _5\l5 <- & 2.0\lStorage Live _6\l6 <- Use(cp(2.0.0))\l0 <- Cast-IntToInt mv(6)\lStorage Dead _6\lStorage Dead _5\lStorage Dead _2\lReturn\l"]; - } - Xf1c2e3e2362b71b1_0 -> X83f8b52e3f0ef4c5_0 [label="mv(4)"]; - Xf1c2e3e2362b71b1_1 -> X5c233e009f84aa6c_0 [label="mv(3)"]; - subgraph cluster_7 { - label="maximum"; - style="filled"; - color=palegreen; - X9585eeb1b7d3a83d_0 -> X9585eeb1b7d3a83d_2 [label="0"]; - X9585eeb1b7d3a83d_0 -> X9585eeb1b7d3a83d_1 [label="other"]; - X9585eeb1b7d3a83d_0 [label="5 <- Lt(cp(1), cp(2))\lSwitchInt mv(5)\l"]; - X9585eeb1b7d3a83d_1 -> X9585eeb1b7d3a83d_3; - X9585eeb1b7d3a83d_1 [label="4 <- Use(cp(2))\lGoto\l"]; - X9585eeb1b7d3a83d_2 -> X9585eeb1b7d3a83d_3; - X9585eeb1b7d3a83d_2 [label="4 <- Use(cp(1))\lGoto\l"]; - X9585eeb1b7d3a83d_3 -> X9585eeb1b7d3a83d_5 [label="0"]; - X9585eeb1b7d3a83d_3 -> X9585eeb1b7d3a83d_4 [label="other"]; - X9585eeb1b7d3a83d_3 [label="7 <- Use(cp(4))\l6 <- Lt(mv(7), cp(3))\lSwitchInt mv(6)\l"]; - X9585eeb1b7d3a83d_4 -> X9585eeb1b7d3a83d_6; - X9585eeb1b7d3a83d_4 [label="0 <- Use(cp(3))\lGoto\l"]; - X9585eeb1b7d3a83d_5 -> X9585eeb1b7d3a83d_6; - X9585eeb1b7d3a83d_5 [label="0 <- Use(cp(4))\lGoto\l"]; - X9585eeb1b7d3a83d_6 [label="Return\l"]; - } - subgraph cluster_8 { - label="<{closure@std::rt::lang_s\ntart<()>::{closure#0}} \nas \nstd::ops::FnOnce<()>>::ca\nll_once"; - style="filled"; - color=lightgray; - X58ae416f9afa06ac_0 -> X58ae416f9afa06ac_3 [label="Cleanup"]; - X58ae416f9afa06ac_0 -> X58ae416f9afa06ac_1 [label="0"]; - X58ae416f9afa06ac_0 [label="3 <- &mut 1\lCall\l"]; - X58ae416f9afa06ac_1 -> X58ae416f9afa06ac_2; - X58ae416f9afa06ac_1 [label="Drop 1\l"]; - X58ae416f9afa06ac_2 [label="Return\l"]; - X58ae416f9afa06ac_3 -> X58ae416f9afa06ac_4; - X58ae416f9afa06ac_3 [label="Drop 1\l"]; - X58ae416f9afa06ac_4 [label="Resume\l"]; - } - X58ae416f9afa06ac_0 -> Xf1c2e3e2362b71b1_0 [label="mv(3),mv(2)"]; - subgraph cluster_9 { - label="std::ptr::drop_in_place::\n<{closure@std::rt::lang_s\ntart<()>::{closure#0}}>"; - style="filled"; - color=lightgray; - Xefb68cd7a0d5be14_0 [label="Return\l"]; - } -} diff --git a/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.json b/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.json deleted file mode 100644 index 02182bb8e2e51..0000000000000 --- a/kmir-proofs/maximum-example-proof/main-max-with-lt.smir.json +++ /dev/null @@ -1,2676 +0,0 @@ -{ - "name": "main_max_with_lt", - "crate_id": 5373935543796547206, - "allocs": [ - [ - 1, - { - "Memory": { - "bytes": [ - 97, - 115, - 115, - 101, - 114, - 116, - 105, - 111, - 110, - 32, - 102, - 97, - 105, - 108, - 101, - 100, - 58, - 32, - 114, - 101, - 115, - 117, - 108, - 116, - 32, - 62, - 61, - 32, - 97, - 32, - 38, - 38, - 32, - 114, - 101, - 115, - 117, - 108, - 116, - 32, - 62, - 61, - 32, - 98, - 32, - 38, - 38, - 32, - 114, - 101, - 115, - 117, - 108, - 116, - 32, - 62, - 61, - 32, - 99, - 32, - 38, - 38, - 10, - 32, - 32, - 32, - 32, - 40, - 114, - 101, - 115, - 117, - 108, - 116, - 32, - 61, - 61, - 32, - 97, - 32, - 124, - 124, - 32, - 114, - 101, - 115, - 117, - 108, - 116, - 32, - 61, - 61, - 32, - 98, - 32, - 124, - 124, - 32, - 114, - 101, - 115, - 117, - 108, - 116, - 32, - 61, - 61, - 32, - 99, - 41 - ], - "provenance": { - "ptrs": [] - }, - "align": 1, - "mutability": "Not" - } - } - ] - ], - "functions": [ - [ - 13, - { - "NormalSym": "_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h3491d8bffa495004E" - } - ], - [ - 23, - { - "NormalSym": "_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hb1d17c99442a8691E" - } - ], - [ - 14, - { - "NormalSym": "_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17hf5d9ff8f37d5cc66E" - } - ], - [ - 0, - { - "NormalSym": "_ZN3std2rt19lang_start_internal17h018b8394ba015d86E" - } - ], - [ - 21, - { - "NormalSym": "_ZN4core3ops8function6FnOnce9call_once17h35ef4f3d7035a7c0E" - } - ], - [ - 19, - { - "NormalSym": "_ZN4core3ops8function6FnOnce9call_once17h68df99d5221b15e2E" - } - ], - [ - 20, - { - "IntrinsicSym": "black_box" - } - ], - [ - 25, - { - "NormalSym": "_ZN16main_max_with_lt7maximum17h5e37abb753494251E" - } - ], - [ - 27, - { - "NormalSym": "_ZN4core9panicking5panic17h941160ead03e2d54E" - } - ], - [ - 33, - { - "NoOpSym": "" - } - ] - ], - "uneval_consts": [], - "items": [ - { - "symbol_name": "_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hb1d17c99442a8691E", - "mono_item_kind": { - "MonoItemFn": { - "name": "std::rt::lang_start::<()>::{closure#0}", - "id": 1, - "body": { - "blocks": [ - { - "statements": [ - { - "kind": { - "StorageLive": 2 - }, - "span": 16 - }, - { - "kind": { - "StorageLive": 3 - }, - "span": 15 - }, - { - "kind": { - "StorageLive": 4 - }, - "span": 17 - }, - { - "kind": { - "Assign": [ - { - "local": 4, - "projection": [] - }, - { - "Use": { - "Copy": { - "local": 1, - "projection": [ - "Deref", - { - "Field": [ - 0, - 7 - ] - } - ] - } - } - } - ] - }, - "span": 17 - } - ], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 14, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 13, - "id": 1 - } - } - }, - "args": [ - { - "Move": { - "local": 4, - "projection": [] - } - } - ], - "destination": { - "local": 3, - "projection": [] - }, - "target": 1, - "unwind": "Continue" - } - }, - "span": 15 - } - }, - { - "statements": [ - { - "kind": { - "StorageDead": 4 - }, - "span": 19 - } - ], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 18, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 14, - "id": 2 - } - } - }, - "args": [ - { - "Move": { - "local": 3, - "projection": [] - } - } - ], - "destination": { - "local": 2, - "projection": [] - }, - "target": 2, - "unwind": "Continue" - } - }, - "span": 16 - } - }, - { - "statements": [ - { - "kind": { - "StorageDead": 3 - }, - "span": 21 - }, - { - "kind": { - "StorageLive": 5 - }, - "span": 22 - }, - { - "kind": { - "Assign": [ - { - "local": 5, - "projection": [] - }, - { - "Ref": [ - { - "kind": "ReErased" - }, - "Shared", - { - "local": 2, - "projection": [ - { - "Field": [ - 0, - 15 - ] - } - ] - } - ] - } - ] - }, - "span": 22 - }, - { - "kind": { - "StorageLive": 6 - }, - "span": 23 - }, - { - "kind": { - "Assign": [ - { - "local": 6, - "projection": [] - }, - { - "Use": { - "Copy": { - "local": 2, - "projection": [ - { - "Field": [ - 0, - 15 - ] - }, - { - "Field": [ - 0, - 9 - ] - } - ] - } - } - } - ] - }, - "span": 23 - }, - { - "kind": { - "Assign": [ - { - "local": 0, - "projection": [] - }, - { - "Cast": [ - "IntToInt", - { - "Move": { - "local": 6, - "projection": [] - } - }, - 16 - ] - } - ] - }, - "span": 24 - }, - { - "kind": { - "StorageDead": 6 - }, - "span": 25 - }, - { - "kind": { - "StorageDead": 5 - }, - "span": 26 - }, - { - "kind": { - "StorageDead": 2 - }, - "span": 27 - } - ], - "terminator": { - "kind": "Return", - "span": 20 - } - } - ], - "locals": [ - { - "ty": 16, - "span": 28, - "mutability": "Mut" - }, - { - "ty": 11, - "span": 3, - "mutability": "Mut" - }, - { - "ty": 17, - "span": 16, - "mutability": "Mut" - }, - { - "ty": 1, - "span": 15, - "mutability": "Mut" - }, - { - "ty": 7, - "span": 17, - "mutability": "Mut" - }, - { - "ty": 18, - "span": 22, - "mutability": "Mut" - }, - { - "ty": 9, - "span": 23, - "mutability": "Mut" - } - ], - "arg_count": 1, - "var_debug_info": [ - { - "name": "main", - "source_info": { - "span": 9, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 1, - "projection": [ - "Deref", - { - "Field": [ - 0, - 7 - ] - } - ] - } - }, - "argument_index": null - }, - { - "name": "self", - "source_info": { - "span": 29, - "scope": 1 - }, - "composite": null, - "value": { - "Place": { - "local": 2, - "projection": [] - } - }, - "argument_index": 1 - }, - { - "name": "self", - "source_info": { - "span": 30, - "scope": 2 - }, - "composite": null, - "value": { - "Place": { - "local": 5, - "projection": [] - } - }, - "argument_index": 1 - } - ], - "spread_arg": null, - "span": 3 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN4core3ops8function6FnOnce9call_once17h68df99d5221b15e2E", - "mono_item_kind": { - "MonoItemFn": { - "name": ">::call_once", - "id": 3, - "body": { - "blocks": [ - { - "statements": [], - "terminator": { - "kind": { - "Call": { - "func": { - "Move": { - "local": 1, - "projection": [] - } - }, - "args": [], - "destination": { - "local": 0, - "projection": [] - }, - "target": 1, - "unwind": "Continue" - } - }, - "span": 43 - } - }, - { - "statements": [], - "terminator": { - "kind": "Return", - "span": 43 - } - } - ], - "locals": [ - { - "ty": 1, - "span": 43, - "mutability": "Mut" - }, - { - "ty": 7, - "span": 43, - "mutability": "Not" - }, - { - "ty": 1, - "span": 43, - "mutability": "Not" - } - ], - "arg_count": 2, - "var_debug_info": [], - "spread_arg": 2, - "span": 43 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN3std2rt10lang_start17h8f98b99a20edb596E", - "mono_item_kind": { - "MonoItemFn": { - "name": "std::rt::lang_start::<()>", - "id": 0, - "body": { - "blocks": [ - { - "statements": [ - { - "kind": { - "StorageLive": 5 - }, - "span": 1 - }, - { - "kind": { - "StorageLive": 6 - }, - "span": 2 - }, - { - "kind": { - "StorageLive": 8 - }, - "span": 3 - }, - { - "kind": { - "Assign": [ - { - "local": 8, - "projection": [] - }, - { - "Aggregate": [ - { - "Closure": [ - 1, - [ - { - "Type": 1 - }, - { - "Type": 2 - }, - { - "Type": 3 - }, - { - "Type": 4 - } - ] - ] - }, - [ - { - "Copy": { - "local": 1, - "projection": [] - } - } - ] - ] - } - ] - }, - "span": 3 - }, - { - "kind": { - "Assign": [ - { - "local": 7, - "projection": [] - }, - { - "Ref": [ - { - "kind": "ReErased" - }, - "Shared", - { - "local": 8, - "projection": [] - } - ] - } - ] - }, - "span": 2 - }, - { - "kind": { - "Assign": [ - { - "local": 6, - "projection": [] - }, - { - "Cast": [ - { - "PointerCoercion": "Unsize" - }, - { - "Copy": { - "local": 7, - "projection": [] - } - }, - 5 - ] - } - ] - }, - "span": 2 - } - ], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 0, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 0, - "id": 0 - } - } - }, - "args": [ - { - "Move": { - "local": 6, - "projection": [] - } - }, - { - "Move": { - "local": 2, - "projection": [] - } - }, - { - "Move": { - "local": 3, - "projection": [] - } - }, - { - "Move": { - "local": 4, - "projection": [] - } - } - ], - "destination": { - "local": 5, - "projection": [] - }, - "target": 1, - "unwind": "Continue" - } - }, - "span": 1 - } - }, - { - "statements": [ - { - "kind": { - "StorageDead": 6 - }, - "span": 5 - }, - { - "kind": { - "Assign": [ - { - "local": 0, - "projection": [] - }, - { - "Use": { - "Copy": { - "local": 5, - "projection": [ - { - "Downcast": 0 - }, - { - "Field": [ - 0, - 6 - ] - } - ] - } - } - } - ] - }, - "span": 6 - }, - { - "kind": { - "StorageDead": 8 - }, - "span": 7 - }, - { - "kind": { - "StorageDead": 5 - }, - "span": 7 - } - ], - "terminator": { - "kind": "Return", - "span": 4 - } - } - ], - "locals": [ - { - "ty": 6, - "span": 8, - "mutability": "Mut" - }, - { - "ty": 7, - "span": 9, - "mutability": "Not" - }, - { - "ty": 6, - "span": 10, - "mutability": "Not" - }, - { - "ty": 8, - "span": 11, - "mutability": "Not" - }, - { - "ty": 9, - "span": 12, - "mutability": "Not" - }, - { - "ty": 10, - "span": 1, - "mutability": "Mut" - }, - { - "ty": 5, - "span": 2, - "mutability": "Mut" - }, - { - "ty": 11, - "span": 2, - "mutability": "Not" - }, - { - "ty": 12, - "span": 3, - "mutability": "Not" - } - ], - "arg_count": 4, - "var_debug_info": [ - { - "name": "main", - "source_info": { - "span": 9, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 1, - "projection": [] - } - }, - "argument_index": 1 - }, - { - "name": "argc", - "source_info": { - "span": 10, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 2, - "projection": [] - } - }, - "argument_index": 2 - }, - { - "name": "argv", - "source_info": { - "span": 11, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 3, - "projection": [] - } - }, - "argument_index": 3 - }, - { - "name": "sigpipe", - "source_info": { - "span": 12, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 4, - "projection": [] - } - }, - "argument_index": 4 - }, - { - "name": "v", - "source_info": { - "span": 6, - "scope": 1 - }, - "composite": null, - "value": { - "Place": { - "local": 0, - "projection": [] - } - }, - "argument_index": null - } - ], - "spread_arg": null, - "span": 13 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN16main_max_with_lt7maximum17h5e37abb753494251E", - "mono_item_kind": { - "MonoItemFn": { - "name": "maximum", - "id": 7, - "body": { - "blocks": [ - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 5, - "projection": [] - }, - { - "BinaryOp": [ - "Lt", - { - "Copy": { - "local": 1, - "projection": [] - } - }, - { - "Copy": { - "local": 2, - "projection": [] - } - } - ] - } - ] - }, - "span": 69 - } - ], - "terminator": { - "kind": { - "SwitchInt": { - "discr": { - "Move": { - "local": 5, - "projection": [] - } - }, - "targets": { - "branches": [ - [ - 0, - 2 - ] - ], - "otherwise": 1 - } - } - }, - "span": 69 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 4, - "projection": [] - }, - { - "Use": { - "Copy": { - "local": 2, - "projection": [] - } - } - } - ] - }, - "span": 71 - } - ], - "terminator": { - "kind": { - "Goto": { - "target": 3 - } - }, - "span": 70 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 4, - "projection": [] - }, - { - "Use": { - "Copy": { - "local": 1, - "projection": [] - } - } - } - ] - }, - "span": 72 - } - ], - "terminator": { - "kind": { - "Goto": { - "target": 3 - } - }, - "span": 70 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 7, - "projection": [] - }, - { - "Use": { - "Copy": { - "local": 4, - "projection": [] - } - } - } - ] - }, - "span": 74 - }, - { - "kind": { - "Assign": [ - { - "local": 6, - "projection": [] - }, - { - "BinaryOp": [ - "Lt", - { - "Move": { - "local": 7, - "projection": [] - } - }, - { - "Copy": { - "local": 3, - "projection": [] - } - } - ] - } - ] - }, - "span": 73 - } - ], - "terminator": { - "kind": { - "SwitchInt": { - "discr": { - "Move": { - "local": 6, - "projection": [] - } - }, - "targets": { - "branches": [ - [ - 0, - 5 - ] - ], - "otherwise": 4 - } - } - }, - "span": 73 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 0, - "projection": [] - }, - { - "Use": { - "Copy": { - "local": 3, - "projection": [] - } - } - } - ] - }, - "span": 76 - } - ], - "terminator": { - "kind": { - "Goto": { - "target": 6 - } - }, - "span": 75 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 0, - "projection": [] - }, - { - "Use": { - "Copy": { - "local": 4, - "projection": [] - } - } - } - ] - }, - "span": 77 - } - ], - "terminator": { - "kind": { - "Goto": { - "target": 6 - } - }, - "span": 75 - } - }, - { - "statements": [], - "terminator": { - "kind": "Return", - "span": 78 - } - } - ], - "locals": [ - { - "ty": 26, - "span": 79, - "mutability": "Mut" - }, - { - "ty": 26, - "span": 80, - "mutability": "Not" - }, - { - "ty": 26, - "span": 81, - "mutability": "Not" - }, - { - "ty": 26, - "span": 82, - "mutability": "Not" - }, - { - "ty": 26, - "span": 83, - "mutability": "Not" - }, - { - "ty": 29, - "span": 69, - "mutability": "Mut" - }, - { - "ty": 29, - "span": 73, - "mutability": "Mut" - }, - { - "ty": 26, - "span": 74, - "mutability": "Mut" - } - ], - "arg_count": 3, - "var_debug_info": [ - { - "name": "a", - "source_info": { - "span": 80, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 1, - "projection": [] - } - }, - "argument_index": 1 - }, - { - "name": "b", - "source_info": { - "span": 81, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 2, - "projection": [] - } - }, - "argument_index": 2 - }, - { - "name": "c", - "source_info": { - "span": 82, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 3, - "projection": [] - } - }, - "argument_index": 3 - }, - { - "name": "max_ab", - "source_info": { - "span": 83, - "scope": 1 - }, - "composite": null, - "value": { - "Place": { - "local": 4, - "projection": [] - } - }, - "argument_index": null - } - ], - "spread_arg": null, - "span": 84 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN4core3ops8function6FnOnce9call_once17h35ef4f3d7035a7c0E", - "mono_item_kind": { - "MonoItemFn": { - "name": "<{closure@std::rt::lang_start<()>::{closure#0}} as std::ops::FnOnce<()>>::call_once", - "id": 3, - "body": { - "blocks": [ - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 3, - "projection": [] - }, - { - "Ref": [ - { - "kind": "ReErased" - }, - { - "Mut": { - "kind": "Default" - } - }, - { - "local": 1, - "projection": [] - } - ] - } - ] - }, - "span": 43 - } - ], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 43, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 23, - "id": 7 - } - } - }, - "args": [ - { - "Move": { - "local": 3, - "projection": [] - } - }, - { - "Move": { - "local": 2, - "projection": [] - } - } - ], - "destination": { - "local": 0, - "projection": [] - }, - "target": 1, - "unwind": { - "Cleanup": 3 - } - } - }, - "span": 43 - } - }, - { - "statements": [], - "terminator": { - "kind": { - "Drop": { - "place": { - "local": 1, - "projection": [] - }, - "target": 2, - "unwind": "Continue" - } - }, - "span": 43 - } - }, - { - "statements": [], - "terminator": { - "kind": "Return", - "span": 43 - } - }, - { - "statements": [], - "terminator": { - "kind": { - "Drop": { - "place": { - "local": 1, - "projection": [] - }, - "target": 4, - "unwind": "Terminate" - } - }, - "span": 43 - } - }, - { - "statements": [], - "terminator": { - "kind": "Resume", - "span": 43 - } - } - ], - "locals": [ - { - "ty": 16, - "span": 43, - "mutability": "Mut" - }, - { - "ty": 12, - "span": 43, - "mutability": "Not" - }, - { - "ty": 1, - "span": 43, - "mutability": "Not" - }, - { - "ty": 24, - "span": 43, - "mutability": "Not" - } - ], - "arg_count": 2, - "var_debug_info": [], - "spread_arg": 2, - "span": 43 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN16main_max_with_lt4main17h96bac61ef98236a2E", - "mono_item_kind": { - "MonoItemFn": { - "name": "main", - "id": 6, - "body": { - "blocks": [ - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 1, - "projection": [] - }, - { - "Use": { - "Constant": { - "span": 52, - "user_ty": null, - "const_": { - "kind": { - "Allocated": { - "bytes": [ - 42, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "provenance": { - "ptrs": [] - }, - "align": 8, - "mutability": "Mut" - } - }, - "ty": 26, - "id": 10 - } - } - } - } - ] - }, - "span": 52 - }, - { - "kind": { - "Assign": [ - { - "local": 2, - "projection": [] - }, - { - "Use": { - "Constant": { - "span": 53, - "user_ty": null, - "const_": { - "kind": { - "Allocated": { - "bytes": [ - 22, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "provenance": { - "ptrs": [] - }, - "align": 8, - "mutability": "Mut" - } - }, - "ty": 26, - "id": 11 - } - } - } - } - ] - }, - "span": 53 - }, - { - "kind": { - "Assign": [ - { - "local": 3, - "projection": [] - }, - { - "Use": { - "Constant": { - "span": 54, - "user_ty": null, - "const_": { - "kind": { - "Allocated": { - "bytes": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "provenance": { - "ptrs": [] - }, - "align": 8, - "mutability": "Mut" - } - }, - "ty": 26, - "id": 12 - } - } - } - } - ] - }, - "span": 54 - } - ], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 50, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 25, - "id": 9 - } - } - }, - "args": [ - { - "Copy": { - "local": 1, - "projection": [] - } - }, - { - "Copy": { - "local": 2, - "projection": [] - } - }, - { - "Copy": { - "local": 3, - "projection": [] - } - } - ], - "destination": { - "local": 4, - "projection": [] - }, - "target": 1, - "unwind": "Continue" - } - }, - "span": 51 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 5, - "projection": [] - }, - { - "BinaryOp": [ - "Ge", - { - "Copy": { - "local": 4, - "projection": [] - } - }, - { - "Copy": { - "local": 1, - "projection": [] - } - } - ] - } - ] - }, - "span": 55 - } - ], - "terminator": { - "kind": { - "SwitchInt": { - "discr": { - "Move": { - "local": 5, - "projection": [] - } - }, - "targets": { - "branches": [ - [ - 0, - 7 - ] - ], - "otherwise": 2 - } - } - }, - "span": 55 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 6, - "projection": [] - }, - { - "BinaryOp": [ - "Ge", - { - "Copy": { - "local": 4, - "projection": [] - } - }, - { - "Copy": { - "local": 2, - "projection": [] - } - } - ] - } - ] - }, - "span": 56 - } - ], - "terminator": { - "kind": { - "SwitchInt": { - "discr": { - "Move": { - "local": 6, - "projection": [] - } - }, - "targets": { - "branches": [ - [ - 0, - 7 - ] - ], - "otherwise": 3 - } - } - }, - "span": 56 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 7, - "projection": [] - }, - { - "BinaryOp": [ - "Ge", - { - "Copy": { - "local": 4, - "projection": [] - } - }, - { - "Copy": { - "local": 3, - "projection": [] - } - } - ] - } - ] - }, - "span": 57 - } - ], - "terminator": { - "kind": { - "SwitchInt": { - "discr": { - "Move": { - "local": 7, - "projection": [] - } - }, - "targets": { - "branches": [ - [ - 0, - 7 - ] - ], - "otherwise": 4 - } - } - }, - "span": 57 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 8, - "projection": [] - }, - { - "BinaryOp": [ - "Eq", - { - "Copy": { - "local": 4, - "projection": [] - } - }, - { - "Copy": { - "local": 1, - "projection": [] - } - } - ] - } - ] - }, - "span": 58 - } - ], - "terminator": { - "kind": { - "SwitchInt": { - "discr": { - "Move": { - "local": 8, - "projection": [] - } - }, - "targets": { - "branches": [ - [ - 0, - 5 - ] - ], - "otherwise": 8 - } - } - }, - "span": 58 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 9, - "projection": [] - }, - { - "BinaryOp": [ - "Eq", - { - "Copy": { - "local": 4, - "projection": [] - } - }, - { - "Copy": { - "local": 2, - "projection": [] - } - } - ] - } - ] - }, - "span": 59 - } - ], - "terminator": { - "kind": { - "SwitchInt": { - "discr": { - "Move": { - "local": 9, - "projection": [] - } - }, - "targets": { - "branches": [ - [ - 0, - 6 - ] - ], - "otherwise": 8 - } - } - }, - "span": 59 - } - }, - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 10, - "projection": [] - }, - { - "BinaryOp": [ - "Eq", - { - "Copy": { - "local": 4, - "projection": [] - } - }, - { - "Copy": { - "local": 3, - "projection": [] - } - } - ] - } - ] - }, - "span": 60 - } - ], - "terminator": { - "kind": { - "SwitchInt": { - "discr": { - "Move": { - "local": 10, - "projection": [] - } - }, - "targets": { - "branches": [ - [ - 0, - 7 - ] - ], - "otherwise": 8 - } - } - }, - "span": 60 - } - }, - { - "statements": [], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 61, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 27, - "id": 13 - } - } - }, - "args": [ - { - "Constant": { - "span": 32, - "user_ty": null, - "const_": { - "kind": { - "Allocated": { - "bytes": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 110, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "provenance": { - "ptrs": [ - [ - 0, - 0 - ] - ] - }, - "align": 8, - "mutability": "Mut" - } - }, - "ty": 28, - "id": 14 - } - } - } - ], - "destination": { - "local": 11, - "projection": [] - }, - "target": null, - "unwind": "Continue" - } - }, - "span": 61 - } - }, - { - "statements": [], - "terminator": { - "kind": "Return", - "span": 62 - } - } - ], - "locals": [ - { - "ty": 1, - "span": 63, - "mutability": "Mut" - }, - { - "ty": 26, - "span": 64, - "mutability": "Not" - }, - { - "ty": 26, - "span": 65, - "mutability": "Not" - }, - { - "ty": 26, - "span": 66, - "mutability": "Not" - }, - { - "ty": 26, - "span": 67, - "mutability": "Not" - }, - { - "ty": 29, - "span": 55, - "mutability": "Mut" - }, - { - "ty": 29, - "span": 56, - "mutability": "Mut" - }, - { - "ty": 29, - "span": 57, - "mutability": "Mut" - }, - { - "ty": 29, - "span": 58, - "mutability": "Mut" - }, - { - "ty": 29, - "span": 59, - "mutability": "Mut" - }, - { - "ty": 29, - "span": 60, - "mutability": "Mut" - }, - { - "ty": 30, - "span": 61, - "mutability": "Mut" - } - ], - "arg_count": 0, - "var_debug_info": [ - { - "name": "a", - "source_info": { - "span": 64, - "scope": 1 - }, - "composite": null, - "value": { - "Place": { - "local": 1, - "projection": [] - } - }, - "argument_index": null - }, - { - "name": "b", - "source_info": { - "span": 65, - "scope": 2 - }, - "composite": null, - "value": { - "Place": { - "local": 2, - "projection": [] - } - }, - "argument_index": null - }, - { - "name": "c", - "source_info": { - "span": 66, - "scope": 3 - }, - "composite": null, - "value": { - "Place": { - "local": 3, - "projection": [] - } - }, - "argument_index": null - }, - { - "name": "result", - "source_info": { - "span": 67, - "scope": 4 - }, - "composite": null, - "value": { - "Place": { - "local": 4, - "projection": [] - } - }, - "argument_index": null - } - ], - "spread_arg": null, - "span": 68 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h3491d8bffa495004E", - "mono_item_kind": { - "MonoItemFn": { - "name": "std::sys::backtrace::__rust_begin_short_backtrace::", - "id": 2, - "body": { - "blocks": [ - { - "statements": [], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 31, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 19, - "id": 3 - } - } - }, - "args": [ - { - "Move": { - "local": 1, - "projection": [] - } - }, - { - "Constant": { - "span": 32, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 1, - "id": 4 - } - } - } - ], - "destination": { - "local": 0, - "projection": [] - }, - "target": 1, - "unwind": "Continue" - } - }, - "span": 33 - } - }, - { - "statements": [], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 34, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 20, - "id": 5 - } - } - }, - "args": [ - { - "Constant": { - "span": 32, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 1, - "id": 4 - } - } - } - ], - "destination": { - "local": 2, - "projection": [] - }, - "target": 2, - "unwind": "Unreachable" - } - }, - "span": 35 - } - }, - { - "statements": [], - "terminator": { - "kind": "Return", - "span": 36 - } - } - ], - "locals": [ - { - "ty": 1, - "span": 37, - "mutability": "Mut" - }, - { - "ty": 7, - "span": 38, - "mutability": "Not" - }, - { - "ty": 1, - "span": 39, - "mutability": "Not" - } - ], - "arg_count": 1, - "var_debug_info": [ - { - "name": "f", - "source_info": { - "span": 38, - "scope": 0 - }, - "composite": null, - "value": { - "Place": { - "local": 1, - "projection": [] - } - }, - "argument_index": 1 - }, - { - "name": "result", - "source_info": { - "span": 40, - "scope": 1 - }, - "composite": null, - "value": { - "Place": { - "local": 0, - "projection": [] - } - }, - "argument_index": null - }, - { - "name": "dummy", - "source_info": { - "span": 41, - "scope": 2 - }, - "composite": null, - "value": { - "Const": { - "span": 32, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 1, - "id": 4 - } - } - }, - "argument_index": 1 - } - ], - "spread_arg": null, - "span": 42 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17hd99c80c4d0898bdeE", - "mono_item_kind": { - "MonoItemFn": { - "name": "std::ptr::drop_in_place::<{closure@std::rt::lang_start<()>::{closure#0}}>", - "id": 4, - "body": { - "blocks": [ - { - "statements": [], - "terminator": { - "kind": "Return", - "span": 44 - } - } - ], - "locals": [ - { - "ty": 1, - "span": 44, - "mutability": "Mut" - }, - { - "ty": 22, - "span": 44, - "mutability": "Not" - } - ], - "arg_count": 1, - "var_debug_info": [], - "spread_arg": null, - "span": 44 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h739869090782dad0E", - "mono_item_kind": { - "MonoItemFn": { - "name": "<{closure@std::rt::lang_start<()>::{closure#0}} as std::ops::FnOnce<()>>::call_once", - "id": 3, - "body": { - "blocks": [ - { - "statements": [], - "terminator": { - "kind": { - "Call": { - "func": { - "Constant": { - "span": 43, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 21, - "id": 6 - } - } - }, - "args": [ - { - "Move": { - "local": 1, - "projection": [ - "Deref" - ] - } - }, - { - "Move": { - "local": 2, - "projection": [] - } - } - ], - "destination": { - "local": 0, - "projection": [] - }, - "target": 1, - "unwind": "Continue" - } - }, - "span": 43 - } - }, - { - "statements": [], - "terminator": { - "kind": "Return", - "span": 43 - } - } - ], - "locals": [ - { - "ty": 16, - "span": 43, - "mutability": "Mut" - }, - { - "ty": 22, - "span": 43, - "mutability": "Not" - }, - { - "ty": 1, - "span": 43, - "mutability": "Not" - } - ], - "arg_count": 2, - "var_debug_info": [], - "spread_arg": 2, - "span": 43 - } - } - }, - "details": null - }, - { - "symbol_name": "_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17hf5d9ff8f37d5cc66E", - "mono_item_kind": { - "MonoItemFn": { - "name": "<() as std::process::Termination>::report", - "id": 5, - "body": { - "blocks": [ - { - "statements": [ - { - "kind": { - "Assign": [ - { - "local": 0, - "projection": [] - }, - { - "Use": { - "Constant": { - "span": 46, - "user_ty": null, - "const_": { - "kind": { - "Allocated": { - "bytes": [ - 0 - ], - "provenance": { - "ptrs": [] - }, - "align": 1, - "mutability": "Mut" - } - }, - "ty": 17, - "id": 8 - } - } - } - } - ] - }, - "span": 46 - } - ], - "terminator": { - "kind": "Return", - "span": 45 - } - } - ], - "locals": [ - { - "ty": 17, - "span": 47, - "mutability": "Mut" - }, - { - "ty": 1, - "span": 48, - "mutability": "Not" - } - ], - "arg_count": 1, - "var_debug_info": [ - { - "name": "self", - "source_info": { - "span": 48, - "scope": 0 - }, - "composite": null, - "value": { - "Const": { - "span": 32, - "user_ty": null, - "const_": { - "kind": "ZeroSized", - "ty": 1, - "id": 4 - } - } - }, - "argument_index": 1 - } - ], - "spread_arg": null, - "span": 49 - } - } - }, - "details": null - } - ], - "types": [ - [ - 6, - { - "RigidTy": { - "Int": "Isize" - } - } - ], - [ - 2, - { - "RigidTy": { - "Int": "I8" - } - } - ], - [ - 16, - { - "RigidTy": { - "Int": "I32" - } - } - ], - [ - 9, - { - "RigidTy": { - "Uint": "U8" - } - } - ], - [ - 29, - { - "RigidTy": "Bool" - } - ], - [ - 26, - { - "RigidTy": { - "Uint": "Usize" - } - } - ] - ], - "debug": null -} diff --git a/kmir-proofs/maximum-example-proof/maximum-spec.k b/kmir-proofs/maximum-example-proof/maximum-spec.k deleted file mode 100644 index ee506eb8f2e82..0000000000000 --- a/kmir-proofs/maximum-example-proof/maximum-spec.k +++ /dev/null @@ -1,73 +0,0 @@ -module MAXIMUM-SPEC - imports KMIR - - claim [maximum-spec]: - - ( // LHS, start state - #execTerminator ( - terminator (... - kind: terminatorKindCall (... - func: operandConstant ( - constOperand (... - span: span ( 50 ) , - userTy: noUserTypeAnnotationIndex , - const: mirConst (... - kind: constantKindZeroSized , - ty: ty ( 25 ) , // <- this is the reference to `maximum` - id: mirConstId ( 9 ) - ) - ) - ) , - args: - operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) - operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) - operandCopy ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ), - destination: DEST, - target: noBasicBlockIdx, - // forcing the proof to stop because there is no caller to return to - unwind: _ - ), - span: _ - ) - ) - => - // RHS: target - // #execTerminator ( terminator (... kind: terminatorKindReturn , span: ?_ ) ) - #EndProgram - ) - ~> .K - - _ - _ => ty ( 25 ) - - _ => ?_ - _ => ?_ - _ => DEST - _ => noBasicBlockIdx - _ => ?_ - - ListItem ( _ ) - ListItem ( typedValue ( Integer ( A , 64 , false ) , ty ( 26 ) , _ ) ) - ListItem ( typedValue ( Integer ( B , 64 , false ) , ty ( 26 ) , _ ) ) - ListItem ( typedValue ( Integer ( C , 64 , false ) , ty ( 26 ) , _ ) ) - // _ // if we keep this we need a lemma for list size predicate simplification - => - ListItem ( typedValue ( Integer ( ?RESULT, 64, false), ty ( 26 ) , ?_ )) - ?_ - - - _ => ?_ - - ty ( 25 ) |-> monoItemFn (... name: symbol ( "maximum" ) , id: defId ( 7 ) , body: someBody ( body (... blocks: basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 5 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpLt , operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 69 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 5 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 2 ) ) .Branches , otherwise: basicBlockIdx ( 1 ) ) ) , span: span ( 69 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 71 ) ) .Statements , terminator: terminator (... kind: terminatorKindGoto (... target: basicBlockIdx ( 3 ) ) , span: span ( 70 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 4 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 72 ) ) .Statements , terminator: terminator (... kind: terminatorKindGoto (... target: basicBlockIdx ( 3 ) ) , span: span ( 70 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 7 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 74 ) ) statement (... kind: statementKindAssign (... place: place (... local: local ( 6 ) , projection: .ProjectionElems ) , rvalue: rvalueBinaryOp ( binOpLt , operandMove ( place (... local: local ( 7 ) , projection: .ProjectionElems ) ) , operandCopy ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 73 ) ) .Statements , terminator: terminator (... kind: terminatorKindSwitchInt (... discr: operandMove ( place (... local: local ( 6 ) , projection: .ProjectionElems ) ) , targets: switchTargets (... branches: branch ( 0 , basicBlockIdx ( 5 ) ) .Branches , otherwise: basicBlockIdx ( 4 ) ) ) , span: span ( 73 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 76 ) ) .Statements , terminator: terminator (... kind: terminatorKindGoto (... target: basicBlockIdx ( 6 ) ) , span: span ( 75 ) ) ) basicBlock (... statements: statement (... kind: statementKindAssign (... place: place (... local: local ( 0 ) , projection: .ProjectionElems ) , rvalue: rvalueUse ( operandCopy ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) ) ) , span: span ( 77 ) ) .Statements , terminator: terminator (... kind: terminatorKindGoto (... target: basicBlockIdx ( 6 ) ) , span: span ( 75 ) ) ) basicBlock (... statements: .Statements , terminator: terminator (... kind: terminatorKindReturn , span: span ( 78 ) ) ) .BasicBlocks , locals: localDecl (... ty: ty ( 26 ) , span: span ( 79 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 26 ) , span: span ( 80 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 81 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 82 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 26 ) , span: span ( 83 ) , mut: mutabilityNot ) localDecl (... ty: ty ( 29 ) , span: span ( 69 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 29 ) , span: span ( 73 ) , mut: mutabilityMut ) localDecl (... ty: ty ( 26 ) , span: span ( 74 ) , mut: mutabilityMut ) .LocalDecls , argCount: 3 , varDebugInfo: varDebugInfo (... name: symbol ( "a" ) , sourceInfo: sourceInfo (... span: span ( 80 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 1 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 1 ) ) varDebugInfo (... name: symbol ( "b" ) , sourceInfo: sourceInfo (... span: span ( 81 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 2 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 2 ) ) varDebugInfo (... name: symbol ( "c" ) , sourceInfo: sourceInfo (... span: span ( 82 ) , scope: sourceScope ( 0 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 3 ) , projection: .ProjectionElems ) ) , argumentIndex: someInt ( 3 ) ) varDebugInfo (... name: symbol ( "max_ab" ) , sourceInfo: sourceInfo (... span: span ( 83 ) , scope: sourceScope ( 1 ) ) , composite: noVarDebugInfoFragment , value: varDebugInfoContentsPlace ( place (... local: local ( 4 ) , projection: .ProjectionElems ) ) , argumentIndex: noInt ) .VarDebugInfos , spreadArg: noLocal , span: span ( 84 ) ) ) ) - - - requires // invariant of the `Integer` constructor - 0 <=Int A - andBool A Date: Fri, 9 May 2025 16:21:05 +1000 Subject: [PATCH 28/29] rewrite description of proofs in kmir-proofs --- kmir-proofs/README.md | 158 +++++++++++++++++------------------------- 1 file changed, 65 insertions(+), 93 deletions(-) diff --git a/kmir-proofs/README.md b/kmir-proofs/README.md index e2ba12e5522d9..76f899d7ce20f 100644 --- a/kmir-proofs/README.md +++ b/kmir-proofs/README.md @@ -15,121 +15,93 @@ with its dependency [`stable-mir-json`](https://github.com/runtimeverification/stable-mir-json). The installation is described in the repository's `README.md` files. -## Example Proof: Proving a Maximum Finding Function That only Uses `lower-than` +The following description assumes that the `kmir` tool and `stable-mir-json` are +installed and available on the path. -Considering a function that receives three integer arguments, this -function should return the highest value among them. Assertions can be -used to enforce this condition, and an example code that tests this -function can be seen below: +## Program Property Proofs in KMIR -```Rust -fn main() { - - let a:usize = 42; - let b:usize = 43; - let c:usize = 0; +The most user-friendly way to create and run a proof in KMIR is the `prove-rs` +functionality, which allows a user to prove that a given program will +run to completion without an error. - let result = maximum(a, b, c); - - assert!(result >= a && result >= b && result >= c - && (result == a || result == b || result == c ) ); -} +Desired post-conditions of the program, such as properties of the computed result, +can be formulated as simple `assert` statements. Preconditions can be modelled +as `if` statements which skip execution altogether if the precondition is not met. +They can be added to a test function using the following macro: -fn maximum(a: usize, b: usize, c: usize) -> usize { - // max(max(a,b), c) - let max_ab = if a < b {b} else {a}; - if max_ab < c {c} else {max_ab} +```Rust +/// If the precondition is not met, the program is not executed (exits cleanly, ex falso quodlibet) +macro_rules! precondition { + ($pre:expr, $block:expr) => { + if $pre { $block } + }; } ``` +If the precondition is not met, the statements in `$block` won't be executed. If +the `$block` is executed, we can assume that the boolean expression `$pre` holds +true. -Notice in this case that `a`, `b`, and `c` are concrete, fixed -values. To turn the parameters of `maximum` into symbolic variables, -we can obtain the representation of the function call to `maximum` -executed using KMIR and then replace the concrete values of these -variables with symbolic values. Furthermore, the assertion specified -in the code can be manually translated as a requirement that should be -met by the symbolic variables, meaning that any value that they can -assume must respect the conditions contained in the -specification. Following this approach, we can utilize KMIR to give us -formal proof that, for any valid `isize` input, the maximum value -among the three parameters will be returned. - -Work on KMIR is in progress and the way a Rust program is turned into -a K claim will be automated in the near future, but is currently a -manual process described in the longer [description of -`maximum-example-proof`](./maximum-example-proof/README.md). - -To run this proof in your terminal from this folder, execute: - -```sh -cd maximum-proof -kmir prove run $PWD/maximum-spec.k --proof-dir $PWD/proof -``` - -If option `--proof-dir` was used, the finished (or an unfinished) proof can be inspected using the following command: - -```sh -kmir prove view MAXIMUM-SPEC.maximum-spec --proof-dir $PWD/proof -``` +KMIR will stop executing the program as soon as any undefined behaviour arises +from the executed statements. Therefore, running to completion proves the absense +of undefined behaviour, as well as the post-conditions expressed as assertions +(possibly under the assumption of preconditions modelled using the above macro). -## Example Proofs: Safety of Unsafe Arithmetic Operations +## Example: Proving Absense of Undefined Behaviour in `unchecked_*` arithmetic The proofs in subdirectory `unchecked_arithmetic` concern a section of the challenge of securing [Safety of Methods for Numeric Primitive Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html#challenge-11-safety-of-methods-for-numeric-primitive-types) of the Verify Rust Standard Library Effort. -The `*-spec.k` files set up a proof of concept of how KMIR can be used -to prove unsafe methods according to their undefined behaviors. Proofs -were set up using the same method as described for the -`maximum-example-proof`. - -All K claim files follow the same pattern (illustrated using -`unchecked_add` on `i16` as an example): -1) For a given unsafe operation, a calling wrapper function - `unchecked_op` is written and translated to Stable MIR +As an example of a property proof in KMIR, consider the following function which +tests that `unchecked_add` does not trigger undefined behaviour if its safety +conditions are met by the arguments: -```rust - fn unchecked_op(a: i16, b: i16) -> i16 { - let unchecked_res = unsafe { a.unchecked_add(b) }; - unchecked_res - } +```Rust +fn unchecked_add_i32(a: i32, b: i32) { + + precondition!( + ((a as i128 + b as i128) <= i32::MAX as i128) && + ( a as i128 + b as i128 >= i32::MIN as i128), + // ========================================= + unsafe { + let result = a.unchecked_add(b); + assert!(result as i128 == a as i128 + b as i128) + } + ); +} ``` -2) A K configuration for a Rust program that calls this function with - symbolic `i16` arguments `A` and `B` is constructed (currently in a - manual fashion). The `i16` arguments are represented as `Integer(A, - 16, true)`. +According to the [documentation of the unchecked_add function for the i32 primitive +type](https://doc.rust-lang.org/std/primitive.i32.html#method.unchecked_add), -3) According to the [documentation of the unchecked_add function for - the i16 primitive - type](https://doc.rust-lang.org/std/primitive.i16.html#method.unchecked_add), +> "This results in undefined behavior when `self + rhs > i32::MAX` or +> `self + rhs < i32::MIN`, i.e. when `checked_add` would return `None`" -> "This results in undefined behavior when `self + rhs > i16::MAX` or -> `self + rhs < i16::MIN`, i.e. when `checked_add` would return `None`" - This safety condition is translated into a `requires` clause in the - K claim. In addition, the invariants for `A`'s and `B`'s - representation as `i16` can be assumed, giving: +If the sum of the two arguments `a` and `b` does not exceed the bounds of type `i32` +(checked by computing it in range `i128`), the `unchecked_add` function should +not trigger undefined behaviour and produce the correct result, expressed by the +`precondition` macro and the assertion at the end of the unsafe block. -``` - requires // i16 invariants - 0 -Int (1 < Date: Fri, 9 May 2025 16:44:23 +1000 Subject: [PATCH 29/29] Adjust kmir.md description to updated software --- doc/src/tools/kmir.md | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/doc/src/tools/kmir.md b/doc/src/tools/kmir.md index 9245187d41e4b..9840b651fb4b2 100644 --- a/doc/src/tools/kmir.md +++ b/doc/src/tools/kmir.md @@ -47,7 +47,7 @@ terminate normally or reach an exception or construct with undefined behaviour, which terminates the execution abnormally. KMIR is designed to provide sound assurances about undefined behavior (UB) in Rust’s MIR. Rather than statically over‑approximating or flagging UB at every unsafe block, KMIR models the full -MIR semantics, including UB transitions, use a **refusal-to-execute** strategy. +MIR semantics, including UB transitions, using a **refusal-to-execute** strategy. This means that if symbolic execution reaches a MIR instruction and cannot prove that executing it would not result in UB (e.g., an out-of-bounds pointer dereference or an unchecked arithmetic overflow), execution halts in a `UB DETECTED` state. @@ -170,7 +170,7 @@ requires an installation of `K Framework`, best done [using the `kup` tool](https://github.com/runtimeverification/kup/README.md), and includes a git submodule dependency on `stable-mir-json`. -The `stable-mir-json` tool is a custom version of `rustc` which, while compiling +The `stable-mir-json` tool is a custom driver for `rustc` which, while compiling Rust code, writes the code's Stable MIR, represented in a JSON format, to a file. Just like `rustc` itself, `stable-mir-json` extracts MIR of a single crate and must be invoked via `cargo` for multi-crate programs. Besides the JSON @@ -181,21 +181,28 @@ The `kmir` tool provides commands to work with the Stable MIR representation of Rust programs that `stable-mir-json` extracts. * Run Stable MIR code extracted from Rust programs (`kmir run my-program.smir.json`); -* Prove a property about a Rust program, which is given as a K "claim" and - proven using an all-path reachability proof in K (`kmir prove run my-program-spec.k`); -* Inspect the control flow graph of a program's proof constructed by the `kmir - prove run` command (`kmir prove view Module.Proof-Identifier`). - -Examples of proofs using KMIR, and how to derive them from a Rust program -manually, are [provided in the `kmir-proofs` +* Prove that a given Rust program or function will terminate without panics or + undefined behaviour (`kmir prove-rs my-program.rs [--start-symbol my_function]`). + This command invokes `stable-mir-json` internally, and then performs an all-path + reachability proof that the program reaches normal termination under all possible inputs. + Any statements that would panic or cause undefined behaviour will terminate execution + so this proves the successful execution. Pre-and post-conditions can be modelled + using assertions and conditional execution. +* (Advanced use for K experts:) Prove a property about a Rust program, which is given + as a K "claim" and proven using an all-path reachability proof in K + (`kmir prove my-program-spec.k`); +* Print or inspect the control flow graph of a program's proof constructed by the + `kmir prove` or `kmir prove-rs` commands (`kmir show module.proof_identifier` + and `kmir view module.proof_identifier`); + +An example of proofs using KMIR, and how to construct them in Rust code, +are [provided in the `kmir-proofs` directory](https://github.com/model-checking/verify-rust-std/tree/main/kmir-proofs). The `kmir` tool is under active development at the time of writing. -Constructing a K claim from a given Rust program is currently a manual process -but will be automated in a future version. Likewise, at the time of writing, the -`kmir` tool does not automatically extract Stable MIR from a Rust program, the -Stable MIR must be extracted by invoking `stable-mir-json` manually. - +Future development will include using source code annotations instead of explicit +test functions to better integrate with Rust code bases, using a suitable annotation +language to express pre- and post-conditions. ## Background Reading