Skip to content

Commit 3325e49

Browse files
authored
Merge pull request #51 from os-checker/kani-list
Feat: add Kind field to SerFunction; check json with output of kani list
2 parents 949f4a5 + 06b91c7 commit 3325e49

24 files changed

+272
-56
lines changed

src/functions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ mod cache;
1111
pub use cache::{clear_rustc_ctx, set_rustc_ctx};
1212

1313
mod kani;
14+
1415
mod utils;
16+
pub use utils::vec_convertion;
1517

1618
mod serialization;
1719
pub use serialization::SerFunction;

src/functions/serialization.rs

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use serde::Serialize;
44
use stable_mir::{CrateDef, mir::mono::Instance};
55
use std::{cmp::Ordering, hash::Hasher};
66

7-
/// A Rust funtion with its file source, attributes, and raw function content.
7+
/// A kani proof with its file source, attributes, and raw function content.
88
#[derive(Debug, Serialize)]
99
pub struct SerFunction {
1010
hash: String,
@@ -13,6 +13,8 @@ pub struct SerFunction {
1313
/// Attributes are attached the function, but it seems that attributes
1414
/// and function must be separated to query.
1515
attrs: Vec<String>,
16+
/// Proof kind
17+
kind: Kind,
1618
/// Raw function string, including name, signature, and body.
1719
func: SourceCode,
1820
/// Count of callees.
@@ -26,6 +28,7 @@ impl SerFunction {
2628
let inst = fun.instance;
2729
let def_id = format_def_id(&inst);
2830
let attrs: Vec<_> = fun.attrs.iter().map(|a| a.as_str().to_owned()).collect();
31+
let kind = Kind::new(&attrs);
2932
// Though this is from body span, fn name and signature are included.
3033
let func = cache::get_source_code(&inst).unwrap_or_default();
3134
let callees: Vec<_> = fun.callees.iter().map(Callee::new).collect();
@@ -40,7 +43,7 @@ impl SerFunction {
4043
callees.iter().for_each(|callee| callee.func.with_hasher(&mut hasher));
4144
let Hash128(hash) = hasher.finish();
4245

43-
SerFunction { hash, def_id, attrs, func, callees_len, callees }
46+
SerFunction { hash, def_id, attrs, kind, func, callees_len, callees }
4447
}
4548

4649
/// Compare by file and func string.
@@ -78,3 +81,83 @@ impl Callee {
7881
Callee { def_id, func }
7982
}
8083
}
84+
85+
/// kani proof kind
86+
#[derive(Debug, Serialize)]
87+
pub enum Kind {
88+
/// `#[kani::proof]` (actually `kanitool::proof`)
89+
Standard,
90+
/// `#[kani::proof_for_contract]` (actually `kanitool::proof_for_contract`)
91+
Contract,
92+
}
93+
94+
impl Kind {
95+
/// ## Panic
96+
///
97+
/// The given attributes must contain one of the proof kind macro.
98+
fn new(attrs: &[String]) -> Self {
99+
for attr in attrs {
100+
if attr.contains("kanitool::proof_for_contract") {
101+
return Kind::Contract;
102+
} else if attr.contains("kanitool::proof") {
103+
return Kind::Standard;
104+
}
105+
}
106+
panic!("{attrs:?} doesn't contain a proof kind.")
107+
}
108+
}
109+
110+
/// Convertion from lib's SerFunction into the counterpart in main.rs
111+
mod conversion {
112+
use super::*;
113+
use crate::functions::utils::{MacroBacktrace, vec_convertion};
114+
use distributed_verification as lib;
115+
116+
impl From<SerFunction> for lib::SerFunction {
117+
fn from(value: SerFunction) -> Self {
118+
let SerFunction { hash, def_id, attrs, kind, func, callees_len, callees } = value;
119+
let func = func.into();
120+
let kind = kind.into();
121+
let callees = vec_convertion(callees);
122+
Self { hash, def_id, attrs, kind, func, callees_len, callees }
123+
}
124+
}
125+
126+
impl From<Kind> for lib::Kind {
127+
fn from(value: Kind) -> Self {
128+
match value {
129+
Kind::Standard => Self::Standard,
130+
Kind::Contract => Self::Contract,
131+
}
132+
}
133+
}
134+
135+
impl From<Callee> for lib::Callee {
136+
fn from(Callee { def_id, func }: Callee) -> Self {
137+
let func = func.into();
138+
Self { def_id, func }
139+
}
140+
}
141+
142+
impl From<SourceCode> for lib::SourceCode {
143+
fn from(value: SourceCode) -> Self {
144+
let SourceCode {
145+
name,
146+
mangled_name,
147+
kind,
148+
file,
149+
src,
150+
macro_backtrace_len,
151+
macro_backtrace,
152+
} = value;
153+
let macro_backtrace = vec_convertion(macro_backtrace);
154+
Self { name, mangled_name, kind, file, src, macro_backtrace_len, macro_backtrace }
155+
}
156+
}
157+
158+
impl From<MacroBacktrace> for lib::MacroBacktrace {
159+
fn from(MacroBacktrace { callsite, defsite }: MacroBacktrace) -> Self {
160+
Self { callsite, defsite }
161+
}
162+
}
163+
}

src/functions/utils.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ impl SourceCode {
6161

6262
#[derive(Clone, Debug, Serialize, PartialEq, Eq, PartialOrd, Ord, Default)]
6363
pub struct MacroBacktrace {
64-
callsite: String,
65-
defsite: String,
64+
pub callsite: String,
65+
pub defsite: String,
6666
}
6767

6868
fn span_to_source(span: Span, src_map: &SourceMap) -> String {
@@ -123,3 +123,7 @@ pub fn source_code_with(
123123
let kind = format!("{:?}", inst.kind);
124124
SourceCode { name, mangled_name, kind, file, src, macro_backtrace_len, macro_backtrace }
125125
}
126+
127+
pub fn vec_convertion<U, T: From<U>>(vec: Vec<U>) -> Vec<T> {
128+
vec.into_iter().map(T::from).collect()
129+
}

src/kani_list.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use indexmap::{IndexMap, IndexSet};
2+
use serde::{Deserialize, Serialize};
3+
use std::{collections::HashMap, process::Command};
4+
5+
use crate::{Kind, SerFunction};
6+
7+
/// Output of `kani list` command.
8+
#[derive(Debug, Serialize, Deserialize, Clone)]
9+
#[serde(rename_all = "kebab-case")]
10+
pub struct KaniList {
11+
pub kani_version: String,
12+
pub file_version: String,
13+
pub standard_harnesses: IndexMap<String, IndexSet<String>>,
14+
pub contract_harnesses: IndexMap<String, IndexSet<String>>,
15+
pub contracts: IndexSet<ContractedFunction>,
16+
pub totals: Total,
17+
}
18+
19+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20+
pub struct ContractedFunction {
21+
pub function: String,
22+
pub file: String,
23+
pub harnesses: Vec<String>,
24+
}
25+
26+
#[derive(Debug, Serialize, Deserialize, Clone)]
27+
#[serde(rename_all = "kebab-case")]
28+
pub struct Total {
29+
pub standard_harnesses: usize,
30+
pub contract_harnesses: usize,
31+
pub functions_under_contract: usize,
32+
}
33+
34+
/// Get kani list and check if it complies with Vec<SerFunction>.
35+
pub fn check(file: &str, v_ser_fun: &[SerFunction]) {
36+
let list = get_kani_list(file);
37+
check_proofs(&list, v_ser_fun);
38+
}
39+
40+
pub fn get_kani_list(file: &str) -> KaniList {
41+
// kani list -Zlist -Zfunction-contracts --format=json file.rs
42+
let args = ["list", "-Zlist", "-Zfunction-contracts", "--format=json", file];
43+
let output = Command::new("kani").args(args).output().unwrap();
44+
assert!(
45+
output.status.success(),
46+
"Failed to run `kani list -Zlist -Zfunction-contracts --format=json {file}`:\n{}",
47+
std::str::from_utf8(&output.stderr).unwrap()
48+
);
49+
50+
// read kani-list.json
51+
let file_json = std::fs::File::open("kani-list.json").unwrap();
52+
serde_json::from_reader(file_json).unwrap()
53+
}
54+
55+
pub fn check_proofs(list: &KaniList, v_ser_fun: &[SerFunction]) {
56+
// sanity check
57+
let totals = &list.totals;
58+
assert_eq!(v_ser_fun.len(), totals.standard_harnesses + totals.contract_harnesses);
59+
assert_eq!(
60+
list.standard_harnesses.values().map(|s| s.len()).sum::<usize>(),
61+
totals.standard_harnesses
62+
);
63+
assert_eq!(
64+
list.contract_harnesses.values().map(|s| s.len()).sum::<usize>(),
65+
totals.contract_harnesses
66+
);
67+
68+
let map: HashMap<_, _> = v_ser_fun
69+
.iter()
70+
.enumerate()
71+
.map(|(idx, f)| ((&*f.func.file, &*f.func.name), (idx, f.kind)))
72+
.collect();
73+
74+
// check all standard proofs are in distributed-verification json
75+
for (path, proofs) in &list.standard_harnesses {
76+
for proof in proofs {
77+
let key = (path.as_str(), proof.as_str());
78+
let val = map.get(&key).unwrap();
79+
dbg!(val);
80+
}
81+
}
82+
83+
// check all contract proofs are in distributed-verification json
84+
for (path, proofs) in &list.contract_harnesses {
85+
for proof in proofs {
86+
let key = (path.as_str(), proof.as_str());
87+
let idx = map.get(&key).unwrap();
88+
dbg!(idx);
89+
}
90+
}
91+
92+
// double check
93+
for (&(path, proof), &(_, kind)) in &map {
94+
let harnesses = match kind {
95+
Kind::Standard => &list.standard_harnesses[path],
96+
Kind::Contract => &list.contract_harnesses[path],
97+
};
98+
_ = harnesses.get(proof).unwrap();
99+
}
100+
}

src/lib.rs

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use indexmap::{IndexMap, IndexSet};
21
use serde::{Deserialize, Serialize};
32

4-
/// A Rust funtion with its file source, attributes, and raw function content.
3+
pub mod kani_list;
4+
5+
/// A kani proof with its file source, attributes, and raw function content.
56
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
67
pub struct SerFunction {
78
pub hash: String,
@@ -10,6 +11,8 @@ pub struct SerFunction {
1011
/// Attributes are attached the function, but it seems that attributes
1112
/// and function must be separated to query.
1213
pub attrs: Vec<String>,
14+
/// Proof kind
15+
pub kind: Kind,
1316
/// Raw function string, including name, signature, and body.
1417
pub func: SourceCode,
1518
/// Count of callees.
@@ -18,6 +21,16 @@ pub struct SerFunction {
1821
pub callees: Vec<Callee>,
1922
}
2023

24+
/// kani proof kind
25+
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy)]
26+
pub enum Kind {
27+
/// `#[kani::proof]` (actually `kanitool::proof`)
28+
#[default]
29+
Standard,
30+
/// `#[kani::proof_for_contract]` (actually `kanitool::proof_for_contract`)
31+
Contract,
32+
}
33+
2134
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
2235
pub struct Callee {
2336
pub def_id: String,
@@ -61,8 +74,8 @@ pub struct SourceCode {
6174

6275
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
6376
pub struct MacroBacktrace {
64-
callsite: String,
65-
defsite: String,
77+
pub callsite: String,
78+
pub defsite: String,
6679
}
6780

6881
/// A local path to kani's artifacts.
@@ -84,30 +97,3 @@ pub fn kani_path() -> String {
8497
assert!(std::fs::exists(&path).unwrap());
8598
path
8699
}
87-
88-
/// Output of `kani list` command.
89-
#[derive(Debug, Serialize, Deserialize, Clone)]
90-
#[serde(rename_all = "kebab-case")]
91-
pub struct KaniList {
92-
pub kani_version: String,
93-
pub file_version: String,
94-
pub standard_harnesses: IndexMap<String, IndexSet<String>>,
95-
pub contract_harnesses: IndexMap<String, IndexSet<String>>,
96-
pub contracts: IndexSet<ContractedFunction>,
97-
pub totals: Total,
98-
}
99-
100-
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
101-
pub struct ContractedFunction {
102-
pub function: String,
103-
pub file: String,
104-
pub harnesses: Vec<String>,
105-
}
106-
107-
#[derive(Debug, Serialize, Deserialize, Clone)]
108-
#[serde(rename_all = "kebab-case")]
109-
pub struct Total {
110-
pub standard_harnesses: usize,
111-
pub contract_harnesses: usize,
112-
pub functions_under_contract: usize,
113-
}

src/main.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern crate stable_mir;
1414

1515
use distributed_verification::kani_path;
1616
use functions::{clear_rustc_ctx, set_rustc_ctx};
17+
use rustc_middle::ty::TyCtxt;
1718
// FIXME: this is a bug for rustc_smir, because rustc_interface is used by
1819
// run_with_tcx! without being imported inside.
1920
use rustc_smir::rustc_internal;
@@ -78,10 +79,18 @@ fn main() {
7879
};
7980

8081
res().unwrap();
82+
check_kani_list(output, tcx);
8183

8284
// Stop emitting artifact for the source code being compiled.
8385
ControlFlow::<(), ()>::Break(())
8486
});
8587
// rustc_smir uses `Err(CompilerError::Interrupted)` to represent ControlFlow::Break.
8688
assert!(res == Err(stable_mir::CompilerError::Interrupted(())), "Unexpected {res:?}");
8789
}
90+
91+
fn check_kani_list(output: Vec<functions::SerFunction>, tcx: TyCtxt) {
92+
let output: Vec<distributed_verification::SerFunction> = functions::vec_convertion(output);
93+
let crate_file = tcx.sess.local_crate_source_file().expect("No real crate root file.");
94+
let crate_file = crate_file.local_path().unwrap().to_str().unwrap();
95+
distributed_verification::kani_list::check(crate_file, &output);
96+
}

tests/kani_list.rs

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use distributed_verification::KaniList;
2-
use std::process::Command;
1+
use distributed_verification::kani_list::{check_proofs, get_kani_list};
32

43
mod utils;
54
use utils::*;
@@ -9,27 +8,20 @@ fn validate_kani_list_json() -> Result<()> {
98
let proofs = get_proofs("tests/proofs")?;
109

1110
for path in &proofs {
12-
let kani_list = get_kani_list(path.to_str().unwrap());
1311
let file_stem = file_stem(path);
1412
let list_path = format!("kani_list/{file_stem}.txt");
1513
dbg!(&list_path);
14+
15+
let path = path.to_str().unwrap();
16+
// run `kani list`
17+
let kani_list = get_kani_list(path);
1618
expect_file![list_path].assert_debug_eq(&kani_list);
19+
20+
// run `distributed_verification`
21+
let text = cmd(&[path]);
22+
let v_ser_function: Vec<SerFunction> = serde_json::from_str(&text).unwrap();
23+
check_proofs(&kani_list, &v_ser_function);
1724
}
1825

1926
Ok(())
2027
}
21-
22-
fn get_kani_list(file: &str) -> KaniList {
23-
// kani list -Zlist -Zfunction-contracts --format=json file.rs
24-
let args = ["list", "-Zlist", "-Zfunction-contracts", "--format=json", file];
25-
let output = Command::new("kani").args(args).output().unwrap();
26-
assert!(
27-
output.status.success(),
28-
"Failed to run `kani list -Zlist -Zfunction-contracts --format=json {file}`:\n{}",
29-
std::str::from_utf8(&output.stderr).unwrap()
30-
);
31-
32-
// read kani-list.json
33-
let file_json = std::fs::File::open("kani-list.json").unwrap();
34-
serde_json::from_reader(file_json).unwrap()
35-
}

0 commit comments

Comments
 (0)