Skip to content

Commit 6eef3e0

Browse files
authored
Introduce prototype cargo-kani assess feature to help find places to start writing proofs (rust-lang#1756)
1 parent a28c21b commit 6eef3e0

File tree

14 files changed

+546
-14
lines changed

14 files changed

+546
-14
lines changed

Cargo.lock

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,18 @@ dependencies = [
190190
"os_str_bytes",
191191
]
192192

193+
[[package]]
194+
name = "comfy-table"
195+
version = "6.0.0"
196+
source = "registry+https://github.com/rust-lang/crates.io-index"
197+
checksum = "121d8a5b0346092c18a4b2fd6f620d7a06f0eb7ac0a45860939a0884bc579c56"
198+
dependencies = [
199+
"crossterm",
200+
"strum",
201+
"strum_macros",
202+
"unicode-width",
203+
]
204+
193205
[[package]]
194206
name = "compiletest"
195207
version = "0.0.0"
@@ -265,26 +277,49 @@ dependencies = [
265277

266278
[[package]]
267279
name = "crossbeam-epoch"
268-
version = "0.9.10"
280+
version = "0.9.11"
269281
source = "registry+https://github.com/rust-lang/crates.io-index"
270-
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
282+
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
271283
dependencies = [
272284
"autocfg",
273285
"cfg-if",
274286
"crossbeam-utils",
275287
"memoffset",
276-
"once_cell",
277288
"scopeguard",
278289
]
279290

280291
[[package]]
281292
name = "crossbeam-utils"
282-
version = "0.8.11"
293+
version = "0.8.12"
283294
source = "registry+https://github.com/rust-lang/crates.io-index"
284-
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
295+
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
285296
dependencies = [
286297
"cfg-if",
287-
"once_cell",
298+
]
299+
300+
[[package]]
301+
name = "crossterm"
302+
version = "0.23.2"
303+
source = "registry+https://github.com/rust-lang/crates.io-index"
304+
checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17"
305+
dependencies = [
306+
"bitflags",
307+
"crossterm_winapi",
308+
"libc",
309+
"mio",
310+
"parking_lot",
311+
"signal-hook",
312+
"signal-hook-mio",
313+
"winapi",
314+
]
315+
316+
[[package]]
317+
name = "crossterm_winapi"
318+
version = "0.9.0"
319+
source = "registry+https://github.com/rust-lang/crates.io-index"
320+
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
321+
dependencies = [
322+
"winapi",
288323
]
289324

290325
[[package]]
@@ -432,6 +467,7 @@ dependencies = [
432467
"anyhow",
433468
"cargo_metadata",
434469
"clap 2.34.0",
470+
"comfy-table",
435471
"console",
436472
"glob",
437473
"kani_metadata",
@@ -545,6 +581,18 @@ dependencies = [
545581
"autocfg",
546582
]
547583

584+
[[package]]
585+
name = "mio"
586+
version = "0.8.4"
587+
source = "registry+https://github.com/rust-lang/crates.io-index"
588+
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
589+
dependencies = [
590+
"libc",
591+
"log",
592+
"wasi",
593+
"windows-sys",
594+
]
595+
548596
[[package]]
549597
name = "num"
550598
version = "0.4.0"
@@ -916,6 +964,36 @@ version = "1.1.0"
916964
source = "registry+https://github.com/rust-lang/crates.io-index"
917965
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
918966

967+
[[package]]
968+
name = "signal-hook"
969+
version = "0.3.14"
970+
source = "registry+https://github.com/rust-lang/crates.io-index"
971+
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
972+
dependencies = [
973+
"libc",
974+
"signal-hook-registry",
975+
]
976+
977+
[[package]]
978+
name = "signal-hook-mio"
979+
version = "0.2.3"
980+
source = "registry+https://github.com/rust-lang/crates.io-index"
981+
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
982+
dependencies = [
983+
"libc",
984+
"mio",
985+
"signal-hook",
986+
]
987+
988+
[[package]]
989+
name = "signal-hook-registry"
990+
version = "1.4.0"
991+
source = "registry+https://github.com/rust-lang/crates.io-index"
992+
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
993+
dependencies = [
994+
"libc",
995+
]
996+
919997
[[package]]
920998
name = "smallvec"
921999
version = "1.9.0"

kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ impl<'tcx> GotocCtx<'tcx> {
123123
self.symbol_table.update_fn_declaration_with_definition(&name, body);
124124

125125
self.handle_kanitool_attributes();
126+
self.record_test_harness_metadata();
126127
}
127128
self.reset_current_fn();
128129
}
@@ -316,12 +317,53 @@ impl<'tcx> GotocCtx<'tcx> {
316317
}
317318
}
318319

320+
/// We record test harness information in kani-metadata, just like we record
321+
/// proof harness information. This is used to support e.g. cargo-kani assess.
322+
///
323+
/// Note that we do not actually spot the function that was annotated by `#[test]`
324+
/// but instead the closure that gets put into the "test description" that macro
325+
/// expands into. (See comment below) This ends up being preferrable, actually,
326+
/// as it add asserts for tests that return `Result` types.
327+
fn record_test_harness_metadata(&mut self) {
328+
let def_id = self.current_fn().instance().def_id();
329+
if def_id.is_local() {
330+
let local_def_id = def_id.expect_local();
331+
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
332+
333+
// We want to detect the case where we're codegen'ing the closure inside what test "descriptions"
334+
// are macro-expanded to:
335+
//
336+
// #[rustc_test_marker]
337+
// pub const check_2: test::TestDescAndFn = test::TestDescAndFn {
338+
// desc: ...,
339+
// testfn: test::StaticTestFn(|| test::assert_test_result(check_2())),
340+
// };
341+
342+
// The parent item of the closure appears to reliably be the `const` declaration item.
343+
let parent_id = self.tcx.hir().get_parent_item(hir_id);
344+
let attrs = self.tcx.get_attrs_unchecked(parent_id.to_def_id());
345+
346+
if self.tcx.sess.contains_name(attrs, rustc_span::symbol::sym::rustc_test_marker) {
347+
let loc = self.codegen_span(&self.current_fn().mir().span);
348+
self.test_harnesses.push(HarnessMetadata {
349+
pretty_name: self.current_fn().readable_name().to_owned(),
350+
mangled_name: self.current_fn().name(),
351+
original_file: loc.filename().unwrap(),
352+
original_start_line: loc.start_line().unwrap() as usize,
353+
original_end_line: loc.end_line().unwrap() as usize,
354+
unwind_value: None,
355+
})
356+
}
357+
}
358+
}
359+
319360
/// This updates the goto context with any information that should be accumulated from a function's
320361
/// attributes.
321362
///
322363
/// Handle all attributes i.e. `#[kani::x]` (which kani_macros translates to `#[kanitool::x]` for us to handle here)
323364
fn handle_kanitool_attributes(&mut self) {
324-
let all_attributes = self.tcx.get_attrs_unchecked(self.current_fn().instance().def_id());
365+
let def_id = self.current_fn().instance().def_id();
366+
let all_attributes = self.tcx.get_attrs_unchecked(def_id);
325367
let (proof_attributes, other_attributes) = partition_kanitool_attributes(all_attributes);
326368
if !proof_attributes.is_empty() {
327369
self.create_proof_harness(other_attributes);

kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ impl CodegenBackend for GotocCodegenBackend {
129129
// Print compilation report.
130130
print_report(&gcx, tcx);
131131

132+
let unsupported_features = gcx.unsupported_metadata();
133+
132134
// perform post-processing symbol table passes
133135
let passes = self.queries.get_symbol_table_passes();
134136
let symtab = symtab_transformer::do_passes(gcx.symbol_table, &passes);
@@ -144,7 +146,11 @@ impl CodegenBackend for GotocCodegenBackend {
144146
None
145147
};
146148

147-
let metadata = KaniMetadata { proof_harnesses: gcx.proof_harnesses };
149+
let metadata = KaniMetadata {
150+
proof_harnesses: gcx.proof_harnesses,
151+
unsupported_features,
152+
test_harnesses: gcx.test_harnesses,
153+
};
148154

149155
// No output should be generated if user selected no_codegen.
150156
if !tcx.sess.opts.unstable_opts.no_codegen && tcx.sess.opts.output_types.should_codegen() {

kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use cbmc::goto_program::{DatatypeComponent, Expr, Location, Stmt, Symbol, Symbol
2121
use cbmc::utils::aggr_tag;
2222
use cbmc::InternedString;
2323
use cbmc::{MachineModel, RoundingMode};
24-
use kani_metadata::HarnessMetadata;
24+
use kani_metadata::{HarnessMetadata, UnsupportedFeature};
2525
use kani_queries::{QueryDb, UserInput};
2626
use rustc_data_structures::fx::FxHashMap;
2727
use rustc_data_structures::owning_ref::OwningRef;
@@ -59,6 +59,7 @@ pub struct GotocCtx<'tcx> {
5959
pub current_fn: Option<CurrentFnCtx<'tcx>>,
6060
pub type_map: FxHashMap<InternedString, Ty<'tcx>>,
6161
pub proof_harnesses: Vec<HarnessMetadata>,
62+
pub test_harnesses: Vec<HarnessMetadata>,
6263
/// a global counter for generating unique IDs for checks
6364
pub global_checks_count: u64,
6465
/// A map of unsupported constructs that were found while codegen
@@ -84,6 +85,7 @@ impl<'tcx> GotocCtx<'tcx> {
8485
current_fn: None,
8586
type_map: FxHashMap::default(),
8687
proof_harnesses: vec![],
88+
test_harnesses: vec![],
8789
global_checks_count: 0,
8890
unsupported_constructs: FxHashMap::default(),
8991
}
@@ -99,6 +101,32 @@ impl<'tcx> GotocCtx<'tcx> {
99101
pub fn current_fn_mut(&mut self) -> &mut CurrentFnCtx<'tcx> {
100102
self.current_fn.as_mut().unwrap()
101103
}
104+
105+
/// Maps the goto-context "unsupported features" data into the
106+
/// KaniMetadata "unsupported features" format.
107+
///
108+
/// These are different because the KaniMetadata is a flat serializable list,
109+
/// while we need a more richly structured HashMap in the goto context.
110+
pub(crate) fn unsupported_metadata(&self) -> Vec<UnsupportedFeature> {
111+
self.unsupported_constructs
112+
.iter()
113+
.map(|(construct, location)| UnsupportedFeature {
114+
feature: construct.to_string(),
115+
locations: location
116+
.iter()
117+
.map(|l| {
118+
// We likely (and should) have no instances of
119+
// calling `codegen_unimplemented` without file/line.
120+
// So while we map out of `Option` here, we expect them to always be `Some`
121+
(
122+
l.filename().unwrap_or_default(),
123+
l.start_line().map(|x| x.to_string()).unwrap_or_default(),
124+
)
125+
})
126+
.collect(),
127+
})
128+
.collect()
129+
}
102130
}
103131

104132
/// Generate variables

kani-driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ regex = "1.6"
3131
rustc-demangle = "0.1.21"
3232
pathdiff = "0.2.1"
3333
rayon = "1.5.3"
34+
comfy-table = "6.0.0"
3435

3536
# A good set of suggested dependencies can be found in rustup:
3637
# https://github.com/rust-lang/rustup/blob/master/Cargo.toml

kani-driver/src/args.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,29 @@ pub struct StandaloneArgs {
3535
setting = structopt::clap::AppSettings::AllArgsOverrideSelf
3636
)]
3737
pub struct CargoKaniArgs {
38+
#[structopt(subcommand)]
39+
pub command: Option<CargoKaniSubcommand>,
40+
3841
#[structopt(flatten)]
3942
pub common_opts: KaniArgs,
4043
}
4144

45+
// cargo-kani takes optional subcommands to request specialized behavior
46+
#[derive(Debug, StructOpt)]
47+
pub enum CargoKaniSubcommand {
48+
#[structopt(setting(structopt::clap::AppSettings::Hidden))]
49+
Assess,
50+
}
51+
4252
// Common arguments for invoking Kani. This gets put into KaniContext, whereas
4353
// anything above is "local" to "main"'s control flow.
4454
#[derive(Debug, StructOpt)]
4555
pub struct KaniArgs {
56+
/// Temporary option to trigger assess mode for out test suite
57+
/// where we are able to add options but not subcommands
58+
#[structopt(long, hidden = true, requires("enable-unstable"))]
59+
pub assess: bool,
60+
4661
/// Generate visualizer report to <target-dir>/report/html/index.html
4762
#[structopt(long)]
4863
pub visualize: bool,

0 commit comments

Comments
 (0)