Skip to content

Commit 753ff30

Browse files
authored
Merge pull request rust-lang#23 from Chris-Hawblitzel/debugger
Rust and VIR-level models
2 parents d02dead + 6870e86 commit 753ff30

File tree

12 files changed

+366
-32
lines changed

12 files changed

+366
-32
lines changed

verify/air/src/model.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ pub struct Model<'a> {
1919
/// Externally facing mapping from snapshot IDs to snapshots that map AIR variables
2020
/// to their concrete values.
2121
/// TODO: Upgrade to a semantics-preserving value type, instead of String.
22-
value_snapshots: HashMap<Ident, HashMap<Ident, String>>,
22+
/// TODO: Expose via a more abstract interface
23+
pub value_snapshots: HashMap<Ident, HashMap<Ident, String>>,
2324
}
2425

2526
impl<'a> Model<'a> {

verify/air/src/var_to_const.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ fn lower_stmt(
3838
versions: &mut HashMap<Ident, u32>,
3939
version_decls: &mut HashSet<Ident>,
4040
snapshots: &mut Snapshots,
41+
all_snapshots: &mut Snapshots,
4142
types: &HashMap<Ident, Typ>,
4243
stmt: &Stmt,
4344
) -> Stmt {
@@ -67,12 +68,21 @@ fn lower_stmt(
6768
}
6869
StmtX::Snapshot(snap) => {
6970
snapshots.insert(snap.clone(), versions.clone());
71+
all_snapshots.insert(snap.clone(), versions.clone());
7072
Arc::new(StmtX::Block(Arc::new(vec![])))
7173
}
7274
StmtX::Block(ss) => {
7375
let mut stmts: Vec<Stmt> = Vec::new();
7476
for s in ss.iter() {
75-
stmts.push(lower_stmt(decls, versions, version_decls, snapshots, types, s));
77+
stmts.push(lower_stmt(
78+
decls,
79+
versions,
80+
version_decls,
81+
snapshots,
82+
all_snapshots,
83+
types,
84+
s,
85+
));
7686
}
7787
Arc::new(StmtX::Block(Arc::new(stmts)))
7888
}
@@ -88,10 +98,12 @@ fn lower_stmt(
8898
&mut versions_i,
8999
version_decls,
90100
&mut snapshots_i,
101+
all_snapshots,
91102
types,
92103
s,
93104
));
94105
all_versions.push(versions_i);
106+
all_snapshots.extend(snapshots_i);
95107
}
96108
for x in all_versions[0].keys() {
97109
// For each variable x, the different branches may have different versions[x],
@@ -125,6 +137,7 @@ pub(crate) fn lower_query(query: &Query) -> (Query, Snapshots, Vec<Decl>) {
125137
let mut versions: HashMap<Ident, u32> = HashMap::new();
126138
let mut version_decls: HashSet<Ident> = HashSet::new();
127139
let mut snapshots: Snapshots = HashMap::new();
140+
let mut all_snapshots: Snapshots = HashMap::new();
128141
let mut types: HashMap<Ident, Typ> = HashMap::new();
129142
let mut local_vars: Vec<Decl> = Vec::new();
130143

@@ -151,9 +164,10 @@ pub(crate) fn lower_query(query: &Query) -> (Query, Snapshots, Vec<Decl>) {
151164
&mut versions,
152165
&mut version_decls,
153166
&mut snapshots,
167+
&mut all_snapshots,
154168
&types,
155169
assertion,
156170
);
157171
let local = Arc::new(decls);
158-
(Arc::new(QueryX { local, assertion }), snapshots, local_vars)
172+
(Arc::new(QueryX { local, assertion }), all_snapshots, local_vars)
159173
}

verify/rust_verify/example/debug.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,56 @@ use pervasive::*;
55

66
fn main() {}
77

8-
//fn test1(i: int, n: nat, u: u8) {
9-
// assert(n >= 5);
10-
//}
8+
/*
9+
fn test_params(i: int, n: nat, u: u8) {
10+
assert(n >= 5);
11+
}
1112
12-
fn test2(i: int, n: nat, u: u8) {
13+
fn test_mutation(i: int, n: nat, u: u8) {
1314
let mut x = 5;
1415
x = x + i;
1516
x = x + n;
1617
x = x + u;
1718
assert(x >= 5);
1819
}
20+
*/
21+
22+
fn test_if_else(b:bool, z:int) {
23+
let mut x : int = 0;
24+
let mut y : int = z;
25+
x = x + y; // 1_mutation
26+
if b {
27+
x = 2*x; // 2_mutation
28+
y = x + 1; // 3_mutation
29+
} else {
30+
x = y + 1; // 4_mutation
31+
y = 7; // 5_mutation
32+
}
33+
assert(x + y > 5); // 6_join
34+
}
35+
36+
/*
37+
fn test_loop() {
38+
let mut i: u64 = 10;
39+
let mut b1: u8 = 20;
40+
let mut b2: u8 = 200;
41+
let mut b3: u8 = 30; // 0_entry
42+
i = i + 1; // 1_mutation
43+
i = i - 1; // 2_mutation
44+
45+
while i < 100 {
46+
invariant([
47+
10 <= i,
48+
i <= 100,
49+
b1 as u64 == i * 2,
50+
]);
51+
// 3_while_begin
52+
assert(b1 == 5);
53+
i = i + 1; // 4_mutation
54+
b1 = b1 + 2; // 5_mutation
55+
b2 = b2 + 1; // 6_mutation
56+
} // 5_while_end
57+
58+
assert(true); // 7_while_end
59+
}
60+
*/

verify/rust_verify/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern crate rustc_typeck;
1515

1616
pub mod config;
1717
pub mod context;
18+
pub mod model;
1819
pub mod rust_to_vir;
1920
pub mod rust_to_vir_adts;
2021
pub mod rust_to_vir_base;

verify/rust_verify/src/model.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use crate::util::from_raw_span;
2+
use air::ast::Ident;
3+
use air::ast::Span as ASpan;
4+
use rustc_span::source_map::SourceMap;
5+
use rustc_span::Span;
6+
use std::collections::HashMap;
7+
use std::fmt;
8+
use vir::def::SnapPos;
9+
use vir::model::Model as VModel;
10+
11+
#[derive(Debug)]
12+
/// Rust-level model of a concrete counterexample
13+
pub struct Model<'a> {
14+
/// Handle to the AIR-level model; only for internal use, e.g., for `eval`
15+
vir_model: VModel<'a>,
16+
/// Internal mapping from a line in the source file to a snapshot ID
17+
line_map: HashMap<usize, Ident>,
18+
}
19+
20+
impl<'a> Model<'a> {
21+
pub fn new(
22+
vir_model: VModel<'a>,
23+
snap_map: &Vec<(ASpan, SnapPos)>,
24+
source_map: &SourceMap,
25+
) -> Model<'a> {
26+
let mut line_map = HashMap::new();
27+
28+
if snap_map.len() > 0 {
29+
let (air_span, snap_pos) = &snap_map[0];
30+
let span: &Span = &from_raw_span(&air_span.raw_span);
31+
let (start, end) =
32+
source_map.is_valid_span(*span).expect("internal error: invalid Span");
33+
34+
let mut min_snap: Ident = match snap_pos {
35+
SnapPos::Start(span_id) => span_id.clone(),
36+
SnapPos::Full(span_id) => span_id.clone(),
37+
SnapPos::End(span_id) => span_id.clone(),
38+
};
39+
let mut min_line = start.line;
40+
let mut max_line = end.line;
41+
42+
for (air_span, snap_pos) in snap_map {
43+
let span: &Span = &from_raw_span(&air_span.raw_span);
44+
let (span_start, span_end) =
45+
source_map.is_valid_span(*span).expect("internal error: invalid Span");
46+
47+
let (start, end, cur_snap) = match snap_pos {
48+
SnapPos::Start(span_id) => (span_start.line, span_start.line + 1, span_id),
49+
SnapPos::Full(span_id) => (span_start.line, span_end.line + 1, span_id),
50+
SnapPos::End(span_id) => (span_end.line, span_end.line + 1, span_id),
51+
};
52+
53+
println!("Apply {} to lines {}..{}", cur_snap, start, end);
54+
for line in start..end {
55+
line_map.insert(line, cur_snap.clone());
56+
}
57+
58+
if span_start.line < min_line {
59+
min_line = span_start.line;
60+
min_snap = cur_snap.clone();
61+
}
62+
max_line = std::cmp::max(max_line, span_end.line);
63+
}
64+
65+
// Fill in any gaps
66+
let mut cur_snap = min_snap;
67+
for line in min_line..max_line {
68+
match line_map.get(&line) {
69+
Some(snap) => cur_snap = snap.clone(),
70+
None => {
71+
let _ = line_map.insert(line, cur_snap.clone());
72+
()
73+
}
74+
}
75+
}
76+
}
77+
78+
// Debugging sanity checks
79+
for (air_span, snap_pos) in snap_map {
80+
let span: &Span = &from_raw_span(&air_span.raw_span);
81+
let (start, end) =
82+
source_map.is_valid_span(*span).expect("internal error: invalid Span");
83+
println!("Span from {} to {} => {:?}", start.line, end.line, snap_pos);
84+
}
85+
Model { vir_model, line_map }
86+
}
87+
88+
pub fn query_variable(&self, line: usize, name: Ident) -> Option<String> {
89+
Some(self.vir_model.query_variable(self.line_map.get(&line)?.clone(), name)?)
90+
}
91+
}
92+
93+
impl<'a> fmt::Display for Model<'a> {
94+
/// Dump the contents of the Rust model for debugging purposes
95+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96+
write!(f, "\nDisplaying model with {} lines\n", self.line_map.len())?;
97+
let mut lines: Vec<_> = self.line_map.iter().collect();
98+
lines.sort_by_key(|t| t.0);
99+
for (line, snap_id) in lines {
100+
write!(f, "Line {}: {}\n", line, snap_id)?;
101+
}
102+
Ok(())
103+
}
104+
}

verify/rust_verify/src/verifier.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::config::Args;
22
use crate::context::Context;
3+
use crate::model::Model;
34
use crate::unsupported;
45
use crate::util::from_raw_span;
56
use air::ast::{Command, CommandX, SpanOption};
@@ -11,6 +12,8 @@ use rustc_span::{CharPos, FileName, MultiSpan, Span};
1112
use std::fs::File;
1213
use std::io::Write;
1314
use vir::ast::{Krate, VirErr, VirErrX};
15+
use vir::def::SnapPos;
16+
use vir::model::Model as VModel;
1417

1518
pub struct Verifier {
1619
pub encountered_vir_error: bool,
@@ -128,6 +131,7 @@ impl Verifier {
128131
fn check_result_validity(
129132
&mut self,
130133
compiler: &Compiler,
134+
snap_map: &Vec<(air::ast::Span, SnapPos)>,
131135
command: &Command,
132136
result: ValidityResult,
133137
) {
@@ -140,7 +144,7 @@ impl Verifier {
140144
ValidityResult::TypeError(err) => {
141145
panic!("internal error: generated ill-typed AIR code: {}", err);
142146
}
143-
ValidityResult::Invalid(m, span1, span2) => {
147+
ValidityResult::Invalid(air_model, span1, span2) => {
144148
report_verify_error(compiler, &span1, &span2);
145149
self.errors.push((
146150
span1
@@ -153,7 +157,10 @@ impl Verifier {
153157
.map(|x| ErrorSpan::new_from_air_span(compiler.session().source_map(), x)),
154158
));
155159
if self.args.debug {
156-
println!("Received model: {}", m);
160+
println!("Received AIR model: {}", air_model);
161+
let vir_model = VModel::new(air_model);
162+
let model = Model::new(vir_model, snap_map, compiler.session().source_map());
163+
println!("Build Rust model: {}", model);
157164
}
158165
}
159166
}
@@ -231,19 +238,24 @@ impl Verifier {
231238
air_context.comment(&("Function-Decl ".to_string() + &function.x.name));
232239
}
233240
for command in commands.iter() {
234-
self.check_result_validity(compiler, &command, air_context.command(&command));
241+
Self::check_internal_result(air_context.command(&command));
235242
}
236243
}
237244

238245
// Create queries to check the validity of proof/exec function bodies
239246
for function in &krate.functions {
240-
let commands = vir::func_to_air::func_def_to_air(&ctx, &function)?;
247+
let (commands, snap_map) = vir::func_to_air::func_def_to_air(&ctx, &function)?;
241248
if commands.len() > 0 {
242249
air_context.blank_line();
243250
air_context.comment(&("Function-Def ".to_string() + &function.x.name));
244251
}
245252
for command in commands.iter() {
246-
self.check_result_validity(compiler, &command, air_context.command(&command));
253+
self.check_result_validity(
254+
compiler,
255+
&snap_map,
256+
&command,
257+
air_context.command(&command),
258+
);
247259
}
248260
}
249261

verify/vir/src/def.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ pub fn suffix_local_id(ident: &Ident) -> Ident {
8585
Arc::new(ident.to_string() + SUFFIX_LOCAL)
8686
}
8787

88+
pub fn rm_suffix_local_id(ident: &Ident) -> Ident {
89+
let mut name = ident.to_string();
90+
if name.ends_with(SUFFIX_LOCAL) {
91+
name = name[..name.len() - SUFFIX_LOCAL.len()].to_string();
92+
}
93+
Arc::new(name)
94+
}
95+
8896
pub fn suffix_typ_param_id(ident: &Ident) -> Ident {
8997
Arc::new(ident.to_string() + SUFFIX_TYPE_PARAM)
9098
}
@@ -138,6 +146,15 @@ pub fn variant_positional_field_ident(variant_ident: &Ident, idx: usize) -> Iden
138146
variant_field_ident(variant_ident, format!("{}", idx).as_str())
139147
}
140148

149+
/// For a given snapshot, does it represent the state
150+
/// at the start of the corresponding span, the end, or the full span?
151+
#[derive(Debug)]
152+
pub enum SnapPos {
153+
Start(Ident),
154+
Full(Ident),
155+
End(Ident),
156+
}
157+
141158
pub struct Spanned<X> {
142159
pub span: Span,
143160
pub x: X,

verify/vir/src/func_to_air.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::ast::{Function, Ident, Idents, Mode, ParamX, Params, VirErr};
22
use crate::context::Ctx;
33
use crate::def::{
44
prefix_ensures, prefix_fuel_id, prefix_fuel_nat, prefix_recursive, prefix_requires,
5-
suffix_global_id, suffix_local_id, suffix_typ_param_id, Spanned, FUEL_BOOL, FUEL_BOOL_DEFAULT,
6-
FUEL_LOCAL, FUEL_TYPE, SUCC, ZERO,
5+
suffix_global_id, suffix_local_id, suffix_typ_param_id, SnapPos, Spanned, FUEL_BOOL,
6+
FUEL_BOOL_DEFAULT, FUEL_LOCAL, FUEL_TYPE, SUCC, ZERO,
77
};
88
use crate::sst_to_air::{exp_to_expr, typ_invariant, typ_to_air};
99
use crate::util::{vec_map, vec_map_result};
@@ -284,7 +284,10 @@ pub fn func_decl_to_air(ctx: &Ctx, function: &Function) -> Result<Commands, VirE
284284
Ok(Arc::new(commands))
285285
}
286286

287-
pub fn func_def_to_air(ctx: &Ctx, function: &Function) -> Result<Commands, VirErr> {
287+
pub fn func_def_to_air(
288+
ctx: &Ctx,
289+
function: &Function,
290+
) -> Result<(Commands, Vec<(Span, SnapPos)>), VirErr> {
288291
match (function.x.mode, function.x.ret.as_ref(), function.x.body.as_ref()) {
289292
(Mode::Exec, _, Some(body)) | (Mode::Proof, _, Some(body)) => {
290293
let dest = function.x.ret.as_ref().map(|(x, _, _)| x.clone());
@@ -294,7 +297,7 @@ pub fn func_def_to_air(ctx: &Ctx, function: &Function) -> Result<Commands, VirEr
294297
vec_map_result(&*function.x.ensure, |e| crate::ast_to_sst::expr_to_exp(ctx, e))?;
295298
let stm = crate::ast_to_sst::expr_to_stm(&ctx, &body, &dest)?;
296299
let stm = crate::recursion::check_termination_stm(ctx, function, &stm)?;
297-
let commands = crate::sst_to_air::body_stm_to_air(
300+
let (commands, snap_map) = crate::sst_to_air::body_stm_to_air(
298301
ctx,
299302
&function.x.typ_params,
300303
&function.x.params,
@@ -304,8 +307,8 @@ pub fn func_def_to_air(ctx: &Ctx, function: &Function) -> Result<Commands, VirEr
304307
&enss,
305308
&stm,
306309
);
307-
Ok(commands)
310+
Ok((commands, snap_map))
308311
}
309-
_ => Ok(Arc::new(vec![])),
312+
_ => Ok((Arc::new(vec![]), vec![])),
310313
}
311314
}

0 commit comments

Comments
 (0)