Skip to content

Commit 91e2596

Browse files
authored
Fix target selection for MIR Linker (rust-lang#1789)
When running MIR Linker with multiple targets (e.g., unit tests), we were failing to compile since the call to `cargo rustc` would have multiple targets. Invoke separate calls for each target for now.
1 parent e890d37 commit 91e2596

File tree

13 files changed

+244
-27
lines changed

13 files changed

+244
-27
lines changed

kani-driver/src/call_cargo.rs

+74-23
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use crate::args::KaniArgs;
55
use crate::session::KaniSession;
66
use anyhow::{Context, Result};
7-
use cargo_metadata::{Metadata, MetadataCommand};
7+
use cargo_metadata::{Metadata, MetadataCommand, Package};
88
use std::ffi::OsString;
99
use std::path::{Path, PathBuf};
1010
use std::process::Command;
@@ -26,7 +26,7 @@ impl KaniSession {
2626
/// Calls `cargo_build` to generate `*.symtab.json` files in `target_dir`
2727
pub fn cargo_build(&self) -> Result<CargoOutputs> {
2828
let build_target = env!("TARGET"); // see build.rs
29-
let metadata = MetadataCommand::new().exec().expect("Failed to get cargo metadata.");
29+
let metadata = MetadataCommand::new().exec().context("Failed to get cargo metadata.")?;
3030
let target_dir = self
3131
.args
3232
.target_dir
@@ -39,10 +39,6 @@ impl KaniSession {
3939
let rustc_args = self.kani_rustc_flags();
4040

4141
let mut cargo_args: Vec<OsString> = vec!["rustc".into()];
42-
if self.args.tests {
43-
cargo_args.push("--tests".into());
44-
}
45-
4642
if self.args.all_features {
4743
cargo_args.push("--all-features".into());
4844
}
@@ -53,6 +49,13 @@ impl KaniSession {
5349
cargo_args.push("--target-dir".into());
5450
cargo_args.push(target_dir.into());
5551

52+
if self.args.tests {
53+
// Use test profile in order to pull dev-dependencies and compile using `--test`.
54+
// Initially the plan was to use `--tests` but that brings in multiple targets.
55+
cargo_args.push("--profile".into());
56+
cargo_args.push("test".into());
57+
}
58+
5659
if self.args.verbose {
5760
cargo_args.push("-v".into());
5861
}
@@ -75,17 +78,20 @@ impl KaniSession {
7578
// Only joing them at the end. All kani flags must come first.
7679
kani_args.extend_from_slice(&rustc_args);
7780

78-
let members = project_members(&self.args, &metadata);
79-
for member in members {
80-
let mut cmd = Command::new("cargo");
81-
cmd.args(&cargo_args)
82-
.args(vec!["-p", &member])
83-
.args(&pkg_args)
84-
.env("RUSTC", &self.kani_compiler)
85-
.env("RUSTFLAGS", "--kani-flags")
86-
.env("KANIFLAGS", &crate::util::join_osstring(&kani_args, " "));
87-
88-
self.run_terminal(cmd)?;
81+
let packages = packages_to_verify(&self.args, &metadata);
82+
for package in packages {
83+
for target in package_targets(&self.args, package) {
84+
let mut cmd = Command::new("cargo");
85+
cmd.args(&cargo_args)
86+
.args(vec!["-p", &package.name])
87+
.args(&target.to_args())
88+
.args(&pkg_args)
89+
.env("RUSTC", &self.kani_compiler)
90+
.env("RUSTFLAGS", "--kani-flags")
91+
.env("KANIFLAGS", &crate::util::join_osstring(&kani_args, " "));
92+
93+
self.run_terminal(cmd)?;
94+
}
8995
}
9096

9197
if self.args.dry_run {
@@ -123,15 +129,60 @@ fn glob(path: &Path) -> Result<Vec<PathBuf>> {
123129
/// - I.e.: Do whatever cargo does when there's no default_members.
124130
/// - This is because `default_members` is not available in cargo metadata.
125131
/// See <https://github.com/rust-lang/cargo/issues/8033>.
126-
fn project_members(args: &KaniArgs, metadata: &Metadata) -> Vec<String> {
132+
fn packages_to_verify<'a, 'b>(args: &'a KaniArgs, metadata: &'b Metadata) -> Vec<&'b Package> {
127133
if !args.package.is_empty() {
128-
args.package.clone()
134+
args.package
135+
.iter()
136+
.map(|pkg_name| {
137+
metadata
138+
.packages
139+
.iter()
140+
.find(|pkg| pkg.name == *pkg_name)
141+
.expect(&format!("Cannot find package '{}'", pkg_name))
142+
})
143+
.collect()
129144
} else {
130145
match (args.workspace, metadata.root_package()) {
131-
(true, _) | (_, None) => {
132-
metadata.workspace_members.iter().map(|id| metadata[id].name.clone()).collect()
133-
}
134-
(_, Some(root_pkg)) => vec![root_pkg.name.clone()],
146+
(true, _) | (_, None) => metadata.workspace_packages(),
147+
(_, Some(root_pkg)) => vec![root_pkg],
135148
}
136149
}
137150
}
151+
152+
/// Possible verification targets.
153+
enum VerificationTarget {
154+
Bin(String),
155+
Lib,
156+
Test(String),
157+
}
158+
159+
impl VerificationTarget {
160+
/// Convert to cargo argument that select the specific target.
161+
fn to_args(&self) -> Vec<String> {
162+
match self {
163+
VerificationTarget::Test(name) => vec![String::from("--test"), name.clone()],
164+
VerificationTarget::Bin(name) => vec![String::from("--bin"), name.clone()],
165+
VerificationTarget::Lib => vec![String::from("--lib")],
166+
}
167+
}
168+
}
169+
170+
/// Extract the targets inside a package.
171+
/// If `--tests` is given, the list of targets will include any integration tests.
172+
fn package_targets(args: &KaniArgs, package: &Package) -> Vec<VerificationTarget> {
173+
package
174+
.targets
175+
.iter()
176+
.filter_map(|target| {
177+
if target.kind.contains(&String::from("bin")) {
178+
Some(VerificationTarget::Bin(target.name.clone()))
179+
} else if target.kind.contains(&String::from("lib")) {
180+
Some(VerificationTarget::Lib)
181+
} else if target.kind.contains(&String::from("test")) && args.tests {
182+
Some(VerificationTarget::Test(target.name.clone()))
183+
} else {
184+
None
185+
}
186+
})
187+
.collect()
188+
}

rfc/src/rfcs/0001-mir-linker.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
- **Feature Name:** MIR Linker (mir_linker)
22
- **RFC Tracking Issue**: <https://github.com/model-checking/kani/issues/1588>
33
- **RFC PR:** <https://github.com/model-checking/kani/pull/1600>
4-
- **Status:** In Progress
5-
- **Version:** 1
4+
- **Status:** Unstable
5+
- **Version:** 2
66

77
-------------------
88

@@ -236,8 +236,8 @@ These results were obtained by looking at the artifacts generated during the sam
236236
Thus, it shouldn't have any side effect.
237237
That relies on all constant initializers being evaluated during compilation.
238238
- ~~What's the best way to handle `cargo kani --tests`?~~
239-
We will use similar mechanism to regular Kani runs:
240-
- `cargo rustc --tests -- --reachability=harnesses`
239+
For now, we are going to use the test profile and iterate over all the targets available in the crate:
240+
- `cargo rustc --profile test -- --reachability=harnesses`
241241

242242

243243
## Future possibilities
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
# Create a workspace with three independent libraries that have integration tests.
5+
# The `in_src_harness` has a proof harness co-located with the code.
6+
# The `integ_harness` has a proof harness co-located with an integration test.
7+
# The `all_harness` has proof harnesses with src and integration tests.
8+
[workspace]
9+
members = [
10+
"in_src_harness",
11+
"integ_harness",
12+
"all_harness",
13+
]
14+
15+
[workspace.metadata.kani.flags]
16+
workspace = true
17+
tests = true
18+
enable-unstable=true
19+
mir-linker=true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
[package]
4+
name = "all_harness"
5+
version = "0.1.0"
6+
edition = "2021"
7+
description = "Package with harnesses that co-located with integration tests and src"
8+
9+
[dependencies]
10+
integ_harness = { path= "../integ_harness" }
11+
in_src_harness = { path= "../in_src_harness" }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
//
4+
//! Dummy harness and library that check left rotation.
5+
use in_src_harness::rotate_left;
6+
use integ_harness::rotate_right;
7+
8+
/// Returns if any rotation would result in wrapping bits.
9+
pub fn will_rotate_wrap(num: u8, rhs: u32) -> bool {
10+
rotate_left(num, rhs).1 || rotate_right(num, rhs).1
11+
}
12+
13+
#[cfg(kani)]
14+
mod proofs {
15+
use super::*;
16+
17+
#[kani::proof]
18+
fn check_propagate() {
19+
let num = kani::any();
20+
let shift = kani::any();
21+
kani::assume(rotate_right(num, shift).1);
22+
assert!(will_rotate_wrap(num, shift));
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
use all_harness::*;
5+
6+
#[cfg(kani)]
7+
mod proofs {
8+
use super::*;
9+
10+
#[kani::proof]
11+
fn with_no_panic() {
12+
will_rotate_wrap(kani::any(), kani::any());
13+
}
14+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Checking harness proofs::check_propagate...
2+
VERIFICATION:- SUCCESSFUL
3+
4+
Checking harness proofs::with_no_panic...
5+
VERIFICATION:- SUCCESSFUL
6+
7+
Checking harness proofs::no_panic...
8+
VERIFICATION:- SUCCESSFUL
9+
10+
Checking harness proofs::will_not_panic...
11+
VERIFICATION:- SUCCESSFUL
12+
13+
14+
Complete - 4 successfully verified harnesses, 0 failures, 4 total.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
[package]
4+
name = "in_src_harness"
5+
version = "0.1.0"
6+
edition = "2021"
7+
description = "Package with a harness that is co-located with the library code"
8+
9+
[dependencies]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
//
4+
//! Dummy harness and library that check left rotation.
5+
6+
/// Return the left rotation of the give number and whether the result had wrapped bits.
7+
pub fn rotate_left(num: u8, rhs: u32) -> (u8, bool) {
8+
let result = num.rotate_left(rhs);
9+
let wrapped = if rhs >= 8 { num != 0 } else { result > (num << rhs) };
10+
(result, wrapped)
11+
}
12+
13+
#[cfg(kani)]
14+
mod proofs {
15+
use super::*;
16+
17+
#[kani::proof]
18+
fn will_not_panic() {
19+
rotate_left(kani::any(), kani::any());
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
use in_src_harness::*;
5+
6+
#[test]
7+
fn test_no_overflow() {
8+
assert_eq!(rotate_left(0x0, 0), (0x0, false));
9+
assert_eq!(rotate_left(0x0, 100), (0x0, false));
10+
assert_eq!(rotate_left(0xF, 0), (0xF, false));
11+
assert_eq!(rotate_left(0x1, 7), (0x80, false));
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
[package]
4+
name = "integ_harness"
5+
version = "0.1.0"
6+
edition = "2021"
7+
description = "Package with a harness that is co-located with integration tests"
8+
9+
[dependencies]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
//
4+
//! Dummy harness and library that check left rotation.
5+
6+
/// Return the right rotation of the give number and whether the result has wrapped bits.
7+
pub fn rotate_right(num: u8, rhs: u32) -> (u8, bool) {
8+
let result = num.rotate_right(rhs);
9+
let wrapped = if rhs >= 8 { num != 0 } else { result < (num >> rhs) };
10+
(result, wrapped)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
use integ_harness::*;
5+
6+
#[test]
7+
fn test_no_overflow() {
8+
assert_eq!(rotate_right(0x0, 0), (0x0, false));
9+
assert_eq!(rotate_right(0x0, 100), (0x0, false));
10+
assert_eq!(rotate_right(0xF, 0), (0xF, false));
11+
assert_eq!(rotate_right(0x80, 7), (0x1, false));
12+
}
13+
14+
#[cfg(kani)]
15+
mod proofs {
16+
use super::*;
17+
18+
#[kani::proof]
19+
fn no_panic() {
20+
rotate_right(kani::any(), kani::any());
21+
}
22+
}

0 commit comments

Comments
 (0)