From ddb4779bc417a09c43162a628f2af8759de5d83d Mon Sep 17 00:00:00 2001 From: hegglinc Date: Tue, 8 Nov 2022 21:51:53 +0100 Subject: [PATCH 01/74] collecting information --- prusti-utils/src/config.rs | 11 +++ prusti/Cargo.toml | 2 + prusti/src/callbacks.rs | 1 + prusti/src/driver.rs | 1 + prusti/src/ide_helper/body_finder.rs | 90 ++++++++++++++++++++++ prusti/src/ide_helper/ide_info.rs | 93 +++++++++++++++++++++++ prusti/src/ide_helper/mod.rs | 3 + prusti/src/ide_helper/span_mapper.rs | 108 +++++++++++++++++++++++++++ prusti/src/verifier.rs | 10 ++- 9 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 prusti/src/ide_helper/body_finder.rs create mode 100644 prusti/src/ide_helper/ide_info.rs create mode 100644 prusti/src/ide_helper/mod.rs create mode 100644 prusti/src/ide_helper/span_mapper.rs diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index 666878fc1ef..4626dcf1714 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -163,6 +163,10 @@ lazy_static::lazy_static! { settings.set_default::>("verify_only_basic_block_path", vec![]).unwrap(); settings.set_default::>("delete_basic_blocks", vec![]).unwrap(); + // Flags specifically for Prusti-Assistant: + settings.set_default("show_ide_info", false).unwrap(); + + // Get the list of all allowed flags. let mut allowed_keys = get_keys(&settings); allowed_keys.insert("server_max_stored_verifiers".to_string()); @@ -965,3 +969,10 @@ pub fn cargo_command() -> String { pub fn enable_type_invariants() -> bool { read_setting("enable_type_invariants") } + +/// When enabled, prusti should return various Data structures that are +/// used by prusti-assistant, such as a list of method calls, +/// a list of all procedures to be verified, etc. +pub fn show_ide_info() -> bool { + read_setting("show_ide_info") +} diff --git a/prusti/Cargo.toml b/prusti/Cargo.toml index a59417f3c1c..4cf342a82ca 100644 --- a/prusti/Cargo.toml +++ b/prusti/Cargo.toml @@ -18,6 +18,8 @@ prusti-common = { path = "../prusti-common" } prusti-rustc-interface = { path = "../prusti-rustc-interface" } log = { version = "0.4", features = ["release_max_level_info"] } lazy_static = "1.4.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" [build-dependencies] chrono = { version = "0.4.22", default-features = false, features = ["clock"] } diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index ec2391c910e..74c2e2d7d63 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -100,6 +100,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { } } CrossCrateSpecs::import_export_cross_crate(&mut env, &mut def_spec); + if !config::no_verify() { verify(env, def_spec); } diff --git a/prusti/src/driver.rs b/prusti/src/driver.rs index feaa07ba6c8..f5203515b5f 100644 --- a/prusti/src/driver.rs +++ b/prusti/src/driver.rs @@ -13,6 +13,7 @@ mod arg_value; mod callbacks; mod verifier; +mod ide_helper; use arg_value::arg_value; use callbacks::PrustiCompilerCalls; diff --git a/prusti/src/ide_helper/body_finder.rs b/prusti/src/ide_helper/body_finder.rs new file mode 100644 index 00000000000..10999a01dd2 --- /dev/null +++ b/prusti/src/ide_helper/body_finder.rs @@ -0,0 +1,90 @@ +use prusti_rustc_interface::{ + span::{ + Span, + BytePos, + }, + middle::{ + ty::TyCtxt, + hir::nested_filter::OnlyBodies, + }, + hir::{intravisit::Visitor, BodyId}, +}; + +// use rustc_middle::{hir::nested_filter::OnlyBodies, ty::TyCtxt}; + +// Most of this code is copied from flowistry! +// + + + +struct BodyFinder<'tcx> { + tcx: TyCtxt<'tcx>, + bodies: Vec<(Span, BodyId)>, +} + +impl<'tcx> Visitor<'tcx> for BodyFinder<'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_nested_body(&mut self, id: BodyId) { + let hir = self.nested_visit_map(); + + // const/static items are considered to have bodies, so we want to exclude + // them from our search for functions + if !hir + .body_owner_kind(hir.body_owner_def_id(id)) + .is_fn_or_closure() + { + return; + } + + let body = hir.body(id); + self.visit_body(body); + + let hir = self.tcx.hir(); + let span = hir.span_with_body(hir.body_owner(id)); + + if !span.from_expansion() { + self.bodies.push((span, id)); + } + } +} + +pub fn find_bodies(tcx: TyCtxt) -> Vec<(Span, BodyId)> { + let mut finder = BodyFinder { + tcx, + bodies: Vec::new(), + }; + tcx.hir().visit_all_item_likes_in_crate(&mut finder); + finder.bodies +} + +pub fn find_enclosing_bodies(tcx: TyCtxt, sp: Span) -> impl Iterator { + let mut bodies = find_bodies(tcx); + bodies.retain(|(other, _)| other.contains(sp)); + bodies.sort_by_key(|(span, _)| span_length(span)); + bodies.into_iter().map(|(_, id)| id) +} + +pub fn charoffset_to_span(byte_start: u32, byte_end: u32, tcx: TyCtxt) -> Span { + let source_map = tcx.sess.source_map(); + let offset = 0; // TODO: sometimes there might be a problem with + // this approach! See to_span in range.rs, flowistry. + + // Span::with_root_ctxt( + // offset + BytePos(byte_start), + // offset + BytePos(byte_end), + // ) + + Span::with_root_ctxt( + BytePos(byte_start), + BytePos(byte_end), + ) +} + +pub fn span_length(span: &Span) -> u32 { + span.hi().0 - span.lo().0 +} diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/ide_info.rs new file mode 100644 index 00000000000..78fd8208ac8 --- /dev/null +++ b/prusti/src/ide_helper/ide_info.rs @@ -0,0 +1,93 @@ +use std::{fmt, ptr::null}; + +use prusti_interface::data::VerificationTask; +use prusti_interface::environment::Environment; + +use prusti_rustc_interface::{ + span::{ + Span, Loc, + source_map::SourceMap, + FileLines, FileLinesResult, + }, +}; + +use serde::Serialize; + +// create some struct storing all the information the IDE will ever need. +// needs to be transformable into json! + +#[derive(Serialize)] +pub struct IdeInfo { + procedure_defs: Vec<(String, VscSpan)>, + // additionally this will contain: + // function_calls: + // ... we'll see +} + + + + +impl IdeInfo { + + // collect information about the program that will be passed to IDE: + pub fn collect_procedures(env: &Environment<'_>, verification_task: &VerificationTask) -> Self { + let sourcemap: &SourceMap = env.tcx().sess.source_map(); + let mut procs = Vec::new(); + for procedure in &verification_task.procedures { + let defpath = env.name.get_item_def_path(*procedure); + let span = env.query.get_def_span(procedure); + println!("found procedure: {}, span: {:?}", defpath, span); + let vscspan = VscSpan::from_span(span, sourcemap).unwrap(); + procs.push((defpath, vscspan)); + } + Self { + procedure_defs: procs + } + + } +} + + + +/// a representation of spans that is more usable with VSCode. +#[derive(Serialize)] +pub struct VscSpan { + from: (usize, usize), + to: (usize, usize), + filename: String, +} + +impl VscSpan { + pub fn from_span(sp: Span, sourcemap: &SourceMap) -> Option { + let filename = sourcemap.span_to_filename(sp); + let diag_filename = sourcemap.filename_for_diagnostics(&filename); + let fname = format!("{}", diag_filename); + + if let Ok((l1, l2)) = sourcemap.is_valid_span(sp) { + let loc1: Loc = l1; + let loc2: Loc = l2; + + let line1 = loc1.line; + let line2 = loc2.line; + + let char1 = loc1.col.0; + let char2 = loc2.col.0; + + Some(Self { + from: (line1, char1), + to: (line2, char2), + filename: fname, + }) + } else { + // println!("{:?}", lines); + // match lines { + // Ok(fl) => { + // + // }, + // Err(_) => + // } + // let parent = format!("{:?}", sp.parent().unwrap()); + None + } + } +} diff --git a/prusti/src/ide_helper/mod.rs b/prusti/src/ide_helper/mod.rs new file mode 100644 index 00000000000..e1354aa6826 --- /dev/null +++ b/prusti/src/ide_helper/mod.rs @@ -0,0 +1,3 @@ +pub mod body_finder; +pub mod span_mapper; +pub mod ide_info; diff --git a/prusti/src/ide_helper/span_mapper.rs b/prusti/src/ide_helper/span_mapper.rs new file mode 100644 index 00000000000..bdb04480f1b --- /dev/null +++ b/prusti/src/ide_helper/span_mapper.rs @@ -0,0 +1,108 @@ +use prusti_common::config; +use prusti_interface::{ + environment::Environment, + specs::typed, +}; +use prusti_rustc_interface::{ + hir::{ + self, + ItemId, + Node, + def_id::LocalDefId, + BodyId, + ItemKind, + }, + middle::hir::map::Map, + span::Span, + middle::ty::TyCtxt, +}; + +use super::body_finder; + + +// use rustc_hir::hir::{ItemId}; + +fn collect_functions(hir: Map) { + for i in hir.items() { + let node = hir.get(i.hir_id()); + //let mut res = Vec::new(); + match node { + Node::Item(item) => { + println!("found an item"); + }, + _ => println!("found something else"), + } + } +} + + + +// fn span_from_offset(offset: u32) -> Span { +// +// } + + +pub fn map_span(env: &Environment<'_>, _def_spec: &typed::DefSpecificationMap) { + // should only be called if we already checked that this argument was given + let offset: u32 = 42; //config::spaninfo().unwrap(); + let tcx: TyCtxt = env.tcx(); + let hir: Map<'_> = tcx.hir(); + let path = env.name.source_path(); + let name = path.to_str().unwrap().clone(); + let pos: usize = offset.try_into().unwrap(); + // println!("env name: {}", env.name.source_path().display()); + // println!("char offset: {}", offset); + + // turn offset into span: + let span: Span = body_finder::charoffset_to_span(offset, offset, tcx); + println!("function identifier: {:#?}", span); + + let mut bodies = body_finder::find_enclosing_bodies(env.tcx(), span); + let body_id: hir::BodyId = bodies.next().unwrap(); + // let _def_id: LocalDefId = hir.body_owner_def_id(body_id); + let body = hir.body(body_id); + + println!("the body : {:#?}", body); + + // let src_map: &SourceMap = env.tcx().sess.source_map(); +} + + // let all_bodies = hir + // .items() + // .filter_map(|id| match hir.item(id).kind { + // ItemKind::Fn(_, _, body) => Some(body), + // _ => None, + // }); + + + + // let target: Node = hir.get(body.hir_id); + + // search target for the matching expression: + // collect_functions(hir); + // for i in hir.items() { + // let item = hir.item(i); + // println!("found item: {:#?}", item); + // } + // + // for i in hir.items() { + // let item : ItemId = i; + // let node : Node = hir.get(item.hir_id()); + // let item_span: Span = node.ident().unwrap().span; + // // let body = item + // println!("found matching span for node: {:#?}", node); + // } + + + // I guess we can iterate over bodies to find the span of what + // we are looking for: + + + // the information flow analysis crashes for some reason. + //let body_with_facts = borrowck_facts::get_body_with_borrowck_facts(tcx, def_id); + + // println!("body_id: {:?}", body); + //let results = flowistry::infoflow::compute_flow(tcx, body, body_with_facts); + //let location_domain = results.analysis.location_domain(); + + diff --git a/prusti/src/verifier.rs b/prusti/src/verifier.rs index 3520f9bd327..26a0e7ce901 100644 --- a/prusti/src/verifier.rs +++ b/prusti/src/verifier.rs @@ -1,13 +1,14 @@ //! A module that invokes the verifier `prusti-viper` use log::{debug, trace, warn}; -use prusti_common::{config, report::user}; +use prusti_common::{config::{self, print_hash}, report::user}; use prusti_interface::{ data::{VerificationResult, VerificationTask}, environment::Environment, specs::typed, }; use prusti_viper::verifier::Verifier; +use crate::ide_helper::ide_info; pub fn verify(env: Environment<'_>, def_spec: typed::DefSpecificationMap) { trace!("[verify] enter"); @@ -43,6 +44,13 @@ pub fn verify(env: Environment<'_>, def_spec: typed::DefSpecificationMap) { ); } } + + if config::show_ide_info() { + let ideinf = ide_info::IdeInfo::collect_procedures(&env, &verification_task); + let out = serde_json::to_string(&ideinf).unwrap(); + // probably should make this part of compilers output.. + println!("IDE_Info: {}", out); + } let verification_result = if verification_task.procedures.is_empty() && verification_task.types.is_empty() { From bbc07cc4e25635b93b9bcdd04a03f7d487e28309 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sat, 12 Nov 2022 00:55:13 +0100 Subject: [PATCH 02/74] some updates, had a lot of problems with just passing flags... --- Cargo.lock | 2 + prusti/src/ide_helper/ide_info.rs | 63 +++++++++++++++++-------------- prusti/src/verifier.rs | 7 +++- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e338117be9..c0f42b89831 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2065,6 +2065,8 @@ dependencies = [ "prusti-interface", "prusti-rustc-interface", "prusti-viper", + "serde", + "serde_json", ] [[package]] diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/ide_info.rs index 78fd8208ac8..a9ab87e814d 100644 --- a/prusti/src/ide_helper/ide_info.rs +++ b/prusti/src/ide_helper/ide_info.rs @@ -18,13 +18,30 @@ use serde::Serialize; #[derive(Serialize)] pub struct IdeInfo { - procedure_defs: Vec<(String, VscSpan)>, + procedure_defs: Vec, // additionally this will contain: // function_calls: // ... we'll see } +#[derive(Serialize)] +pub struct ProcDef { + name: String, + span: VscSpan, +} +/// a representation of spans that is more usable with VSCode. +#[derive(Serialize)] +pub struct VscSpan { + column_end: usize, + column_start: usize, + line_end: usize, + line_start: usize, + file_name: String, + is_primary: bool, + label: Option<()>, + expansion: Option<()>, +} impl IdeInfo { @@ -38,7 +55,11 @@ impl IdeInfo { let span = env.query.get_def_span(procedure); println!("found procedure: {}, span: {:?}", defpath, span); let vscspan = VscSpan::from_span(span, sourcemap).unwrap(); - procs.push((defpath, vscspan)); + + procs.push(ProcDef{ + name: defpath, + span: vscspan + }); } Self { procedure_defs: procs @@ -49,13 +70,6 @@ impl IdeInfo { -/// a representation of spans that is more usable with VSCode. -#[derive(Serialize)] -pub struct VscSpan { - from: (usize, usize), - to: (usize, usize), - filename: String, -} impl VscSpan { pub fn from_span(sp: Span, sourcemap: &SourceMap) -> Option { @@ -64,29 +78,20 @@ impl VscSpan { let fname = format!("{}", diag_filename); if let Ok((l1, l2)) = sourcemap.is_valid_span(sp) { - let loc1: Loc = l1; - let loc2: Loc = l2; - - let line1 = loc1.line; - let line2 = loc2.line; - - let char1 = loc1.col.0; - let char2 = loc2.col.0; - Some(Self { - from: (line1, char1), - to: (line2, char2), - filename: fname, + column_end: l2.col.0, + column_start: l1.col.0, + line_end: l2.line, + line_start: l2.line, + file_name: fname, + // the following 3 are not relevant here, we just want to be + // able to reuse the existing methods and the parser + // for spans in VSCode + is_primary: false, + label: None, + expansion: None, }) } else { - // println!("{:?}", lines); - // match lines { - // Ok(fl) => { - // - // }, - // Err(_) => - // } - // let parent = format!("{:?}", sp.parent().unwrap()); None } } diff --git a/prusti/src/verifier.rs b/prusti/src/verifier.rs index 26a0e7ce901..6b1d0c6f3c8 100644 --- a/prusti/src/verifier.rs +++ b/prusti/src/verifier.rs @@ -13,6 +13,7 @@ use crate::ide_helper::ide_info; pub fn verify(env: Environment<'_>, def_spec: typed::DefSpecificationMap) { trace!("[verify] enter"); + if env.diagnostic.has_errors() { warn!("The compiler reported an error, so the program will not be verified."); } else { @@ -49,7 +50,11 @@ pub fn verify(env: Environment<'_>, def_spec: typed::DefSpecificationMap) { let ideinf = ide_info::IdeInfo::collect_procedures(&env, &verification_task); let out = serde_json::to_string(&ideinf).unwrap(); // probably should make this part of compilers output.. - println!("IDE_Info: {}", out); + // actually not sure which way is better... + println!("Next line contains IDE-Info:"); + println!("{}", out); + } else { + println!("IDE Info flag was not passed correctly"); } let verification_result = From dc4b1d5ed9654a0fd714d3a0234d9d41a9a2c6e1 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Tue, 22 Nov 2022 07:39:34 +0100 Subject: [PATCH 03/74] passing method calls and verification items to ide --- prusti/src/ide_helper/body_finder.rs | 90 ---------------------- prusti/src/ide_helper/call_finder.rs | 55 ++++++++++++++ prusti/src/ide_helper/ide_info.rs | 72 +++++++++++------- prusti/src/ide_helper/mod.rs | 3 +- prusti/src/ide_helper/span_mapper.rs | 108 --------------------------- prusti/src/verifier.rs | 4 +- 6 files changed, 104 insertions(+), 228 deletions(-) delete mode 100644 prusti/src/ide_helper/body_finder.rs create mode 100644 prusti/src/ide_helper/call_finder.rs delete mode 100644 prusti/src/ide_helper/span_mapper.rs diff --git a/prusti/src/ide_helper/body_finder.rs b/prusti/src/ide_helper/body_finder.rs deleted file mode 100644 index 10999a01dd2..00000000000 --- a/prusti/src/ide_helper/body_finder.rs +++ /dev/null @@ -1,90 +0,0 @@ -use prusti_rustc_interface::{ - span::{ - Span, - BytePos, - }, - middle::{ - ty::TyCtxt, - hir::nested_filter::OnlyBodies, - }, - hir::{intravisit::Visitor, BodyId}, -}; - -// use rustc_middle::{hir::nested_filter::OnlyBodies, ty::TyCtxt}; - -// Most of this code is copied from flowistry! -// - - - -struct BodyFinder<'tcx> { - tcx: TyCtxt<'tcx>, - bodies: Vec<(Span, BodyId)>, -} - -impl<'tcx> Visitor<'tcx> for BodyFinder<'tcx> { - type NestedFilter = OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_nested_body(&mut self, id: BodyId) { - let hir = self.nested_visit_map(); - - // const/static items are considered to have bodies, so we want to exclude - // them from our search for functions - if !hir - .body_owner_kind(hir.body_owner_def_id(id)) - .is_fn_or_closure() - { - return; - } - - let body = hir.body(id); - self.visit_body(body); - - let hir = self.tcx.hir(); - let span = hir.span_with_body(hir.body_owner(id)); - - if !span.from_expansion() { - self.bodies.push((span, id)); - } - } -} - -pub fn find_bodies(tcx: TyCtxt) -> Vec<(Span, BodyId)> { - let mut finder = BodyFinder { - tcx, - bodies: Vec::new(), - }; - tcx.hir().visit_all_item_likes_in_crate(&mut finder); - finder.bodies -} - -pub fn find_enclosing_bodies(tcx: TyCtxt, sp: Span) -> impl Iterator { - let mut bodies = find_bodies(tcx); - bodies.retain(|(other, _)| other.contains(sp)); - bodies.sort_by_key(|(span, _)| span_length(span)); - bodies.into_iter().map(|(_, id)| id) -} - -pub fn charoffset_to_span(byte_start: u32, byte_end: u32, tcx: TyCtxt) -> Span { - let source_map = tcx.sess.source_map(); - let offset = 0; // TODO: sometimes there might be a problem with - // this approach! See to_span in range.rs, flowistry. - - // Span::with_root_ctxt( - // offset + BytePos(byte_start), - // offset + BytePos(byte_end), - // ) - - Span::with_root_ctxt( - BytePos(byte_start), - BytePos(byte_end), - ) -} - -pub fn span_length(span: &Span) -> u32 { - span.hi().0 - span.lo().0 -} diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs new file mode 100644 index 00000000000..65a6d24743b --- /dev/null +++ b/prusti/src/ide_helper/call_finder.rs @@ -0,0 +1,55 @@ +use prusti_rustc_interface::hir::intravisit::{ + self, + Visitor, +}; +use prusti_rustc_interface::middle::hir::map::Map; +use prusti_rustc_interface::hir::{ + Expr, ExprKind, +}; +use prusti_rustc_interface::span::Span; +use prusti_interface::environment::{ + Environment, + EnvQuery, +}; + + +pub struct CallSpanFinder<'tcx> { + pub env_query: EnvQuery<'tcx>, + pub spans: Vec<(String, Span)>, +} + +impl<'tcx> CallSpanFinder<'tcx> { + pub fn new(env: &Environment<'tcx>) -> Self { + Self { + env_query: env.query, + spans: Vec::new(), + } + } +} + +impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { + type Map = Map<'tcx>; + type NestedFilter = prusti_rustc_interface::middle::hir::nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.env_query.hir() + } + fn visit_expr(&mut self, ex: &'tcx Expr) { + intravisit::walk_expr(self, ex); + match ex.kind { + // Find all calls and methodcalls, not sure what the difference is.. + ExprKind::Call(e1, _e2) => { + // let ident = format!("{}", e1.hir_id); + // self.spans.push((ident.clone(), ex.span)); + }, + ExprKind::MethodCall(path, _e1, _e2, _sp) => { + let ident = format!("{}", path.ident.as_str()); + // let path: &'tcx PathSegment<'tcx> = p; + + self.spans.push((ident.clone(), ex.span)); + println!("MethodCall: {:?}", ident); + }, + _ => {} + } + } +} diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/ide_info.rs index a9ab87e814d..87568220b67 100644 --- a/prusti/src/ide_helper/ide_info.rs +++ b/prusti/src/ide_helper/ide_info.rs @@ -1,17 +1,13 @@ -use std::{fmt, ptr::null}; - use prusti_interface::data::VerificationTask; use prusti_interface::environment::Environment; -use prusti_rustc_interface::{ - span::{ - Span, Loc, +use prusti_rustc_interface::span::{ + Span, source_map::SourceMap, - FileLines, FileLinesResult, - }, -}; + }; use serde::Serialize; +use super::call_finder; // create some struct storing all the information the IDE will ever need. // needs to be transformable into json! @@ -19,6 +15,7 @@ use serde::Serialize; #[derive(Serialize)] pub struct IdeInfo { procedure_defs: Vec, + function_calls: Vec, // additionally this will contain: // function_calls: // ... we'll see @@ -43,28 +40,51 @@ pub struct VscSpan { expansion: Option<()>, } +// collect information about the program that will be passed to IDE: +pub fn collect_procedures(env: &Environment<'_>, verification_task: &VerificationTask) -> Vec{ + let sourcemap: &SourceMap = env.tcx().sess.source_map(); + let mut procs = Vec::new(); + for procedure in &verification_task.procedures { + let defpath = env.name.get_item_def_path(*procedure); + let span = env.query.get_def_span(procedure); + println!("found procedure: {}, span: {:?}", defpath, span); + let vscspan = VscSpan::from_span(span, sourcemap).unwrap(); + + procs.push(ProcDef{ + name: defpath, + span: vscspan + }); + } + procs +} + +pub fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, Span)>{ + // let l_hir = env.tcx().hir(); + // let hir_body = l_hir.body(); + + let mut fnvisitor = call_finder::CallSpanFinder::new(env); + // fnvisitor.visit_body(hir_body); + env.tcx().hir().visit_all_item_likes_in_crate(&mut fnvisitor); + + return fnvisitor.spans; +} impl IdeInfo { - - // collect information about the program that will be passed to IDE: - pub fn collect_procedures(env: &Environment<'_>, verification_task: &VerificationTask) -> Self { - let sourcemap: &SourceMap = env.tcx().sess.source_map(); - let mut procs = Vec::new(); - for procedure in &verification_task.procedures { - let defpath = env.name.get_item_def_path(*procedure); - let span = env.query.get_def_span(procedure); - println!("found procedure: {}, span: {:?}", defpath, span); - let vscspan = VscSpan::from_span(span, sourcemap).unwrap(); - - procs.push(ProcDef{ - name: defpath, - span: vscspan - }); - } + pub fn collect(env: &Environment<'_>, verification_task: &VerificationTask) -> Self { + let procs = collect_procedures(env, verification_task); + let source_map = env.tcx().sess.source_map(); + let fncalls = collect_fncalls(env).into_iter() + .map(|(name, sp)| + ProcDef { + name, + span: VscSpan::from_span(sp, source_map).unwrap() + } + ) + .collect(); Self { - procedure_defs: procs + procedure_defs: procs, + function_calls: fncalls, } - } } diff --git a/prusti/src/ide_helper/mod.rs b/prusti/src/ide_helper/mod.rs index e1354aa6826..4d110161de8 100644 --- a/prusti/src/ide_helper/mod.rs +++ b/prusti/src/ide_helper/mod.rs @@ -1,3 +1,2 @@ -pub mod body_finder; -pub mod span_mapper; pub mod ide_info; +pub mod call_finder; diff --git a/prusti/src/ide_helper/span_mapper.rs b/prusti/src/ide_helper/span_mapper.rs deleted file mode 100644 index bdb04480f1b..00000000000 --- a/prusti/src/ide_helper/span_mapper.rs +++ /dev/null @@ -1,108 +0,0 @@ -use prusti_common::config; -use prusti_interface::{ - environment::Environment, - specs::typed, -}; -use prusti_rustc_interface::{ - hir::{ - self, - ItemId, - Node, - def_id::LocalDefId, - BodyId, - ItemKind, - }, - middle::hir::map::Map, - span::Span, - middle::ty::TyCtxt, -}; - -use super::body_finder; - - -// use rustc_hir::hir::{ItemId}; - -fn collect_functions(hir: Map) { - for i in hir.items() { - let node = hir.get(i.hir_id()); - //let mut res = Vec::new(); - match node { - Node::Item(item) => { - println!("found an item"); - }, - _ => println!("found something else"), - } - } -} - - - -// fn span_from_offset(offset: u32) -> Span { -// -// } - - -pub fn map_span(env: &Environment<'_>, _def_spec: &typed::DefSpecificationMap) { - // should only be called if we already checked that this argument was given - let offset: u32 = 42; //config::spaninfo().unwrap(); - let tcx: TyCtxt = env.tcx(); - let hir: Map<'_> = tcx.hir(); - let path = env.name.source_path(); - let name = path.to_str().unwrap().clone(); - let pos: usize = offset.try_into().unwrap(); - // println!("env name: {}", env.name.source_path().display()); - // println!("char offset: {}", offset); - - // turn offset into span: - let span: Span = body_finder::charoffset_to_span(offset, offset, tcx); - println!("function identifier: {:#?}", span); - - let mut bodies = body_finder::find_enclosing_bodies(env.tcx(), span); - let body_id: hir::BodyId = bodies.next().unwrap(); - // let _def_id: LocalDefId = hir.body_owner_def_id(body_id); - let body = hir.body(body_id); - - println!("the body : {:#?}", body); - - // let src_map: &SourceMap = env.tcx().sess.source_map(); -} - - // let all_bodies = hir - // .items() - // .filter_map(|id| match hir.item(id).kind { - // ItemKind::Fn(_, _, body) => Some(body), - // _ => None, - // }); - - - - // let target: Node = hir.get(body.hir_id); - - // search target for the matching expression: - // collect_functions(hir); - // for i in hir.items() { - // let item = hir.item(i); - // println!("found item: {:#?}", item); - // } - // - // for i in hir.items() { - // let item : ItemId = i; - // let node : Node = hir.get(item.hir_id()); - // let item_span: Span = node.ident().unwrap().span; - // // let body = item - // println!("found matching span for node: {:#?}", node); - // } - - - // I guess we can iterate over bodies to find the span of what - // we are looking for: - - - // the information flow analysis crashes for some reason. - //let body_with_facts = borrowck_facts::get_body_with_borrowck_facts(tcx, def_id); - - // println!("body_id: {:?}", body); - //let results = flowistry::infoflow::compute_flow(tcx, body, body_with_facts); - //let location_domain = results.analysis.location_domain(); - - diff --git a/prusti/src/verifier.rs b/prusti/src/verifier.rs index 6b1d0c6f3c8..7b93688ba0a 100644 --- a/prusti/src/verifier.rs +++ b/prusti/src/verifier.rs @@ -1,7 +1,7 @@ //! A module that invokes the verifier `prusti-viper` use log::{debug, trace, warn}; -use prusti_common::{config::{self, print_hash}, report::user}; +use prusti_common::{config, report::user}; use prusti_interface::{ data::{VerificationResult, VerificationTask}, environment::Environment, @@ -47,7 +47,7 @@ pub fn verify(env: Environment<'_>, def_spec: typed::DefSpecificationMap) { } if config::show_ide_info() { - let ideinf = ide_info::IdeInfo::collect_procedures(&env, &verification_task); + let ideinf = ide_info::IdeInfo::collect(&env, &verification_task); let out = serde_json::to_string(&ideinf).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... From dc079fa99bcbdadb73171c63928347c249cb72f1 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sun, 4 Dec 2022 17:43:09 +0100 Subject: [PATCH 04/74] fixed duplicate method calls (this commit is not compiling however!) --- prusti-utils/src/config.rs | 8 ++++++++ prusti/src/callbacks.rs | 27 +++++++++++++++++++++++++-- prusti/src/ide_helper/fake_error.rs | 15 +++++++++++++++ prusti/src/verifier.rs | 22 +++++----------------- 4 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 prusti/src/ide_helper/fake_error.rs diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index 4626dcf1714..922412448de 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -165,6 +165,7 @@ lazy_static::lazy_static! { // Flags specifically for Prusti-Assistant: settings.set_default("show_ide_info", false).unwrap(); + settings.set_default("skip_verification", false).unwrap(); // Get the list of all allowed flags. @@ -976,3 +977,10 @@ pub fn enable_type_invariants() -> bool { pub fn show_ide_info() -> bool { read_setting("show_ide_info") } + +/// Also used by IDE, similar to the existing no_verify +/// flag, but still collects all the important information +/// to be used in combination with show_ide_info +pub fn skip_verification() -> bool { + read_setting("skip_verification") +} diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 74c2e2d7d63..a2b54510854 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -3,6 +3,7 @@ use prusti_common::config; use prusti_interface::{ environment::{mir_storage, Environment}, specs::{self, cross_crate::CrossCrateSpecs, is_spec_fn}, + data::VerificationTask, }; use prusti_rustc_interface::{ driver::Compilation, @@ -15,6 +16,7 @@ use prusti_rustc_interface::{ }, session::Session, }; +use crate::ide_helper::ide_info; #[derive(Default)] pub struct PrustiCompilerCalls; @@ -87,6 +89,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { spec_checker.check(&env); compiler.session().abort_if_errors(); + let hir = env.query.hir(); let mut spec_collector = specs::SpecCollector::new(&mut env); hir.walk_toplevel_module(&mut spec_collector); @@ -101,8 +104,28 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { } CrossCrateSpecs::import_export_cross_crate(&mut env, &mut def_spec); - if !config::no_verify() { - verify(env, def_spec); + // determine procedures that have to be verified. + let (annotated_procedures, types) = env.get_annotated_procedures_and_types(); + let verification_task = VerificationTask { + procedures: annotated_procedures, + types, + }; + + // todo: selective verification, filter procedures that are + // part of verification_task + + if config::show_ide_info() { + let ideinf = ide_info::IdeInfo::collect(&env, &verification_task); + let out = serde_json::to_string(&ideinf).unwrap(); + // probably should make this part of compilers output.. + // actually not sure which way is better... + println!("{}", out); + } + + // collect and output Information used by IDE: + + if !config::no_verify() && !config::skip_verification() { + verify(env, def_spec, verification_task); } }); diff --git a/prusti/src/ide_helper/fake_error.rs b/prusti/src/ide_helper/fake_error.rs new file mode 100644 index 00000000000..d739c763d69 --- /dev/null +++ b/prusti/src/ide_helper/fake_error.rs @@ -0,0 +1,15 @@ +use prusti_interface::{ + environment::Environment, + errors::{DiagnosticBuilder, EmissionGuarantee, MultiSpan}, +}; +use prusti_rustc_interface::span::span_encoding::DUMMY_SP; + + +pub fn fake_error(env: Environment<'tcx>) { + let sp = MultiSpan::from_span(DUMMY_SP); + let message = String::from("Not actually an error"); + let help = None; + let notes = []; + + env.diagnostic.span_err_with_help_and_notes(sp, msg, &help, ¬es); +} diff --git a/prusti/src/verifier.rs b/prusti/src/verifier.rs index 7b93688ba0a..77d33ea9e8c 100644 --- a/prusti/src/verifier.rs +++ b/prusti/src/verifier.rs @@ -8,9 +8,12 @@ use prusti_interface::{ specs::typed, }; use prusti_viper::verifier::Verifier; -use crate::ide_helper::ide_info; -pub fn verify(env: Environment<'_>, def_spec: typed::DefSpecificationMap) { +pub fn verify<'tcx>( + env: Environment<'tcx>, + def_spec: typed::DefSpecificationMap, + verification_task: VerificationTask<'tcx>, +) { trace!("[verify] enter"); @@ -20,11 +23,6 @@ pub fn verify(env: Environment<'_>, def_spec: typed::DefSpecificationMap) { debug!("Prepare verification task..."); // TODO: can we replace `get_annotated_procedures` with information // that is already in `def_spec`? - let (annotated_procedures, types) = env.get_annotated_procedures_and_types(); - let verification_task = VerificationTask { - procedures: annotated_procedures, - types, - }; debug!("Verification task: {:?}", &verification_task); user::message(format!( @@ -46,16 +44,6 @@ pub fn verify(env: Environment<'_>, def_spec: typed::DefSpecificationMap) { } } - if config::show_ide_info() { - let ideinf = ide_info::IdeInfo::collect(&env, &verification_task); - let out = serde_json::to_string(&ideinf).unwrap(); - // probably should make this part of compilers output.. - // actually not sure which way is better... - println!("Next line contains IDE-Info:"); - println!("{}", out); - } else { - println!("IDE Info flag was not passed correctly"); - } let verification_result = if verification_task.procedures.is_empty() && verification_task.types.is_empty() { From c1d86d2401410545ca5e46b7ae7b3d715a9139f5 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sun, 4 Dec 2022 18:57:40 +0100 Subject: [PATCH 05/74] Fixed the issue of no_verify causing subsequent verifications to fail by simply creating a fake error in the case that no_verify and show_ide_info are set. --- prusti-utils/src/config.rs | 12 +++++----- prusti/src/callbacks.rs | 35 +++++++++++++++++++++------- prusti/src/ide_helper/call_finder.rs | 10 ++++---- prusti/src/ide_helper/fake_error.rs | 12 +++++----- prusti/src/ide_helper/ide_info.rs | 10 ++++---- prusti/src/ide_helper/mod.rs | 1 + 6 files changed, 51 insertions(+), 29 deletions(-) diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index 922412448de..daf1e106bf7 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -165,7 +165,7 @@ lazy_static::lazy_static! { // Flags specifically for Prusti-Assistant: settings.set_default("show_ide_info", false).unwrap(); - settings.set_default("skip_verification", false).unwrap(); + settings.set_default::>("selective_verify", None).unwrap(); // Get the list of all allowed flags. @@ -978,9 +978,9 @@ pub fn show_ide_info() -> bool { read_setting("show_ide_info") } -/// Also used by IDE, similar to the existing no_verify -/// flag, but still collects all the important information -/// to be used in combination with show_ide_info -pub fn skip_verification() -> bool { - read_setting("skip_verification") +/// Used for selective verification, can be passed a String containing +/// the DefPath of the method to be verified +pub fn selective_verify() -> Option { + read_setting("selective_verify") } + diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index a2b54510854..22ef3762850 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -16,7 +16,7 @@ use prusti_rustc_interface::{ }, session::Session, }; -use crate::ide_helper::ide_info; +use crate::ide_helper::{ide_info, fake_error}; #[derive(Default)] pub struct PrustiCompilerCalls; @@ -106,16 +106,12 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // determine procedures that have to be verified. let (annotated_procedures, types) = env.get_annotated_procedures_and_types(); - let verification_task = VerificationTask { - procedures: annotated_procedures, - types, - }; // todo: selective verification, filter procedures that are // part of verification_task if config::show_ide_info() { - let ideinf = ide_info::IdeInfo::collect(&env, &verification_task); + let ideinf = ide_info::IdeInfo::collect(&env, &annotated_procedures); let out = serde_json::to_string(&ideinf).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... @@ -124,8 +120,31 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // collect and output Information used by IDE: - if !config::no_verify() && !config::skip_verification() { - verify(env, def_spec, verification_task); + if !config::no_verify() { //&& !config::skip_verification() { + if config::selective_verify().is_none() { + println!("verifying everything\n\n\n"); + let verification_task = VerificationTask { + procedures: annotated_procedures, + types, + }; + verify(env, def_spec, verification_task); + } else { + let target_def_path = config::selective_verify().unwrap(); + println!("Selective Verification invoked for method {}\n\n\n", target_def_path); + let procedures = annotated_procedures.into_iter().filter( + |x| env.name.get_item_def_path(*x) == target_def_path + ).collect(); + let selective_task = VerificationTask { + procedures, + types + }; + verify(env, def_spec, selective_task); + } + } else if config::show_ide_info() { + // add a fake error + // for now maybe only for our use-case + println!("Faking an error :)\n\n\n\n\n\n"); + fake_error::fake_error(env) } }); diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 65a6d24743b..fb36d6c211b 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -29,7 +29,7 @@ impl<'tcx> CallSpanFinder<'tcx> { impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { type Map = Map<'tcx>; - type NestedFilter = prusti_rustc_interface::middle::hir::nested_filter::All; + type NestedFilter = prusti_rustc_interface::middle::hir::nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.env_query.hir() @@ -38,15 +38,17 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { intravisit::walk_expr(self, ex); match ex.kind { // Find all calls and methodcalls, not sure what the difference is.. - ExprKind::Call(e1, _e2) => { + ExprKind::Call(_e1, _e2) => { // let ident = format!("{}", e1.hir_id); // self.spans.push((ident.clone(), ex.span)); }, - ExprKind::MethodCall(path, _e1, _e2, _sp) => { + //TODO: avoid duplicates by checking for nodes hir_id + ExprKind::MethodCall(path, _e1, _e2, sp) => { let ident = format!("{}", path.ident.as_str()); // let path: &'tcx PathSegment<'tcx> = p; + - self.spans.push((ident.clone(), ex.span)); + self.spans.push((ident.clone(), sp)); println!("MethodCall: {:?}", ident); }, _ => {} diff --git a/prusti/src/ide_helper/fake_error.rs b/prusti/src/ide_helper/fake_error.rs index d739c763d69..55aecff5953 100644 --- a/prusti/src/ide_helper/fake_error.rs +++ b/prusti/src/ide_helper/fake_error.rs @@ -1,15 +1,15 @@ -use prusti_interface::{ - environment::Environment, - errors::{DiagnosticBuilder, EmissionGuarantee, MultiSpan}, +use prusti_interface::environment::Environment; +use prusti_rustc_interface::{ + span::DUMMY_SP, + errors::MultiSpan, }; -use prusti_rustc_interface::span::span_encoding::DUMMY_SP; -pub fn fake_error(env: Environment<'tcx>) { +pub fn fake_error<'tcx>(env: Environment<'tcx>) { let sp = MultiSpan::from_span(DUMMY_SP); let message = String::from("Not actually an error"); let help = None; let notes = []; - env.diagnostic.span_err_with_help_and_notes(sp, msg, &help, ¬es); + env.diagnostic.span_err_with_help_and_notes(sp, &message, &help, ¬es); } diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/ide_info.rs index 87568220b67..26d1981b697 100644 --- a/prusti/src/ide_helper/ide_info.rs +++ b/prusti/src/ide_helper/ide_info.rs @@ -1,10 +1,10 @@ -use prusti_interface::data::VerificationTask; use prusti_interface::environment::Environment; use prusti_rustc_interface::span::{ Span, source_map::SourceMap, }; +use prusti_rustc_interface::hir::def_id::DefId; use serde::Serialize; use super::call_finder; @@ -41,10 +41,10 @@ pub struct VscSpan { } // collect information about the program that will be passed to IDE: -pub fn collect_procedures(env: &Environment<'_>, verification_task: &VerificationTask) -> Vec{ +pub fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec{ let sourcemap: &SourceMap = env.tcx().sess.source_map(); let mut procs = Vec::new(); - for procedure in &verification_task.procedures { + for procedure in procedures { let defpath = env.name.get_item_def_path(*procedure); let span = env.query.get_def_span(procedure); println!("found procedure: {}, span: {:?}", defpath, span); @@ -70,8 +70,8 @@ pub fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, Span)>{ } impl IdeInfo { - pub fn collect(env: &Environment<'_>, verification_task: &VerificationTask) -> Self { - let procs = collect_procedures(env, verification_task); + pub fn collect(env: &Environment<'_>, procedures: &Vec) -> Self { + let procs = collect_procedures(env, procedures); let source_map = env.tcx().sess.source_map(); let fncalls = collect_fncalls(env).into_iter() .map(|(name, sp)| diff --git a/prusti/src/ide_helper/mod.rs b/prusti/src/ide_helper/mod.rs index 4d110161de8..3812bc14aeb 100644 --- a/prusti/src/ide_helper/mod.rs +++ b/prusti/src/ide_helper/mod.rs @@ -1,2 +1,3 @@ pub mod ide_info; pub mod call_finder; +pub mod fake_error; From 207a4ad48fe2acc99a66c815a396e74942cac3f9 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sat, 10 Dec 2022 20:52:44 +0100 Subject: [PATCH 06/74] now finding correct defpath for function calls and also handling binops, even though they introduce some new problems --- prusti-utils/src/config.rs | 7 ++++++ prusti/src/callbacks.rs | 11 +++++----- prusti/src/ide_helper/call_finder.rs | 32 ++++++++++++++++++++++------ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index daf1e106bf7..ab9fa410397 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -165,6 +165,7 @@ lazy_static::lazy_static! { // Flags specifically for Prusti-Assistant: settings.set_default("show_ide_info", false).unwrap(); + settings.set_default("skip_verification", false).unwrap(); settings.set_default::>("selective_verify", None).unwrap(); @@ -978,6 +979,12 @@ pub fn show_ide_info() -> bool { read_setting("show_ide_info") } +/// Very similar to no_verify but should only be used by prusti-assistant +/// to avoid interference +pub fn skip_verification() -> bool { + read_setting("skip_verification") +} + /// Used for selective verification, can be passed a String containing /// the DefPath of the method to be verified pub fn selective_verify() -> Option { diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 22ef3762850..20d7d95062f 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -16,7 +16,7 @@ use prusti_rustc_interface::{ }, session::Session, }; -use crate::ide_helper::{ide_info, fake_error}; +use crate::ide_helper::{ide_info, fake_error::fake_error}; #[derive(Default)] pub struct PrustiCompilerCalls; @@ -120,7 +120,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // collect and output Information used by IDE: - if !config::no_verify() { //&& !config::skip_verification() { + if !config::no_verify() && !config::skip_verification() { if config::selective_verify().is_none() { println!("verifying everything\n\n\n"); let verification_task = VerificationTask { @@ -140,11 +140,12 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { }; verify(env, def_spec, selective_task); } - } else if config::show_ide_info() { + } else if config::show_ide_info() && config::skip_verification() { // add a fake error // for now maybe only for our use-case - println!("Faking an error :)\n\n\n\n\n\n"); - fake_error::fake_error(env) + // This stops cargo-prusti from successfully verifying crates + // it should not be able to after this invocation + fake_error(env) } }); diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index fb36d6c211b..38776416014 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -2,9 +2,12 @@ use prusti_rustc_interface::hir::intravisit::{ self, Visitor, }; -use prusti_rustc_interface::middle::hir::map::Map; +use prusti_rustc_interface::middle::{ + hir::map::Map, + ty::TyCtxt, +}; use prusti_rustc_interface::hir::{ - Expr, ExprKind, + Expr, ExprKind, QPath }; use prusti_rustc_interface::span::Span; use prusti_interface::environment::{ @@ -15,6 +18,7 @@ use prusti_interface::environment::{ pub struct CallSpanFinder<'tcx> { pub env_query: EnvQuery<'tcx>, + pub tcx: TyCtxt<'tcx>, pub spans: Vec<(String, Span)>, } @@ -23,6 +27,7 @@ impl<'tcx> CallSpanFinder<'tcx> { Self { env_query: env.query, spans: Vec::new(), + tcx: env.tcx(), } } } @@ -38,19 +43,32 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { intravisit::walk_expr(self, ex); match ex.kind { // Find all calls and methodcalls, not sure what the difference is.. - ExprKind::Call(_e1, _e2) => { - // let ident = format!("{}", e1.hir_id); - // self.spans.push((ident.clone(), ex.span)); + ExprKind::Call(e1, _e2) => { + match e1.kind { + ExprKind::Path(QPath::Resolved(_ty, path)) => { + let defid = path.res.def_id(); + let defpath = self.tcx.def_path_debug_str(defid); + println!("Call: {}", defpath); + self.spans.push((defpath, path.span)); + }, + _ => {}, + } }, - //TODO: avoid duplicates by checking for nodes hir_id ExprKind::MethodCall(path, _e1, _e2, sp) => { let ident = format!("{}", path.ident.as_str()); // let path: &'tcx PathSegment<'tcx> = p; - self.spans.push((ident.clone(), sp)); println!("MethodCall: {:?}", ident); }, + ExprKind::Binary(binop, _e1, _e2) | ExprKind::AssignOp(binop, _e1, _e2) => { + let ident = binop.node.as_str(); + self.spans.push((ident.to_string(), ex.span)); + }, + ExprKind::Unary(unop, _e1) => { + let ident = unop.as_str(); + self.spans.push((ident.to_string(), ex.span)); + }, _ => {} } } From 4636373342c5e5c27db5cf6538a247b096c5d25c Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sun, 11 Dec 2022 13:15:49 +0100 Subject: [PATCH 07/74] Fixed a bug for calls where the DefId can not be found apparently. --- prusti/src/ide_helper/call_finder.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 38776416014..0ff7d2316f1 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -46,10 +46,12 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { ExprKind::Call(e1, _e2) => { match e1.kind { ExprKind::Path(QPath::Resolved(_ty, path)) => { - let defid = path.res.def_id(); - let defpath = self.tcx.def_path_debug_str(defid); - println!("Call: {}", defpath); - self.spans.push((defpath, path.span)); + let defid = path.res.opt_def_id(); + if defid.is_some() { + let defpath = self.tcx.def_path_debug_str(defid.unwrap()); + println!("Call: {}", defpath); + self.spans.push((defpath, path.span)); + } }, _ => {}, } From e311df9d18bf0f47fb66b1eee5532e7b1b2d6dbc Mon Sep 17 00:00:00 2001 From: hegglinc Date: Mon, 12 Dec 2022 10:08:49 +0100 Subject: [PATCH 08/74] fixed minor bug and formatting --- prusti/src/callbacks.rs | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 20d7d95062f..f490959b462 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -1,9 +1,12 @@ -use crate::verifier::verify; +use crate::{ + ide_helper::{fake_error::fake_error, ide_info}, + verifier::verify, +}; use prusti_common::config; use prusti_interface::{ + data::VerificationTask, environment::{mir_storage, Environment}, specs::{self, cross_crate::CrossCrateSpecs, is_spec_fn}, - data::VerificationTask, }; use prusti_rustc_interface::{ driver::Compilation, @@ -16,7 +19,6 @@ use prusti_rustc_interface::{ }, session::Session, }; -use crate::ide_helper::{ide_info, fake_error::fake_error}; #[derive(Default)] pub struct PrustiCompilerCalls; @@ -89,7 +91,6 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { spec_checker.check(&env); compiler.session().abort_if_errors(); - let hir = env.query.hir(); let mut spec_collector = specs::SpecCollector::new(&mut env); hir.walk_toplevel_module(&mut spec_collector); @@ -107,10 +108,10 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // determine procedures that have to be verified. let (annotated_procedures, types) = env.get_annotated_procedures_and_types(); - // todo: selective verification, filter procedures that are + // todo: selective verification, filter procedures that are // part of verification_task - - if config::show_ide_info() { + + if config::show_ide_info() && !config::no_verify() { let ideinf = ide_info::IdeInfo::collect(&env, &annotated_procedures); let out = serde_json::to_string(&ideinf).unwrap(); // probably should make this part of compilers output.. @@ -130,17 +131,19 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { verify(env, def_spec, verification_task); } else { let target_def_path = config::selective_verify().unwrap(); - println!("Selective Verification invoked for method {}\n\n\n", target_def_path); - let procedures = annotated_procedures.into_iter().filter( - |x| env.name.get_item_def_path(*x) == target_def_path - ).collect(); - let selective_task = VerificationTask { - procedures, - types - }; + println!( + "Selective Verification invoked for method {}\n\n\n", + target_def_path + ); + let procedures = annotated_procedures + .into_iter() + .filter(|x| env.name.get_item_def_path(*x) == target_def_path) + .collect(); + let selective_task = VerificationTask { procedures, types }; verify(env, def_spec, selective_task); } - } else if config::show_ide_info() && config::skip_verification() { + } else if config::show_ide_info() && config::skip_verification() && !config::no_verify() + { // add a fake error // for now maybe only for our use-case // This stops cargo-prusti from successfully verifying crates From 3780ce16444a08b545c0404f6f92d71f430ead6e Mon Sep 17 00:00:00 2001 From: hegglinc Date: Mon, 12 Dec 2022 17:45:49 +0100 Subject: [PATCH 09/74] now finding defpath's for MethodCalls too --- prusti-utils/src/config.rs | 7 +++ prusti/src/ide_helper/call_finder.rs | 15 ++++-- prusti/src/ide_helper/ide_info.rs | 72 +++++++++++++++------------- 3 files changed, 57 insertions(+), 37 deletions(-) diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index ab9fa410397..ca58bf84208 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -167,6 +167,7 @@ lazy_static::lazy_static! { settings.set_default("show_ide_info", false).unwrap(); settings.set_default("skip_verification", false).unwrap(); settings.set_default::>("selective_verify", None).unwrap(); + settings.set_default::>("query_method_signature", None).unwrap(); // Get the list of all allowed flags. @@ -991,3 +992,9 @@ pub fn selective_verify() -> Option { read_setting("selective_verify") } +/// A flag that can be used to ask the compiler for the declaration / +/// signature of a method, used to automatically generate a skeleton +/// for an external specification +pub fn query_method_signature() -> Option { + read_setting("query_method_signature") +} diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 0ff7d2316f1..625c3d34505 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -49,7 +49,6 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { let defid = path.res.opt_def_id(); if defid.is_some() { let defpath = self.tcx.def_path_debug_str(defid.unwrap()); - println!("Call: {}", defpath); self.spans.push((defpath, path.span)); } }, @@ -59,9 +58,17 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { ExprKind::MethodCall(path, _e1, _e2, sp) => { let ident = format!("{}", path.ident.as_str()); // let path: &'tcx PathSegment<'tcx> = p; - - self.spans.push((ident.clone(), sp)); - println!("MethodCall: {:?}", ident); + let maybe_method_def_id = self + .tcx + .typeck(ex.hir_id.owner.def_id) + .type_dependent_def_id(ex.hir_id); + if let Some(method_def_id) = maybe_method_def_id { + let is_local = method_def_id.as_local().is_some(); + if !is_local { + let defpath = self.tcx.def_path_debug_str(method_def_id); + self.spans.push((defpath, sp)); + } + } }, ExprKind::Binary(binop, _e1, _e2) | ExprKind::AssignOp(binop, _e1, _e2) => { let ident = binop.node.as_str(); diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/ide_info.rs index 26d1981b697..7b96f9f1ef1 100644 --- a/prusti/src/ide_helper/ide_info.rs +++ b/prusti/src/ide_helper/ide_info.rs @@ -1,13 +1,13 @@ +use prusti_common::config; use prusti_interface::environment::Environment; -use prusti_rustc_interface::span::{ - Span, - source_map::SourceMap, - }; -use prusti_rustc_interface::hir::def_id::DefId; +use prusti_rustc_interface::{ + hir::def_id::DefId, + span::{source_map::SourceMap, Span}, +}; -use serde::Serialize; use super::call_finder; +use serde::Serialize; // create some struct storing all the information the IDE will ever need. // needs to be transformable into json! @@ -21,6 +21,26 @@ pub struct IdeInfo { // ... we'll see } +impl IdeInfo { + pub fn collect(env: &Environment<'_>, procedures: &Vec) -> Self { + let procs = collect_procedures(env, procedures); + let source_map = env.tcx().sess.source_map(); + let fncalls = collect_fncalls(env) + .into_iter() + .map(|(name, sp)| ProcDef { + name, + span: VscSpan::from_span(sp, source_map).unwrap(), + }) + .collect(); + // For declaring external specifications: + let _queried_source = collect_queried_signatures(env); + Self { + procedure_defs: procs, + function_calls: fncalls, + } + } +} + #[derive(Serialize)] pub struct ProcDef { name: String, @@ -41,7 +61,7 @@ pub struct VscSpan { } // collect information about the program that will be passed to IDE: -pub fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec{ +fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec { let sourcemap: &SourceMap = env.tcx().sess.source_map(); let mut procs = Vec::new(); for procedure in procedures { @@ -50,46 +70,32 @@ pub fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec println!("found procedure: {}, span: {:?}", defpath, span); let vscspan = VscSpan::from_span(span, sourcemap).unwrap(); - procs.push(ProcDef{ - name: defpath, - span: vscspan + procs.push(ProcDef { + name: defpath, + span: vscspan, }); } procs } -pub fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, Span)>{ +fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, Span)> { // let l_hir = env.tcx().hir(); // let hir_body = l_hir.body(); let mut fnvisitor = call_finder::CallSpanFinder::new(env); // fnvisitor.visit_body(hir_body); - env.tcx().hir().visit_all_item_likes_in_crate(&mut fnvisitor); + env.tcx() + .hir() + .visit_all_item_likes_in_crate(&mut fnvisitor); return fnvisitor.spans; } -impl IdeInfo { - pub fn collect(env: &Environment<'_>, procedures: &Vec) -> Self { - let procs = collect_procedures(env, procedures); - let source_map = env.tcx().sess.source_map(); - let fncalls = collect_fncalls(env).into_iter() - .map(|(name, sp)| - ProcDef { - name, - span: VscSpan::from_span(sp, source_map).unwrap() - } - ) - .collect(); - Self { - procedure_defs: procs, - function_calls: fncalls, - } - } -} - - +fn collect_queried_signatures(env: &Environment<'_>) -> Option { + let def_path_str: String = config::query_method_signature()?; + return Some(String::from("hello")) +} impl VscSpan { pub fn from_span(sp: Span, sourcemap: &SourceMap) -> Option { @@ -104,7 +110,7 @@ impl VscSpan { line_end: l2.line, line_start: l2.line, file_name: fname, - // the following 3 are not relevant here, we just want to be + // the following 3 are not relevant here, we just want to be // able to reuse the existing methods and the parser // for spans in VSCode is_primary: false, From e518ef4d6433daaf5d0d3978065daa750c7b33a0 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Mon, 19 Dec 2022 09:49:31 +0100 Subject: [PATCH 10/74] Figured out how to resolve MethodCalls, but we are still in trouble.. --- prusti/src/ide_helper/call_finder.rs | 56 +++++++++++++++++++--------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 625c3d34505..890e29bc4a1 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -7,7 +7,7 @@ use prusti_rustc_interface::middle::{ ty::TyCtxt, }; use prusti_rustc_interface::hir::{ - Expr, ExprKind, QPath + Expr, ExprKind, QPath, }; use prusti_rustc_interface::span::Span; use prusti_interface::environment::{ @@ -15,7 +15,6 @@ use prusti_interface::environment::{ EnvQuery, }; - pub struct CallSpanFinder<'tcx> { pub env_query: EnvQuery<'tcx>, pub tcx: TyCtxt<'tcx>, @@ -44,31 +43,51 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { match ex.kind { // Find all calls and methodcalls, not sure what the difference is.. ExprKind::Call(e1, _e2) => { - match e1.kind { - ExprKind::Path(QPath::Resolved(_ty, path)) => { - let defid = path.res.opt_def_id(); - if defid.is_some() { - let defpath = self.tcx.def_path_debug_str(defid.unwrap()); - self.spans.push((defpath, path.span)); - } - }, - _ => {}, + println!("found a call: resolving!"); + if let ExprKind::Path(ref qself) = e1.kind { + let tyck_res = self.tcx.typeck(e1.hir_id.owner.def_id); + let res = tyck_res.qpath_res(qself, e1.hir_id); + if let prusti_rustc_interface::hir::def::Res::Def(_, def_id) = res { + let defpath = self.tcx.def_path_debug_str(def_id); + println!("Call DefPath: {}", defpath); + self.spans.push((defpath, ex.span)) + } } }, - ExprKind::MethodCall(path, _e1, _e2, sp) => { - let ident = format!("{}", path.ident.as_str()); + ExprKind::MethodCall(path, e1, _e2, sp) => { + let ident = format!("Method Call: {}", path.ident.as_str()); // let path: &'tcx PathSegment<'tcx> = p; let maybe_method_def_id = self .tcx .typeck(ex.hir_id.owner.def_id) .type_dependent_def_id(ex.hir_id); if let Some(method_def_id) = maybe_method_def_id { - let is_local = method_def_id.as_local().is_some(); - if !is_local { - let defpath = self.tcx.def_path_debug_str(method_def_id); - self.spans.push((defpath, sp)); + let _is_local = method_def_id.as_local().is_some(); + // if !is_local { + let defpath = self.tcx.def_path_debug_str(method_def_id); + println!("Found MethodCall: {}", defpath.clone()); + self.spans.push((defpath, sp)); + // } + // try something + let owner_def_id = ex.hir_id.owner.def_id; + let owner_def_path = self.tcx.def_path_debug_str(owner_def_id.into()); + // what if we give it e1's defid + let tyck_res = self.tcx.typeck(owner_def_id); + let substs = tyck_res.node_substs(ex.hir_id); + let (other_def_id, _subst) = self + .env_query + .resolve_method_call(owner_def_id, method_def_id, substs); + if other_def_id != method_def_id { + let other_def_path = self.tcx.def_path_debug_str(other_def_id); + println!("Resolved method call to different defpath: {}", other_def_path); + self.spans.push((other_def_path, sp)); } - } + } // local methods do not need external specifications. + // should we still allow it? + // the interesting thing about methodCalls is that in the case of traits + // there are 2 possible things we can annotate. The behavior of the trait + // methods itself, or the methods of this specific implementation + }, ExprKind::Binary(binop, _e1, _e2) | ExprKind::AssignOp(binop, _e1, _e2) => { let ident = binop.node.as_str(); @@ -82,3 +101,4 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { } } } + From 295040c06312d679f71c69031aa3732c3104fdfa Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Mon, 19 Dec 2022 20:35:58 +0100 Subject: [PATCH 11/74] Commit finally to own fork --- Cargo.lock | 3 + .../src/vir/program_normalization.rs | 3 +- prusti-common/src/vir/to_viper.rs | 7 +- prusti-server/Cargo.toml | 2 + prusti-server/src/java_exception.rs | 41 ++++ prusti-server/src/jni_utils.rs | 216 ++++++++++++++++++ prusti-server/src/lib.rs | 4 + prusti-server/src/process_verification.rs | 150 +++++++++++- prusti-server/src/server.rs | 7 +- prusti-server/src/verification_request.rs | 5 +- prusti-smt-solver/Cargo.toml | 3 +- prusti-smt-solver/src/context.rs | 3 + prusti-utils/src/config.rs | 12 + .../src/encoder/errors/position_manager.rs | 4 + .../mir/pure/specifications/encoder_poly.rs | 10 +- prusti-viper/src/verifier.rs | 60 ++++- viper-sys/build.rs | 23 ++ viper/src/ast_factory/expression.rs | 4 +- viper/src/verification_context.rs | 4 + viper/src/verification_result.rs | 7 +- viper/src/verifier.rs | 10 +- vir/defs/polymorphic/ast/expr.rs | 14 ++ 22 files changed, 562 insertions(+), 30 deletions(-) create mode 100644 prusti-server/src/java_exception.rs create mode 100644 prusti-server/src/jni_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 1d77374b4a3..54247444d3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2051,6 +2051,7 @@ dependencies = [ "bincode", "clap", "env_logger", + "jni", "lazy_static", "log", "num_cpus", @@ -2060,6 +2061,7 @@ dependencies = [ "tokio", "url", "viper", + "viper-sys", "warp", ] @@ -2069,6 +2071,7 @@ version = "0.1.0" dependencies = [ "async-std", "futures", + "log", ] [[package]] diff --git a/prusti-common/src/vir/program_normalization.rs b/prusti-common/src/vir/program_normalization.rs index 5ad6af54673..3e99bbb1fad 100644 --- a/prusti-common/src/vir/program_normalization.rs +++ b/prusti-common/src/vir/program_normalization.rs @@ -9,6 +9,7 @@ use viper::VerificationResult; use fxhash::{FxHashSet, FxHashMap}; use log::{debug, trace}; +#[derive(Clone)] pub enum NormalizationInfo { LegacyProgram { original_position_ids: Vec }, LowProgram, @@ -77,7 +78,7 @@ impl NormalizationInfo { ) } - /// Denormalize the verification result. + /// Denormalize the program pub fn denormalize_program(&self, program: &mut Program) { match program { Program::Low(_) => debug!("No denormalization is done for vir::low programs."), diff --git a/prusti-common/src/vir/to_viper.rs b/prusti-common/src/vir/to_viper.rs index 2531de393bc..a73f49d7bb2 100644 --- a/prusti-common/src/vir/to_viper.rs +++ b/prusti-common/src/vir/to_viper.rs @@ -659,12 +659,15 @@ impl<'v> ToViper<'v, viper::Expr<'v>> for Expr { right.to_viper(context, ast), pos.to_viper(context, ast), ), - Expr::ForAll(ref vars, ref triggers, ref body, ref pos) => ast.forall_with_pos( + Expr::ForAll(ref vars, ref triggers, ref body, ref pos) => { + info!("FORALL pos: {:?}", pos); + ast.forall_with_pos( &vars.to_viper_decl(context, ast)[..], &(triggers, pos).to_viper(context, ast), body.to_viper(context, ast), pos.to_viper(context, ast), - ), + ) + }, Expr::Exists(ref vars, ref triggers, ref body, ref pos) => ast.exists_with_pos( &vars.to_viper_decl(context, ast)[..], &(triggers, pos).to_viper(context, ast), diff --git a/prusti-server/Cargo.toml b/prusti-server/Cargo.toml index 416a1bfee84..db7174a0a76 100644 --- a/prusti-server/Cargo.toml +++ b/prusti-server/Cargo.toml @@ -19,6 +19,7 @@ doctest = false [dependencies] log = { version = "0.4", features = ["release_max_level_info"] } viper = { path = "../viper" } +viper-sys = { path = "../viper-sys" } prusti-common = { path = "../prusti-common" } env_logger = "0.9" clap = { version = "3.2", features = ["derive"] } @@ -29,6 +30,7 @@ serde = { version = "1.0", features = ["derive"] } reqwest = { version = "0.11", features = ["json"] } warp = "0.3" tokio = "1.20" +jni = { version = "0.19", features = ["invocation"] } [dev-dependencies] lazy_static = "1.4.0" diff --git a/prusti-server/src/java_exception.rs b/prusti-server/src/java_exception.rs new file mode 100644 index 00000000000..2bbc7fa20d6 --- /dev/null +++ b/prusti-server/src/java_exception.rs @@ -0,0 +1,41 @@ +// © 2020, ETH Zurich +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// The structure describing a Java exception +#[derive(Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct JavaException { + message: String, + stack_trace: String, +} + +impl JavaException { + pub fn new(message: String, stack_trace: String) -> Self { + JavaException { + message, + stack_trace, + } + } + + pub fn get_message(&self) -> &str { + &self.message + } + + pub fn get_stack_trace(&self) -> &str { + &self.stack_trace + } +} + +impl std::fmt::Display for JavaException { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Java exception: {}", self.message) + } +} + +impl std::fmt::Debug for JavaException { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Java exception: {} - {}", self.message, self.stack_trace) + } +} diff --git a/prusti-server/src/jni_utils.rs b/prusti-server/src/jni_utils.rs new file mode 100644 index 00000000000..0a10bd11369 --- /dev/null +++ b/prusti-server/src/jni_utils.rs @@ -0,0 +1,216 @@ +// © 2019, ETH Zurich +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::java_exception::JavaException; +use jni::{ + errors::{Error, Result as JniResult}, + objects::{JObject, JString}, + strings::JNIString, + sys::jsize, + JNIEnv, +}; +use log::error; +use std::collections::HashMap; +use viper_sys::wrappers::*; + +#[derive(Clone, Copy)] +pub struct JniUtils<'a> { + env: &'a JNIEnv<'a>, +} + +impl<'a> JniUtils<'a> { + pub fn new(env: &'a JNIEnv) -> Self { + JniUtils { env } + } + + /// Generates the stack trace from a Java Exception + pub fn get_stack_trace(&self, t: JObject<'a>) -> JniResult { + let sw = java::io::StringWriter::with(self.env).new()?; + let pw = java::io::PrintWriter::with(self.env).new(sw)?; + java::lang::Throwable::with(self.env).call_printStackTrace(t, pw)?; + Ok(self.to_string(sw)) + } + + /// Unwrap a JniResult, retrieving information on raised Java exceptions. + pub fn unwrap_or_exception(&self, res: JniResult) -> Result { + res.map_err(|error| { + if let Error::JavaException = error { + let exception = self.env.exception_occurred().unwrap_or_else(|e| { + error!("{} source: {:?}", e, std::error::Error::source(&e)); + panic!("Failed to get the raised Java exception."); + }); + self.env.exception_clear().unwrap_or_else(|e| { + error!("{} source: {:?}", e, std::error::Error::source(&e)); + panic!("Failed to clear an exception in the process of being thrown."); + }); + // Retrieve information on the Java exception. + // The internal calls `unwrap_result` might lead to infinite recursion. + let exception_message = self.to_string(*exception); + let stack_trace = self.unwrap_result(self.get_stack_trace(*exception)); + JavaException::new(exception_message, stack_trace) + } else { + // This is not a Java exception + error!("{} source: {:?}", error, std::error::Error::source(&error)); + panic!("{}", error); + } + }) + } + + /// Unwrap a JniResult. + pub fn unwrap_result(&self, res: JniResult) -> T { + self.unwrap_or_exception(res) + .unwrap_or_else(|java_exception| panic!("{:?}", java_exception)) + } + + /// Converts a Rust Option to a Scala Option + pub fn new_option(&self, opt: Option>) -> JObject<'a> { + match opt { + Some(o) => self.unwrap_result(scala::Some::with(self.env).new(o)), + None => self.unwrap_result(scala::None_object::with(self.env).singleton()), + } + } + + /// Converts a Rust String to a Java String + pub fn new_string>(&self, string: S) -> JObject<'a> { + self.unwrap_result(self.env.new_string(string)).into() + } + + pub fn new_map(&self, items: &[(JObject<'a>, JObject<'a>)]) -> JObject<'a> { + let hash_map_wrapper = scala::collection::immutable::HashMap::with(self.env); + let mut result = self.unwrap_result(hash_map_wrapper.new()); + for &(k, v) in items { + result = self.unwrap_result(hash_map_wrapper.call_updated(result, k, v)); + } + result + } + + /// Converts a Rust number to a Java BigInt + pub fn new_big_int(&self, number: &dyn ToString) -> JObject { + let number_string = self.new_string(&number.to_string()); + + let java_big_integer = + self.unwrap_result(java::math::BigInteger::with(self.env).new(number_string)); + + self.unwrap_result(scala::math::BigInt::with(self.env).new(java_big_integer)) + } + + /// Converts a Rust Vec to a Scala Seq + pub fn new_seq(&self, objects: &[JObject]) -> JObject { + let array_buffer_wrapper = scala::collection::mutable::ArrayBuffer::with(self.env); + let len = objects.len(); + let res = self.unwrap_result(array_buffer_wrapper.new(len as i32)); + for obj in objects.iter() { + self.unwrap_result(array_buffer_wrapper.call_append(res, *obj)); + } + self.unwrap_result(array_buffer_wrapper.call_toSeq(res)) + } + + /// Converts a Java String to a Rust String + pub fn get_string(&self, string_object: JObject) -> String { + self.unwrap_result(self.env.get_string(JString::from(string_object))) + .into() + } + + /// Calls the "toString" method on a Java object and returns the result as a Rust String + pub fn to_string(self, object: JObject) -> String { + let object_wrapper = java::lang::Object::with(self.env); + let string_object = self.unwrap_result(object_wrapper.call_toString(object)); + self.get_string(string_object) + } + + /// Returns the name of the class of a Java object as Rust String + pub fn class_name(&self, object: JObject) -> String { + let class: JObject = self.unwrap_result(self.env.get_object_class(object)).into(); + let class_name_object = + self.unwrap_result(java::lang::Class::with(self.env).call_getName(class)); + self.get_string(class_name_object) + } + + /// Convert a Scala Seq to a Rust Vec + pub fn seq_to_vec(&self, sequence: JObject<'a>) -> Vec> { + let mut res: Vec = vec![]; + let seq_wrapper = scala::collection::Seq::with(self.env); + let length = self.unwrap_result(seq_wrapper.call_length(sequence)); + for i in 0..length { + let item: JObject = self.unwrap_result(seq_wrapper.call_apply(sequence, i)); + res.push(item); + } + res + } + + pub fn list_to_vec(&self, list: JObject<'a>) -> Vec> { + let list_wrapper = scala::collection::immutable::List::with(self.env); + let seq = self.unwrap_result(list_wrapper.call_toSeq(list)); + self.seq_to_vec(seq) + } + + pub fn vec_to_vec(&self, list: JObject<'a>) -> Vec> { + let vec_wrapper = scala::collection::immutable::Vector::with(self.env); + let seq = self.unwrap_result(vec_wrapper.call_toSeq(list)); + self.seq_to_vec(seq) + } + + /// Converts a Scala Map (using strings! JObjects are not hashable) to a Rust HashMap + pub fn stringmap_to_hashmap(&self, map: JObject<'a>) -> HashMap> { + let iter_wrapper = scala::collection::Iterable::with(self.env); + let product_wrapper = scala::Product::with(self.env); + let seq = self.unwrap_result(iter_wrapper.call_toSeq(map)); + self.seq_to_vec(seq) + .into_iter() + .map(|item| { + let item1 = self.unwrap_result(product_wrapper.call_productElement(item, 0)); + let item2 = self.unwrap_result(product_wrapper.call_productElement(item, 1)); + (self.to_string(item1), item2) + }) + .collect::>() + } + + /// Converts a Scala Map to a Vec of its keys. The order of keys is + /// maintained for map types with a consistent iteration order. + pub fn stringmap_to_keyvec(&self, map: JObject<'a>) -> Vec { + let iter_wrapper = scala::collection::Iterable::with(self.env); + let product_wrapper = scala::Product::with(self.env); + let seq = self.unwrap_result(iter_wrapper.call_toSeq(map)); + self.seq_to_vec(seq) + .into_iter() + .map(|item| { + let item = self.unwrap_result(product_wrapper.call_productElement(item, 0)); + self.to_string(item) + }) + .collect::>() + } + + /// Checks if an object is a subtype of a Java class + pub fn is_instance_of(&self, object: JObject, class: &str) -> bool { + let object_class = self.unwrap_result(self.env.get_object_class(object)); + let super_class = self.unwrap_result(self.env.find_class(class)); + self.unwrap_result(self.env.is_assignable_from(object_class, super_class)) + } + + /// Returns a new Java array of objects, initialised with null values + pub fn new_object_array(&self, length: jsize) -> JObject { + let object_class = self.unwrap_result(self.env.find_class("java/lang/Object")); + JObject::from(self.unwrap_result(self.env.new_object_array( + length, + object_class, + JObject::null(), + ))) + } + + /// Converts a Scala Seq to a Java Array + #[allow(dead_code)] + pub fn seq_to_array(&self, sequence: JObject<'a>, elements_class_ref: &str) -> JObject<'a> { + let elements_class = self.unwrap_result(self.env.find_class(elements_class_ref)); + let class_tag_object_wrapper = scala::reflect::ClassTag_object::with(self.env); + let class_tag_object = self.unwrap_result(class_tag_object_wrapper.singleton()); + let elements_class_tag = self.unwrap_result( + class_tag_object_wrapper.call_apply(class_tag_object, elements_class.into()), + ); + self.unwrap_result( + scala::collection::Seq::with(self.env).call_toArray(sequence, elements_class_tag), + ) + } +} diff --git a/prusti-server/src/lib.rs b/prusti-server/src/lib.rs index cce8f489530..f166b66985d 100644 --- a/prusti-server/src/lib.rs +++ b/prusti-server/src/lib.rs @@ -8,11 +8,15 @@ mod client; mod process_verification; mod server; mod verification_request; +mod jni_utils; +mod java_exception; pub use client::*; pub use process_verification::*; pub use server::*; pub use verification_request::*; +pub use jni_utils::*; +pub use java_exception::*; // Futures returned by `Client` need to be executed in a compatible tokio runtime. pub use tokio; diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index 5cb29a3ecba..c19c896b381 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::{VerificationRequest, ViperBackendConfig}; +use crate::{VerificationRequest, ViperBackendConfig, jni_utils::JniUtils}; use log::info; use prusti_common::{ config, @@ -12,16 +12,22 @@ use prusti_common::{ vir::{program_normalization::NormalizationInfo, ToViper}, Stopwatch, }; -use std::{fs::create_dir_all, path::PathBuf}; +use std::{fs::create_dir_all, path::PathBuf, thread, sync::Arc, collections::HashMap}; use viper::{ - smt_manager::SmtManager, Cache, VerificationBackend, VerificationContext, VerificationResult, + smt_manager::SmtManager, Cache, VerificationBackend, VerificationResult, Viper, }; +use viper_sys::wrappers::viper::*; +use std::time; +use std::sync::mpsc; pub fn process_verification_request<'v, 't: 'v>( - verification_context: &'v VerificationContext<'t>, + viper_arc: &Arc, mut request: VerificationRequest, cache: impl Cache, ) -> viper::VerificationResult { + let stopwatch = Stopwatch::start("prusti-server", "attach thread to JVM"); + let verification_context = viper_arc.attach_current_thread(); + stopwatch.finish(); let ast_utils = verification_context.new_ast_utils(); // Only for testing: Check that the normalization is reversible. @@ -105,11 +111,126 @@ pub fn process_verification_request<'v, 't: 'v>( // Workaround for https://github.com/viperproject/prusti-dev/issues/744 let mut stopwatch = Stopwatch::start("prusti-server", "verifier startup"); let mut verifier = - new_viper_verifier(program_name, verification_context, request.backend_config); + new_viper_verifier(program_name, &verification_context, request.backend_config); + // get the reporter + let env = &verification_context.env(); + let jni = JniUtils::new(env); + let verifier_wrapper = silver::verifier::Verifier::with(env); + let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); + let rep_glob_ref = env.new_global_ref(reporter).unwrap(); + + let viper_arc_clone = Arc::clone(&viper_arc); + //let program_clone = request.program.clone(); + let normalization_info_clone = normalization_info.clone(); + let (main_tx, thread_rx) = mpsc::channel(); + let (thread_tx, main_rx) = mpsc::channel(); + + // start thread for polling messages and print on receive + // TODO: maybe change to scope thread? + let polling_thread = thread::spawn(move || { + /*let functions = match program_clone { + program::Program::Legacy(p) => p.functions, + program::Program::Low(_) => panic!("Program::Low not yet supported!"), + };*/ + let verification_context = viper_arc_clone.attach_current_thread(); + let env = verification_context.env(); + let jni = JniUtils::new(env); + let reporter_instance = rep_glob_ref.as_obj(); + let reporter_wrapper = silver::reporter::PollingReporter::with(env); + let mut done = false; + let mut q_insts = HashMap::<(u64, String), u64>::new(); + while !done { + while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { + let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); + //println!("{}", jni.class_name(msg).as_str()); + match jni.class_name(msg).as_str() { + /* + "viper.silver.reporter.EntitySuccessMessage" => { + let msg_wrapper = silver::reporter::EntitySuccessMessage::with(env); + let verification_time = jni.unwrap_result(msg_wrapper.call_verificationTime(msg)); + //let cached = jni.unwrap_result(msg_wrapper.call_cached(msg)); + //println!("Received EntitySuccessMessage with time: {}", verification_time); + let concerning = jni.unwrap_result(msg_wrapper.call_concerning(msg)); + let member_wrapper = silver::ast::Member::with(env); + //println!("Concerning: {}", jni.get_string(jni.unwrap_result(member_wrapper.call_name(concerning)))); + match jni.class_name(concerning).as_str() { + "viper.silver.ast.Function" => { + let name = jni.get_string(jni.unwrap_result(member_wrapper.call_name(concerning))); + for f in &functions { + if let Some(expr) = &f.body { + if f.name == name { + println!("SUCCESS: Function: {}, time: {}, body_pos: {:?}", name, verification_time, expr.pos()); + } + } + } + } + _ => () + } + } + "viper.silver.reporter.EntityFailureMessage" => { + let msg_wrapper = silver::reporter::EntityFailureMessage::with(env); + let verification_time = jni.unwrap_result(msg_wrapper.call_verificationTime(msg)); + //let cached = jni.unwrap_result(msg_wrapper.call_cached(msg)); + //println!("Received EntityFailureMessage with time: {}", verification_time); + let concerning = jni.unwrap_result(msg_wrapper.call_concerning(msg)); + let member_wrapper = silver::ast::Member::with(env); + //println!("Concerning: {}", jni.get_string(jni.unwrap_result(member_wrapper.call_name(concerning)))); + match jni.class_name(concerning).as_str() { + "viper.silver.ast.Function" => { + let name = jni.get_string(jni.unwrap_result(member_wrapper.call_name(concerning))); + for f in &functions { + if let Some(expr) = &f.body { + if f.name == name { + println!("FAILURE: Function: {}, time: {}, body_pos: {:?}", name, verification_time, expr.pos()); + } + } + } + } + _ => () + } + }*/ + "viper.silver.reporter.QuantifierInstantiationsMessage" => { + let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); + let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); + // also matches the "-aux" quantifiers generated + // problem: we want to keep the aux and non-aux version apart + if q_name.starts_with("quant_with_posID") { + let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); + let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); + let parsed = stripped.parse::(); + match parsed { + Ok(pos_id) => { + let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); + let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); + info!("QuantifierInstantiationsMessage: {} {} {} {}", q_name, q_inst, pos_id, norm_pos_id); + // insert also updates the entry + q_insts.insert((norm_pos_id, q_name), q_inst.try_into().unwrap()); + () + } + _ => info!("Unexpected quantifier name {}", q_name) + } + } + } + _ => () + } + } + if !thread_rx.try_recv().is_err() { + info!("Received termination signal!"); + done = true; + } else { + thread::sleep(time::Duration::from_millis(10)); + } + } + thread_tx.send(q_insts).unwrap(); + }); stopwatch.start_next("verification"); let mut result = verifier.verify(viper_program); + main_tx.send(true).unwrap(); + let q_insts: HashMap<(u64, String), u64> = main_rx.recv().unwrap(); + polling_thread.join().unwrap(); + // Don't cache Java exceptions, which might be due to misconfigured paths. if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { info!( @@ -121,7 +242,24 @@ pub fn process_verification_request<'v, 't: 'v>( } normalization_info.denormalize_result(&mut result); - result + let enriched_result = match result { + VerificationResult::Success => { + info!("Enriching result"); + for ((pos_id, q_name), n) in q_insts.iter() { + info!("({} {}) {}", pos_id, q_name, n); + } + VerificationResult::EnrichedSuccess(q_insts) + }, + VerificationResult::Failure(errors)=> { + info!("Enriching result"); + for ((pos_id, q_name), n) in q_insts.iter() { + info!("({} {}) {}", pos_id, q_name, n); + } + VerificationResult::EnrichedFailure(errors, q_insts) + }, + _ => result, + }; + enriched_result }) } diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index 7c25d01598d..3576fea3276 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -61,10 +61,7 @@ where let cache = Arc::new(Mutex::new(cache_data)); let build_verification_request_handler = |viper_arc: Arc, cache| { move |request: VerificationRequest| { - let stopwatch = Stopwatch::start("prusti-server", "attach thread to JVM"); - let viper_thread = viper_arc.attach_current_thread(); - stopwatch.finish(); - process_verification_request(&viper_thread, request, &cache) + process_verification_request(&viper_arc, request, &cache) } }; @@ -84,7 +81,7 @@ where warp::reject::custom(BincodeReject(err)) }) }) - .map(build_verification_request_handler(viper, cache.clone())) + .map(build_verification_request_handler(viper.clone(), cache.clone())) .map(|result| { warp::http::Response::new( bincode::serialize(&result).expect("could not encode verification result"), diff --git a/prusti-server/src/verification_request.rs b/prusti-server/src/verification_request.rs index 244b64c4cfb..ed24a225ead 100644 --- a/prusti-server/src/verification_request.rs +++ b/prusti-server/src/verification_request.rs @@ -58,9 +58,10 @@ impl ViperBackendConfig { // model.partial changes the default case of functions in counterexamples // to #unspecified format!( - "smt.qi.eager_threshold={} model.partial={}", + "smt.qi.eager_threshold={} model.partial={} smt.qi.profile={}", config::smt_qi_eager_threshold(), - config::counterexample() + config::counterexample(), + config::smt_qi_profile() ), "--logLevel".to_string(), "ERROR".to_string(), diff --git a/prusti-smt-solver/Cargo.toml b/prusti-smt-solver/Cargo.toml index b1ce69b796b..d12af004aec 100644 --- a/prusti-smt-solver/Cargo.toml +++ b/prusti-smt-solver/Cargo.toml @@ -12,7 +12,8 @@ doctest = false # and no doc tests [dependencies] futures = "0.3" +log = { version = "0.4", features = ["release_max_level_info"] } [dependencies.async-std] version = "1.7.0" -features = ["attributes", "unstable"] \ No newline at end of file +features = ["attributes", "unstable"] diff --git a/prusti-smt-solver/src/context.rs b/prusti-smt-solver/src/context.rs index 2b3ba25d1a6..7dc9db069d7 100644 --- a/prusti-smt-solver/src/context.rs +++ b/prusti-smt-solver/src/context.rs @@ -5,6 +5,7 @@ use async_std::{ sync::Mutex, }; use std::{env::VarError, str::FromStr}; +use log::trace; #[derive(Debug)] pub(super) struct Context { @@ -15,6 +16,7 @@ pub(super) struct Context { impl Context { pub(crate) async fn new() -> Self { + trace!("BBBB"); let (log_file_path, z3_trace_path) = if let Some(port) = read_integer("PRUSTI_SMT_SOLVER_MANAGER_PORT") { let stream = TcpStream::connect(("127.0.0.1", port)).await.unwrap(); @@ -29,6 +31,7 @@ impl Context { } else { (None, None) }; + trace!("{:?} BBBB {:?}", log_file_path, z3_trace_path); let quantifier_instantiations_bound_global = read_integer("PRUSTI_SMT_QI_BOUND_GLOBAL"); let log_file = { if let Ok(value) = std::env::var("PRUSTI_LOG_SMT_INTERACTION") { diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index aca64f2a391..b657f8c49e5 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -99,6 +99,8 @@ lazy_static::lazy_static! { settings.set_default("quiet", false).unwrap(); settings.set_default("assert_timeout", 10_000).unwrap(); settings.set_default("smt_qi_eager_threshold", 1000).unwrap(); + settings.set_default("smt_qi_profile", true).unwrap(); + settings.set_default("report_qi_profile", true).unwrap(); settings.set_default("use_more_complete_exhale", true).unwrap(); settings.set_default("skip_unsupported_features", false).unwrap(); settings.set_default("internal_errors_as_warnings", false).unwrap(); @@ -479,6 +481,16 @@ pub fn smt_qi_eager_threshold() -> u64 { read_setting("smt_qi_eager_threshold") } +/// Set `qi.profile` value to the given one. +pub fn smt_qi_profile() -> bool { + read_setting("smt_qi_profile") +} + +/// Whether to report the quantifier instantiations (done as json). +pub fn report_qi_profile() -> bool { + read_setting("report_qi_profile") +} + /// Maximum time (in milliseconds) for the verifier to spend on checks. /// Set to None uses the verifier's default value. Maps to the verifier command-line /// argument `--checkTimeout`. diff --git a/prusti-viper/src/encoder/errors/position_manager.rs b/prusti-viper/src/encoder/errors/position_manager.rs index 1df43dea494..ae9a4b71764 100644 --- a/prusti-viper/src/encoder/errors/position_manager.rs +++ b/prusti-viper/src/encoder/errors/position_manager.rs @@ -86,4 +86,8 @@ impl<'tcx> PositionManager<'tcx> pub fn get_span(&self, pos: Position) -> Option<&MultiSpan> { self.source_span.get(&pos.id()) } + + pub fn get_span_from_id(&self, pos_id: u64) -> Option<&MultiSpan> { + self.source_span.get(&pos_id) + } } diff --git a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs index e9dc682220b..c39660f2d77 100644 --- a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs +++ b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs @@ -24,6 +24,7 @@ use prusti_rustc_interface::{ }; use rustc_hash::FxHashSet; use vir_crate::polymorphic::ExprIterator; +use log::info; // TODO: this variant (poly) should not need to exist, eventually should be // replaced by the high variant + lowering @@ -126,7 +127,7 @@ pub(super) fn inline_spec_item<'tcx>( pub(super) fn encode_quantifier<'tcx>( encoder: &Encoder<'_, 'tcx>, - _span: Span, + span: Span, encoded_args: Vec, is_exists: bool, parent_def_id: DefId, @@ -232,6 +233,9 @@ pub(super) fn encode_quantifier<'tcx>( body_substs, )?; + let pos = encoder.error_manager().register_span(parent_def_id, span); + info!("Encoding quantifier with span '{:?}' and pos '{:?}, parent_def_id '{:?}'", span, pos, parent_def_id); + // replace qvars with a nicer name based on quantifier depth to ensure that // quantifiers remain stable for caching let quantifier_depth = find_quantifier_depth(&encoded_body); @@ -265,6 +269,7 @@ pub(super) fn encode_quantifier<'tcx>( } else { vir_crate::polymorphic::Expr::implies(bounds.into_iter().conjoin(), encoded_body) }; + if is_exists { Ok(vir_crate::polymorphic::Expr::exists( fixed_qvars, @@ -272,10 +277,11 @@ pub(super) fn encode_quantifier<'tcx>( final_body, )) } else { - Ok(vir_crate::polymorphic::Expr::forall( + Ok(vir_crate::polymorphic::Expr::forall_with_pos( fixed_qvars, encoded_trigger_sets, final_body, + pos, )) } } diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 8d3abdbad9b..645899250fe 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -28,6 +28,8 @@ use ::log::{info, debug, error}; use prusti_server::{VerificationRequest, PrustiClient, process_verification_request, spawn_server_thread, ViperBackendConfig}; use prusti_rustc_interface::span::DUMMY_SP; use prusti_server::tokio::runtime::Builder; +use std::sync::Arc; +use std::collections::HashMap; // /// A verifier builder is an object that lives entire program's // /// lifetime, has no mutable state, and is responsible for constructing @@ -270,6 +272,8 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { programs.extend(self.encoder.get_core_proof_programs()); stopwatch.start_next("verifying Viper program"); + // TODO: make this not wait for all programs and after that make the quantifier + // instantiations more asynchronous also on the client/server communication let verification_results = verify_programs(self.env, programs); stopwatch.finish(); @@ -277,6 +281,9 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { let mut verification_errors : Vec<_> = vec![]; let mut consistency_errors : Vec<_> = vec![]; let mut java_exceptions : Vec<_> = vec![]; + // the quantifier instantiations among different programs are independent and thus are + // aggregated + let mut quantifier_instantiations: HashMap::<(u64, String), u64> = HashMap::new(); for (method_name, result) in verification_results.into_iter() { match result { viper::VerificationResult::Success => {} @@ -293,11 +300,52 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { viper::VerificationResult::JavaException(exception) => { java_exceptions.push((method_name, exception)); } + viper::VerificationResult::EnrichedSuccess(q_insts) => { + for (key, n) in q_insts { + match quantifier_instantiations.get(&key) { + Some(before) => { + quantifier_instantiations.insert(key, n+before); + () + }, + None => { + quantifier_instantiations.insert(key, n); + () + }, + } + } + } + viper::VerificationResult::EnrichedFailure(errors, q_insts) => { + for (key, n) in q_insts { + match quantifier_instantiations.get(&key) { + Some(before) => { + quantifier_instantiations.insert(key, n+before); + () + }, + None => { + quantifier_instantiations.insert(key, n); + () + }, + } + } + for error in errors.into_iter() { + verification_errors.push((method_name.clone(), error)); + } + } } } - // Convert verification results to Prusti errors let error_manager = self.encoder.error_manager(); + + // report the quantifier instantiations + info!("Reporting qi_profile"); + if config::report_qi_profile() { + for ((pos_id, q_name), n) in quantifier_instantiations { + let span = error_manager.position_manager().get_span_from_id(pos_id); + info!("{} {} {} {:?}", q_name, n, pos_id, span); + } + } + + // Convert verification results to Prusti errors let mut result = VerificationResult::Success; for (method, error) in consistency_errors.into_iter() { @@ -446,14 +494,12 @@ fn verify_programs(env: &Environment, programs: Vec) (program_name, result) }).collect() } else { - let mut stopwatch = Stopwatch::start("prusti-viper", "JVM startup"); - let viper = Viper::new_with_args(&config::viper_home(), config::extra_jvm_args()); - stopwatch.start_next("attach current thread to the JVM"); - let viper_thread = viper.attach_current_thread(); - stopwatch.finish(); + let stopwatch = Stopwatch::start("prusti-viper", "JVM startup"); + let viper_arc = Arc::new(Viper::new_with_args(&config::viper_home(), config::extra_jvm_args())); let mut cache = PersistentCache::load_cache(config::cache_path()); + stopwatch.finish(); verification_requests.map(|(program_name, request)| { - let result = process_verification_request(&viper_thread, request, &mut cache); + let result = process_verification_request(&viper_arc, request, &mut cache); (program_name, result) }).collect() } diff --git a/viper-sys/build.rs b/viper-sys/build.rs index 44fab0c2ea2..a2995811ad3 100644 --- a/viper-sys/build.rs +++ b/viper-sys/build.rs @@ -143,6 +143,28 @@ fn main() { java_class!("viper.silver.reporter.NoopReporter$", vec![ object_getter!(), ]), + java_class!("viper.silver.reporter.PollingReporter", vec![ + constructor!("(Ljava/lang/String;Lviper/silver/reporter/Reporter;)V"), + method!("hasNewMessage"), + method!("getNewMessage"), + ]), + java_class!("viper.silver.reporter.EntitySuccessMessage", vec![ + method!("verificationTime"), + method!("cached"), + method!("concerning"), + ]), + java_class!("viper.silver.reporter.EntityFailureMessage", vec![ + method!("verificationTime"), + method!("cached"), + method!("concerning"), + ]), + java_class!("viper.silver.reporter.QuantifierInstantiationsMessage", vec![ + method!("quantifier"), + method!("instantiations"), + ]), + java_class!("viper.silver.ast.Member", vec![ + method!("name"), + ]), java_class!("viper.silver.verifier.Verifier", vec![ method!("name"), method!("buildVersion"), @@ -150,6 +172,7 @@ fn main() { method!("start"), method!("stop"), method!("verify"), + method!("reporter"), ]), java_class!("viper.silver.ast.pretty.FastPrettyPrinter$", vec![ object_getter!(), diff --git a/viper/src/ast_factory/expression.rs b/viper/src/ast_factory/expression.rs index 7b0b9781c70..253f2359746 100644 --- a/viper/src/ast_factory/expression.rs +++ b/viper/src/ast_factory/expression.rs @@ -1166,7 +1166,7 @@ impl<'a> AstFactory<'a> { variables: &[LocalVarDecl], triggers: &[Trigger], expr: Expr, - _pos: Position, // FIXME: Why??? + pos: Position, ) -> Expr<'a> { build_ast_node_with_pos!( self, @@ -1175,7 +1175,7 @@ impl<'a> AstFactory<'a> { self.jni.new_seq(&map_to_jobjects!(variables)), self.jni.new_seq(&map_to_jobjects!(triggers)), expr.to_jobject(), - self.no_position().to_jobject() + pos.to_jobject() ) } diff --git a/viper/src/verification_context.rs b/viper/src/verification_context.rs index 3399d3f13dc..4547974b664 100644 --- a/viper/src/verification_context.rs +++ b/viper/src/verification_context.rs @@ -25,6 +25,10 @@ impl<'a> VerificationContext<'a> { VerificationContext { env: env_guard } } + pub fn env(&self) -> &AttachGuard<'a> { + &self.env + } + pub fn new_ast_factory(&self) -> AstFactory { AstFactory::new(&self.env) } diff --git a/viper/src/verification_result.rs b/viper/src/verification_result.rs index 4bf847217df..85d96348379 100644 --- a/viper/src/verification_result.rs +++ b/viper/src/verification_result.rs @@ -5,18 +5,23 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::{silicon_counterexample::SiliconCounterexample, JavaException}; +use std::collections::HashMap; /// The result of a verification request on a Viper program. #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum VerificationResult { /// The program verified. Success, - /// The program did not verify. + /// The program did not verify. Again with quantifier statistics. Failure(Vec), /// The program has consistency errors. ConsistencyErrors(Vec), /// The verification raised a Java exception. JavaException(JavaException), + /// Ships with quantifier statistics (currently only provided by Z3) that + /// map from position ID and name to the number of instantiations. + EnrichedSuccess(HashMap<(u64, String), u64>), + EnrichedFailure(Vec, HashMap<(u64, String), u64>), } impl VerificationResult { diff --git a/viper/src/verifier.rs b/viper/src/verifier.rs index 60241be0eec..e0ffed868d2 100644 --- a/viper/src/verifier.rs +++ b/viper/src/verifier.rs @@ -38,7 +38,7 @@ impl<'a> Verifier<'a> { let ast_utils = AstUtils::new(env); let verifier_wrapper = silver::verifier::Verifier::with(env); let verifier_instance = jni.unwrap_result(env.with_local_frame(16, || { - let reporter = if let Some(real_report_path) = report_path { + let pass_through_reporter = if let Some(real_report_path) = report_path { jni.unwrap_result(silver::reporter::CSVReporter::with(env).new( jni.new_string("csv_reporter"), jni.new_string(real_report_path.to_str().unwrap()), @@ -47,6 +47,10 @@ impl<'a> Verifier<'a> { jni.unwrap_result(silver::reporter::NoopReporter_object::with(env).singleton()) }; + let reporter = jni.unwrap_result(silver::reporter::PollingReporter::with(env).new( + jni.new_string("polling_reporter"), + pass_through_reporter)); + let debug_info = jni.new_seq(&[]); match backend { VerificationBackend::Silicon => { @@ -333,6 +337,10 @@ impl<'a> Verifier<'a> { } }) } + + pub fn verifier_instance(&self) -> &JObject<'a> { + &self.verifier_instance + } } impl<'a> Drop for Verifier<'a> { diff --git a/vir/defs/polymorphic/ast/expr.rs b/vir/defs/polymorphic/ast/expr.rs index 12e3183dfaf..c0e7214690b 100644 --- a/vir/defs/polymorphic/ast/expr.rs +++ b/vir/defs/polymorphic/ast/expr.rs @@ -352,6 +352,20 @@ impl Expr { }) } + pub fn forall_with_pos(vars: Vec, triggers: Vec, body: Expr, pos: Position) -> Self { + assert!( + !vars.is_empty(), + "A quantifier must have at least one variable." + ); + Expr::ForAll(ForAll { + variables: vars, + triggers, + body: Box::new(body), + position: pos, + }) + } + + pub fn exists(vars: Vec, triggers: Vec, body: Expr) -> Self { assert!( !vars.is_empty(), From 7adf64e010db1bd64336224c4b52f354803e2761 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 29 Dec 2022 17:46:41 +0100 Subject: [PATCH 12/74] Finally catching all relevant method calls as far as I can tell.. --- prusti/src/ide_helper/call_finder.rs | 152 ++++++++++++++++----------- prusti/src/ide_helper/ide_info.rs | 2 + 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 890e29bc4a1..6bb6f7a4c79 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -1,18 +1,12 @@ -use prusti_rustc_interface::hir::intravisit::{ - self, - Visitor, -}; -use prusti_rustc_interface::middle::{ - hir::map::Map, - ty::TyCtxt, -}; -use prusti_rustc_interface::hir::{ - Expr, ExprKind, QPath, -}; -use prusti_rustc_interface::span::Span; -use prusti_interface::environment::{ - Environment, - EnvQuery, +use prusti_interface::environment::{EnvQuery, Environment}; +use prusti_rustc_interface::{ + hir::{ + def_id::DefId, + intravisit::{self, Visitor}, + Expr, ExprKind, + }, + middle::{hir::map::Map, ty::TyCtxt}, + span::Span, }; pub struct CallSpanFinder<'tcx> { @@ -29,6 +23,24 @@ impl<'tcx> CallSpanFinder<'tcx> { tcx: env.tcx(), } } + + pub fn resolve_expression(&self, expr: &'tcx Expr) -> Result<(DefId, DefId), ()> { + let maybe_method_def_id = self + .tcx + .typeck(expr.hir_id.owner.def_id) + .type_dependent_def_id(expr.hir_id); + if let Some(method_def_id) = maybe_method_def_id { + let owner_def_id = expr.hir_id.owner.def_id; + let tyck_res = self.tcx.typeck(owner_def_id); + let substs = tyck_res.node_substs(expr.hir_id); + let (resolved_def_id, _subst) = + self.env_query + .resolve_method_call(owner_def_id, method_def_id, substs); + return Ok((method_def_id, resolved_def_id)); + } else { + return Err(()); + } + } } impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { @@ -38,10 +50,9 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { fn nested_visit_map(&mut self) -> Self::Map { self.env_query.hir() } - fn visit_expr(&mut self, ex: &'tcx Expr) { - intravisit::walk_expr(self, ex); - match ex.kind { - // Find all calls and methodcalls, not sure what the difference is.. + fn visit_expr(&mut self, expr: &'tcx Expr) { + intravisit::walk_expr(self, expr); + match expr.kind { ExprKind::Call(e1, _e2) => { println!("found a call: resolving!"); if let ExprKind::Path(ref qself) = e1.kind { @@ -50,55 +61,70 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { if let prusti_rustc_interface::hir::def::Res::Def(_, def_id) = res { let defpath = self.tcx.def_path_debug_str(def_id); println!("Call DefPath: {}", defpath); - self.spans.push((defpath, ex.span)) + self.spans.push((defpath, expr.span)) + } else { + println!("Resolving a call failed!\n\n\n"); } + } else { + println!("Resolving a Call failed!\n\n\n"); } - }, - ExprKind::MethodCall(path, e1, _e2, sp) => { - let ident = format!("Method Call: {}", path.ident.as_str()); - // let path: &'tcx PathSegment<'tcx> = p; - let maybe_method_def_id = self - .tcx - .typeck(ex.hir_id.owner.def_id) - .type_dependent_def_id(ex.hir_id); - if let Some(method_def_id) = maybe_method_def_id { - let _is_local = method_def_id.as_local().is_some(); - // if !is_local { - let defpath = self.tcx.def_path_debug_str(method_def_id); - println!("Found MethodCall: {}", defpath.clone()); - self.spans.push((defpath, sp)); - // } - // try something - let owner_def_id = ex.hir_id.owner.def_id; - let owner_def_path = self.tcx.def_path_debug_str(owner_def_id.into()); - // what if we give it e1's defid - let tyck_res = self.tcx.typeck(owner_def_id); - let substs = tyck_res.node_substs(ex.hir_id); - let (other_def_id, _subst) = self - .env_query - .resolve_method_call(owner_def_id, method_def_id, substs); - if other_def_id != method_def_id { - let other_def_path = self.tcx.def_path_debug_str(other_def_id); - println!("Resolved method call to different defpath: {}", other_def_path); - self.spans.push((other_def_path, sp)); + } + ExprKind::MethodCall(_path, _e1, _e2, sp) => { + let resolve_res = self.resolve_expression(expr); + match resolve_res { + Ok((method_def_id, resolved_def_id)) => { + let _is_local = method_def_id.as_local().is_some(); + let defpath_unresolved = self.tcx.def_path_debug_str(method_def_id); + let defpath_resolved = self.tcx.def_path_debug_str(resolved_def_id); + + if true { + // TODO: replace with is_local once we are not debugging anymore + // no need to create external specs for local methods + if defpath_unresolved == defpath_resolved { + self.spans.push((defpath_resolved, sp)); + } else { + // in this case we want both + self.spans.push((defpath_resolved, sp)); + self.spans.push((defpath_unresolved, sp)); + } + } } - } // local methods do not need external specifications. - // should we still allow it? - // the interesting thing about methodCalls is that in the case of traits - // there are 2 possible things we can annotate. The behavior of the trait - // methods itself, or the methods of this specific implementation + Err(()) => {} + } + } + ExprKind::Binary(..) | ExprKind::AssignOp(..) | ExprKind::Unary(..) => { + let resolve_res = self.resolve_expression(expr); + // this will already fail for standard addition + match resolve_res { + Ok((method_def_id, resolved_def_id)) => { + let _is_local = method_def_id.as_local().is_some(); + let defpath_unresolved = self.tcx.def_path_debug_str(method_def_id); + let defpath_resolved = self.tcx.def_path_debug_str(resolved_def_id); + + if true { + // TODO: replace with is_local once we are not debugging anymore + // no need to create external specs for local methods + if defpath_unresolved == defpath_resolved { + println!("Defpaths for binary operation were equal"); + self.spans.push((defpath_resolved, expr.span)); + } else { + // For binary operations this will be the operation + // from the standard libary and the "overriding" method + println!( + "\n\n\n\nFound two differing defpaths for binary operation" + ); + println!("1. {}", defpath_resolved); + println!("2. {}", defpath_unresolved); - }, - ExprKind::Binary(binop, _e1, _e2) | ExprKind::AssignOp(binop, _e1, _e2) => { - let ident = binop.node.as_str(); - self.spans.push((ident.to_string(), ex.span)); - }, - ExprKind::Unary(unop, _e1) => { - let ident = unop.as_str(); - self.spans.push((ident.to_string(), ex.span)); - }, + self.spans.push((defpath_resolved, expr.span)); + self.spans.push((defpath_unresolved, expr.span)); + } + } + } + Err(()) => {} // standard addition etc should be caught here + } + } _ => {} } } } - diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/ide_info.rs index 7b96f9f1ef1..7601813a53c 100644 --- a/prusti/src/ide_helper/ide_info.rs +++ b/prusti/src/ide_helper/ide_info.rs @@ -83,6 +83,8 @@ fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, Span)> { // let hir_body = l_hir.body(); let mut fnvisitor = call_finder::CallSpanFinder::new(env); + + // let mut adjusted_visitor = // fnvisitor.visit_body(hir_body); env.tcx() .hir() From 49432559d98e2ac9983238053625cc2a29bc236e Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Tue, 3 Jan 2023 22:49:06 +0100 Subject: [PATCH 13/74] initial quantifier instantiations working --- Cargo.lock | 1 + prusti-common/src/vir/to_viper.rs | 1 - .../src/environment/diagnostic.rs | 9 + prusti-interface/src/prusti_error.rs | 13 ++ prusti-server/src/process_verification.rs | 171 +++++++----------- prusti-viper/Cargo.toml | 1 + .../src/encoder/errors/position_manager.rs | 3 +- .../mir/pure/specifications/encoder_poly.rs | 3 +- prusti-viper/src/verifier.rs | 25 ++- 9 files changed, 111 insertions(+), 116 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54247444d3e..355b7e61a16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2135,6 +2135,7 @@ dependencies = [ "regex", "rustc-hash", "serde", + "serde_json", "viper", "vir", ] diff --git a/prusti-common/src/vir/to_viper.rs b/prusti-common/src/vir/to_viper.rs index a73f49d7bb2..979fa959431 100644 --- a/prusti-common/src/vir/to_viper.rs +++ b/prusti-common/src/vir/to_viper.rs @@ -660,7 +660,6 @@ impl<'v> ToViper<'v, viper::Expr<'v>> for Expr { pos.to_viper(context, ast), ), Expr::ForAll(ref vars, ref triggers, ref body, ref pos) => { - info!("FORALL pos: {:?}", pos); ast.forall_with_pos( &vars.to_viper_decl(context, ast)[..], &(triggers, pos).to_viper(context, ast), diff --git a/prusti-interface/src/environment/diagnostic.rs b/prusti-interface/src/environment/diagnostic.rs index e3e84075f9a..f9aab4f3ca2 100644 --- a/prusti-interface/src/environment/diagnostic.rs +++ b/prusti-interface/src/environment/diagnostic.rs @@ -78,6 +78,15 @@ impl<'tcx> EnvDiagnostic<'tcx> { diagnostic.buffer(&mut self.warn_buffer.borrow_mut()); } + /// Emits a note + pub fn span_note + Clone>( + &self, + sp: S, + msg: &str, + ) { + self.tcx.sess.span_note_without_error(sp, msg); + } + /// Returns true if an error has been emitted pub fn has_errors(&self) -> bool { self.tcx.sess.has_errors().is_some() diff --git a/prusti-interface/src/prusti_error.rs b/prusti-interface/src/prusti_error.rs index f3de993d9c0..ac0ea6b24b5 100644 --- a/prusti-interface/src/prusti_error.rs +++ b/prusti-interface/src/prusti_error.rs @@ -40,6 +40,7 @@ pub enum PrustiErrorKind { Warning, /// A warning which is only shown if at least one error is emitted. WarningOnError, + Message, } impl PartialOrd for PrustiError { @@ -146,6 +147,14 @@ impl PrustiError { error } + /// Report a message + pub fn message(message: S, span: MultiSpan) -> Self { + check_message(message.to_string()); + let mut msg = PrustiError::new(message.to_string(), span); + msg.kind = PrustiErrorKind::Message; + msg + } + /// Set that this Prusti error should be reported as a warning to the user pub fn set_warning(&mut self) { self.kind = PrustiErrorKind::Warning; @@ -203,6 +212,10 @@ impl PrustiError { &self.help, &self.notes, ), + PrustiErrorKind::Message => env_diagnostic.span_note( + *self.span, + &self.message, + ), }; } diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index c19c896b381..094670a8448 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -113,123 +113,78 @@ pub fn process_verification_request<'v, 't: 'v>( let mut verifier = new_viper_verifier(program_name, &verification_context, request.backend_config); - // get the reporter - let env = &verification_context.env(); - let jni = JniUtils::new(env); - let verifier_wrapper = silver::verifier::Verifier::with(env); - let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); - let rep_glob_ref = env.new_global_ref(reporter).unwrap(); - - let viper_arc_clone = Arc::clone(&viper_arc); - //let program_clone = request.program.clone(); + let mut result = VerificationResult::Success; + let mut q_insts = HashMap::<(u64, String), u64>::new(); let normalization_info_clone = normalization_info.clone(); - let (main_tx, thread_rx) = mpsc::channel(); - let (thread_tx, main_rx) = mpsc::channel(); // start thread for polling messages and print on receive - // TODO: maybe change to scope thread? - let polling_thread = thread::spawn(move || { - /*let functions = match program_clone { - program::Program::Legacy(p) => p.functions, - program::Program::Low(_) => panic!("Program::Low not yet supported!"), - };*/ - let verification_context = viper_arc_clone.attach_current_thread(); - let env = verification_context.env(); + // TODO: Detach warning + thread::scope(|s| { + // get the reporter + let env = &verification_context.env(); let jni = JniUtils::new(env); - let reporter_instance = rep_glob_ref.as_obj(); - let reporter_wrapper = silver::reporter::PollingReporter::with(env); - let mut done = false; - let mut q_insts = HashMap::<(u64, String), u64>::new(); - while !done { - while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { - let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); - //println!("{}", jni.class_name(msg).as_str()); - match jni.class_name(msg).as_str() { - /* - "viper.silver.reporter.EntitySuccessMessage" => { - let msg_wrapper = silver::reporter::EntitySuccessMessage::with(env); - let verification_time = jni.unwrap_result(msg_wrapper.call_verificationTime(msg)); - //let cached = jni.unwrap_result(msg_wrapper.call_cached(msg)); - //println!("Received EntitySuccessMessage with time: {}", verification_time); - let concerning = jni.unwrap_result(msg_wrapper.call_concerning(msg)); - let member_wrapper = silver::ast::Member::with(env); - //println!("Concerning: {}", jni.get_string(jni.unwrap_result(member_wrapper.call_name(concerning)))); - match jni.class_name(concerning).as_str() { - "viper.silver.ast.Function" => { - let name = jni.get_string(jni.unwrap_result(member_wrapper.call_name(concerning))); - for f in &functions { - if let Some(expr) = &f.body { - if f.name == name { - println!("SUCCESS: Function: {}, time: {}, body_pos: {:?}", name, verification_time, expr.pos()); - } - } - } - } - _ => () - } - } - "viper.silver.reporter.EntityFailureMessage" => { - let msg_wrapper = silver::reporter::EntityFailureMessage::with(env); - let verification_time = jni.unwrap_result(msg_wrapper.call_verificationTime(msg)); - //let cached = jni.unwrap_result(msg_wrapper.call_cached(msg)); - //println!("Received EntityFailureMessage with time: {}", verification_time); - let concerning = jni.unwrap_result(msg_wrapper.call_concerning(msg)); - let member_wrapper = silver::ast::Member::with(env); - //println!("Concerning: {}", jni.get_string(jni.unwrap_result(member_wrapper.call_name(concerning)))); - match jni.class_name(concerning).as_str() { - "viper.silver.ast.Function" => { - let name = jni.get_string(jni.unwrap_result(member_wrapper.call_name(concerning))); - for f in &functions { - if let Some(expr) = &f.body { - if f.name == name { - println!("FAILURE: Function: {}, time: {}, body_pos: {:?}", name, verification_time, expr.pos()); - } + let verifier_wrapper = silver::verifier::Verifier::with(env); + let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); + let rep_glob_ref = env.new_global_ref(reporter).unwrap(); + + let (main_tx, thread_rx) = mpsc::channel(); + let (thread_tx, main_rx) = mpsc::channel(); + let polling_thread = s.spawn(move || { + let verification_context = viper_arc.attach_current_thread(); + let env = verification_context.env(); + let jni = JniUtils::new(env); + let reporter_instance = rep_glob_ref.as_obj(); + let reporter_wrapper = silver::reporter::PollingReporter::with(env); + let mut done = false; + let mut q_insts = HashMap::<(u64, String), u64>::new(); + while !done { + while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { + let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); + match jni.class_name(msg).as_str() { + "viper.silver.reporter.QuantifierInstantiationsMessage" => { + let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); + let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); + let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); + // TODO: find out which more quantifiers are derived from the user + // quantifiers + info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); + // also matches the "-aux" quantifiers generated + // TODO: some positions have just the id 0 and cannot be denormalized... + if q_name.starts_with("quant_with_posID") { + let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); + let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); + let parsed = stripped.parse::(); + match parsed { + Ok(pos_id) => { + let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); + // insert also updates the entry + q_insts.insert((norm_pos_id, q_name), q_inst.try_into().unwrap()); + () } + _ => info!("Unexpected quantifier name {}", q_name) } } - _ => () - } - }*/ - "viper.silver.reporter.QuantifierInstantiationsMessage" => { - let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); - let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); - // also matches the "-aux" quantifiers generated - // problem: we want to keep the aux and non-aux version apart - if q_name.starts_with("quant_with_posID") { - let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); - let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); - let parsed = stripped.parse::(); - match parsed { - Ok(pos_id) => { - let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); - let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); - info!("QuantifierInstantiationsMessage: {} {} {} {}", q_name, q_inst, pos_id, norm_pos_id); - // insert also updates the entry - q_insts.insert((norm_pos_id, q_name), q_inst.try_into().unwrap()); - () - } - _ => info!("Unexpected quantifier name {}", q_name) - } } + _ => () } - _ => () + } + if !thread_rx.try_recv().is_err() { + info!("Received termination signal!"); + done = true; + } else { + thread::sleep(time::Duration::from_millis(10)); } } - if !thread_rx.try_recv().is_err() { - info!("Received termination signal!"); - done = true; - } else { - thread::sleep(time::Duration::from_millis(10)); - } - } - thread_tx.send(q_insts).unwrap(); - }); - stopwatch.start_next("verification"); - let mut result = verifier.verify(viper_program); + thread_tx.send(q_insts).unwrap(); + }); + stopwatch.start_next("verification"); + result = verifier.verify(viper_program); - main_tx.send(true).unwrap(); - let q_insts: HashMap<(u64, String), u64> = main_rx.recv().unwrap(); - polling_thread.join().unwrap(); + main_tx.send(true).unwrap(); + q_insts = main_rx.recv().unwrap(); + // FIXME: here the global ref is dropped from a detached thread + polling_thread.join().unwrap(); + }); // Don't cache Java exceptions, which might be due to misconfigured paths. if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { @@ -241,19 +196,21 @@ pub fn process_verification_request<'v, 't: 'v>( cache.insert(hash, result.clone()); } + // denormalized positioned id, name, total number of instantiations (because we overwrite + // each time in the polling thread) normalization_info.denormalize_result(&mut result); let enriched_result = match result { VerificationResult::Success => { info!("Enriching result"); for ((pos_id, q_name), n) in q_insts.iter() { - info!("({} {}) {}", pos_id, q_name, n); + info!("Enriching with {} {}: {}", q_name, pos_id, n); } VerificationResult::EnrichedSuccess(q_insts) }, VerificationResult::Failure(errors)=> { info!("Enriching result"); for ((pos_id, q_name), n) in q_insts.iter() { - info!("({} {}) {}", pos_id, q_name, n); + info!("Enriching with {} {}: {}", q_name, pos_id, n); } VerificationResult::EnrichedFailure(errors, q_insts) }, diff --git a/prusti-viper/Cargo.toml b/prusti-viper/Cargo.toml index cf61d95407d..588775c4ca1 100644 --- a/prusti-viper/Cargo.toml +++ b/prusti-viper/Cargo.toml @@ -21,6 +21,7 @@ vir-crate = { package = "vir", path = "../vir" } num-traits = "0.2" regex = "1.5" serde = "1.0" +serde_json = "1.0" backtrace = "0.3" rustc-hash = "1.1.0" derive_more = "0.99.16" diff --git a/prusti-viper/src/encoder/errors/position_manager.rs b/prusti-viper/src/encoder/errors/position_manager.rs index ae9a4b71764..419b77329e1 100644 --- a/prusti-viper/src/encoder/errors/position_manager.rs +++ b/prusti-viper/src/encoder/errors/position_manager.rs @@ -8,7 +8,7 @@ use vir_crate::polymorphic::Position; use rustc_hash::FxHashMap; use prusti_rustc_interface::span::source_map::SourceMap; use prusti_rustc_interface::errors::MultiSpan; -use log::{debug, trace}; +use log::debug; use prusti_interface::data::ProcedureDefId; /// Mapping from VIR positions to the source code that generated them. @@ -39,7 +39,6 @@ impl<'tcx> PositionManager<'tcx> let span = span.into(); let pos_id = self.next_pos_id; self.next_pos_id += 1; - trace!("Register position id {} for span {:?} in {:?}, ", pos_id, span, def_id); let pos = if let Some(primary_span) = span.primary_span() { let lines_info_res = self diff --git a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs index c39660f2d77..c43cd405cb8 100644 --- a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs +++ b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs @@ -24,7 +24,6 @@ use prusti_rustc_interface::{ }; use rustc_hash::FxHashSet; use vir_crate::polymorphic::ExprIterator; -use log::info; // TODO: this variant (poly) should not need to exist, eventually should be // replaced by the high variant + lowering @@ -233,8 +232,8 @@ pub(super) fn encode_quantifier<'tcx>( body_substs, )?; + // FIXME: we get the wrong span here let pos = encoder.error_manager().register_span(parent_def_id, span); - info!("Encoding quantifier with span '{:?}' and pos '{:?}, parent_def_id '{:?}'", span, pos, parent_def_id); // replace qvars with a nicer name based on quantifier depth to ensure that // quantifiers remain stable for caching diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 645899250fe..7b0322eaca8 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -30,6 +30,7 @@ use prusti_rustc_interface::span::DUMMY_SP; use prusti_server::tokio::runtime::Builder; use std::sync::Arc; use std::collections::HashMap; +use serde_json::json; // /// A verifier builder is an object that lives entire program's // /// lifetime, has no mutable state, and is responsible for constructing @@ -283,24 +284,31 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { let mut java_exceptions : Vec<_> = vec![]; // the quantifier instantiations among different programs are independent and thus are // aggregated + // (denormalized ID, name (with normalized ID)), num_instantiations for each file let mut quantifier_instantiations: HashMap::<(u64, String), u64> = HashMap::new(); for (method_name, result) in verification_results.into_iter() { match result { - viper::VerificationResult::Success => {} + viper::VerificationResult::Success => { + info!("Result: Normal Success"); + } viper::VerificationResult::ConsistencyErrors(errors) => { + info!("Result: ConsistencyErrors"); for error in errors.into_iter() { consistency_errors.push((method_name.clone(), error)); } } viper::VerificationResult::Failure(errors) => { + info!("Result: Failure"); for error in errors.into_iter() { verification_errors.push((method_name.clone(), error)); } } viper::VerificationResult::JavaException(exception) => { + info!("Result: JavaException"); java_exceptions.push((method_name, exception)); } viper::VerificationResult::EnrichedSuccess(q_insts) => { + info!("Result: EnrichedSuccess"); for (key, n) in q_insts { match quantifier_instantiations.get(&key) { Some(before) => { @@ -315,6 +323,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } } viper::VerificationResult::EnrichedFailure(errors, q_insts) => { + info!("Result: EnrichedFailure"); for (key, n) in q_insts { match quantifier_instantiations.get(&key) { Some(before) => { @@ -337,11 +346,19 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { let error_manager = self.encoder.error_manager(); // report the quantifier instantiations - info!("Reporting qi_profile"); if config::report_qi_profile() { + info!("Reporting qi_profile"); for ((pos_id, q_name), n) in quantifier_instantiations { - let span = error_manager.position_manager().get_span_from_id(pos_id); - info!("{} {} {} {:?}", q_name, n, pos_id, span); + match error_manager.position_manager().get_span_from_id(pos_id) { + Some(span) => { + PrustiError::message( + format!("quantifier_instantiations_message{}", + json!({"q_name": q_name, "instantiations": n}), + ), span.clone() + ).emit(&self.env.diagnostic); + }, + None => info!("Quantifier instantiations for unknown position id {} {} {}", q_name, n, pos_id), + } } } From 0bd03b93e3ef1e48c46dfde60574d1b73fbfdb16 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 5 Jan 2023 13:51:24 +0100 Subject: [PATCH 14/74] generating signatures correctly in some cases, struggling with traitbounds --- prusti/src/ide_helper/call_finder.rs | 18 +-- prusti/src/ide_helper/ide_info.rs | 46 +++--- prusti/src/ide_helper/mod.rs | 3 +- prusti/src/ide_helper/query_signature.rs | 192 +++++++++++++++++++++++ 4 files changed, 231 insertions(+), 28 deletions(-) create mode 100644 prusti/src/ide_helper/query_signature.rs diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 6bb6f7a4c79..3ad3a73535e 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -12,14 +12,14 @@ use prusti_rustc_interface::{ pub struct CallSpanFinder<'tcx> { pub env_query: EnvQuery<'tcx>, pub tcx: TyCtxt<'tcx>, - pub spans: Vec<(String, Span)>, + pub called_functions: Vec<(String, DefId, Span)>, } impl<'tcx> CallSpanFinder<'tcx> { pub fn new(env: &Environment<'tcx>) -> Self { Self { env_query: env.query, - spans: Vec::new(), + called_functions: Vec::new(), tcx: env.tcx(), } } @@ -61,7 +61,7 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { if let prusti_rustc_interface::hir::def::Res::Def(_, def_id) = res { let defpath = self.tcx.def_path_debug_str(def_id); println!("Call DefPath: {}", defpath); - self.spans.push((defpath, expr.span)) + self.called_functions.push((defpath, def_id, expr.span)) } else { println!("Resolving a call failed!\n\n\n"); } @@ -81,11 +81,11 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { // TODO: replace with is_local once we are not debugging anymore // no need to create external specs for local methods if defpath_unresolved == defpath_resolved { - self.spans.push((defpath_resolved, sp)); + self.called_functions.push((defpath_resolved, resolved_def_id, sp)); } else { // in this case we want both - self.spans.push((defpath_resolved, sp)); - self.spans.push((defpath_unresolved, sp)); + self.called_functions.push((defpath_resolved, resolved_def_id, sp)); + self.called_functions.push((defpath_unresolved, method_def_id, sp)); } } } @@ -106,7 +106,7 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { // no need to create external specs for local methods if defpath_unresolved == defpath_resolved { println!("Defpaths for binary operation were equal"); - self.spans.push((defpath_resolved, expr.span)); + self.called_functions.push((defpath_resolved, resolved_def_id, expr.span)); } else { // For binary operations this will be the operation // from the standard libary and the "overriding" method @@ -116,8 +116,8 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { println!("1. {}", defpath_resolved); println!("2. {}", defpath_unresolved); - self.spans.push((defpath_resolved, expr.span)); - self.spans.push((defpath_unresolved, expr.span)); + self.called_functions.push((defpath_resolved, resolved_def_id,expr.span)); + self.called_functions.push((defpath_unresolved, method_def_id, expr.span)); } } } diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/ide_info.rs index 7601813a53c..ca7c7f33c98 100644 --- a/prusti/src/ide_helper/ide_info.rs +++ b/prusti/src/ide_helper/ide_info.rs @@ -1,13 +1,11 @@ -use prusti_common::config; use prusti_interface::environment::Environment; - use prusti_rustc_interface::{ hir::def_id::DefId, span::{source_map::SourceMap, Span}, }; - use super::call_finder; -use serde::Serialize; +use super::query_signature; +use serde::{Serialize, ser::SerializeStruct}; // create some struct storing all the information the IDE will ever need. // needs to be transformable into json! @@ -16,35 +14,52 @@ use serde::Serialize; pub struct IdeInfo { procedure_defs: Vec, function_calls: Vec, + queried_source: Option, // additionally this will contain: // function_calls: // ... we'll see + } impl IdeInfo { pub fn collect(env: &Environment<'_>, procedures: &Vec) -> Self { let procs = collect_procedures(env, procedures); let source_map = env.tcx().sess.source_map(); - let fncalls = collect_fncalls(env) + let fncalls: Vec = collect_fncalls(env) .into_iter() - .map(|(name, sp)| ProcDef { + .map(|(name, defid, sp)| ProcDef { name, + defid, span: VscSpan::from_span(sp, source_map).unwrap(), }) .collect(); + // For declaring external specifications: - let _queried_source = collect_queried_signatures(env); + let queried_source = query_signature::collect_queried_signatures(env, &fncalls); Self { procedure_defs: procs, function_calls: fncalls, + queried_source, } } } -#[derive(Serialize)] pub struct ProcDef { - name: String, - span: VscSpan, + pub name: String, + pub defid: DefId, + pub span: VscSpan, +} + +// defid is not needed for VSCode and not serializable as of now.. +impl Serialize for ProcDef { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer { + let mut state = serializer.serialize_struct("ProcDef", 2)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("span", &self.span)?; + state.end() + } } /// a representation of spans that is more usable with VSCode. @@ -72,13 +87,14 @@ fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec) -> Vec<(String, Span)> { +fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, DefId, Span)> { // let l_hir = env.tcx().hir(); // let hir_body = l_hir.body(); @@ -90,13 +106,7 @@ fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, Span)> { .hir() .visit_all_item_likes_in_crate(&mut fnvisitor); - return fnvisitor.spans; -} - -fn collect_queried_signatures(env: &Environment<'_>) -> Option { - let def_path_str: String = config::query_method_signature()?; - - return Some(String::from("hello")) + return fnvisitor.called_functions; } impl VscSpan { diff --git a/prusti/src/ide_helper/mod.rs b/prusti/src/ide_helper/mod.rs index 3812bc14aeb..d3e95a45c7e 100644 --- a/prusti/src/ide_helper/mod.rs +++ b/prusti/src/ide_helper/mod.rs @@ -1,3 +1,4 @@ pub mod ide_info; -pub mod call_finder; +mod call_finder; pub mod fake_error; +mod query_signature; diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs new file mode 100644 index 00000000000..1e8daf03777 --- /dev/null +++ b/prusti/src/ide_helper/query_signature.rs @@ -0,0 +1,192 @@ +use prusti_common::config; +use prusti_interface::environment::Environment; +use std::collections::HashMap; + +use prusti_rustc_interface::{ + hir::{ + def::DefKind, + def_id::DefId, + }, + middle::ty::{ + TraitRef, + DefIdTree, + PredicateKind, + TyCtxt, + ImplSubject, + Generics, + }, +}; +use super::ide_info::ProcDef; + + +pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) -> Option { + let def_path_str: String = config::query_method_signature()?; + println!("\n\n\n\nCollecting Information for Queried Method"); + println!("Got query for external specification template for : {}", def_path_str); + let existing = fncalls.iter().find(|procdef| procdef.name == def_path_str)?; + + // helpers: + let tcx = env.tcx(); + let defid: DefId = existing.defid; + + // check what kind of definition we are looking at: + + let def_kind = tcx.opt_def_kind(defid)?; + match def_kind { + DefKind::AssocFn => { + let opt_item_name = tcx.opt_item_name(defid)?; + let item_name = opt_item_name.as_str(); + println!("Item Name: {}", item_name); + let impl_of_method_opt = tcx.impl_of_method(defid); + match impl_of_method_opt { + Some(impl_of_method) => { + println!("Impl of method : {:?}", impl_of_method); + let mut trait_name = None; + let impl_subj = tcx.impl_subject(impl_of_method); + let self_ty = match impl_subj { + ImplSubject::Trait(tref) => { + trait_name = Some(format!("{}", tref.print_only_trait_name())); + tref.self_ty() + }, + ImplSubject::Inherent(ty) => { + ty + } + }; + println!("Self Type: {:?}", self_ty); + // apprently this is dangerous, is it in our case? + let signature = tcx.fn_sig(defid).skip_binder(); + println!("Signature: {:?}", signature); + + let inputs = signature.inputs().iter(); + let output = signature.output(); + + // Arg Names? We could choose them ourselves if we wanted to.. + let arg_names = tcx.fn_arg_names(defid); + let nr_args = arg_names.len(); + + // let generics = tcx.generics_of(defid); + + + let impl_generics_str = generic_params_str(impl_of_method, &tcx); + + + // Generate result: + let mut res = "#[extern_spec]\nimpl".to_string(); + res.push_str(&format!("{} ", impl_generics_str)); + if let Some(traitname) = trait_name { + res.push_str(&traitname); + res.push_str(" for "); + } + res.push_str(&format!("{} {{\n\tpub fn {}(", self_ty, item_name)); + for (id,(name, ty)) in arg_names.iter().zip(inputs).enumerate() { + let argument = format!("{}: {}", name.as_str(), ty); + res.push_str(&argument); + if id < nr_args - 1 { + res.push_str(", "); + } + } + res.push_str(&format!(") -> {};\n}}", output)); + + println!("Result: {}", res); + + Some(res) + } + None => { + // we are apparently dealing with a trait, or even something else + None + } + } + } + // as far as I can tell this is all that's supported for extern_spec + _ => { + println!("Apparently not an associated function to some impl block.."); + None + } + } +} + +pub fn generic_params_str(defid: DefId, tcx: &TyCtxt<'_>) -> String { + // should receive defid of either an impl block or a function definition. + // should return a string of the form <'a, S, T> depending on generic parameters. + let generic_params = tcx.generics_of(defid); + let traitref = tcx.predicates_of(defid); + println!("Traitref: {:?}", traitref); + + let mut traitbounds: HashMap = HashMap::new(); + + + for (predicate, _) in traitref.predicates { + println!("Found predicate of kind: {:?}", predicate.kind()); + let kind: PredicateKind = predicate.kind().skip_binder(); // i don't understand this... + match kind { + PredicateKind::Trait(t) => { + println!("PredicateKind::Trait(t) with t: {:?}", t); + let traitref = t.trait_ref; + let self_ty = format!("{}", traitref.self_ty()); + let traitname = format!("{}", traitref.print_only_trait_name()); + println!("self type: {self_ty:?}, traitname: {:?}", traitname); + + let typelist_opt = traitref.substs.try_as_type_list(); + let trait_args = if let Some(typelist) = typelist_opt { + if typelist.len() > 1 { + let mut trait_args: Vec = vec![]; + for i in 1..typelist.len() { + // the first one is self + trait_args.push(format!("{}",typelist[i])); + } + format!("<{}>", trait_args.join(", ")) + } else { + "".to_string() + } + } else { + // throw a warning? I guess this should never happen.. + "".to_string() + }; + let result = format!("{}{}", traitname, trait_args); + match traitbounds.get(&self_ty) { + None => { traitbounds.insert(self_ty, result); }, + Some(previous_traits) => { + let to_append = format!("{previous_traits} + {result}"); + traitbounds.insert(self_ty, to_append); + }, + } + } + PredicateKind::Projection(p) => { + let id = p.projection_ty.item_def_id; + let parent = tcx.parent(id); // I want the identifier e.g. std::ops::Add + let parent_traitref: TraitRef = TraitRef::identity(*tcx, parent).skip_binder(); + let parent_identifier = format!("{}", parent_traitref.print_only_trait_name()); + println!("Parent name: {}", parent_identifier); + let opt_proj_pred = predicate.to_opt_poly_projection_pred().unwrap(); + + + println!("PredicateKind::Projection(p) with p: {:?}", p); + println!("Item Name: {}", tcx.opt_item_ident(id).unwrap()); + println!("Trait Def: {}", tcx.opt_item_ident(opt_proj_pred.trait_def_id(*tcx)).unwrap()); + } + _ => { + println!("Encountered PredicateKind {:?} but we dont handle it", kind); + } + } + + } + + if generic_params.count() > 0 { + + let param_str = generic_params.params + .iter().map(|param| param.name.as_str()) + .map(|param| if let Some(traitbound) = traitbounds.get(param) { + format!("{param}: {traitbound}") + } else { + format!("{param}") + }) + .collect::>() + .join(", "); + + println!("Generics Result: {}", param_str); + format!("<{}>", param_str) + } else { + "".to_string() + } +} + From fe577dcaf630ed191be7f66e00c49d95785e491b Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 5 Jan 2023 18:56:11 +0100 Subject: [PATCH 15/74] successfully resolving projections (it seems) --- prusti/src/ide_helper/query_signature.rs | 191 +++++++++++++---------- 1 file changed, 111 insertions(+), 80 deletions(-) diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 1e8daf03777..f9724865d47 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -1,31 +1,26 @@ use prusti_common::config; use prusti_interface::environment::Environment; +use core::fmt; use std::collections::HashMap; +use super::ide_info::ProcDef; use prusti_rustc_interface::{ - hir::{ - def::DefKind, - def_id::DefId, - }, - middle::ty::{ - TraitRef, - DefIdTree, - PredicateKind, - TyCtxt, - ImplSubject, - Generics, - }, + hir::{def::DefKind, def_id::DefId}, + middle::ty::{DefIdTree, Generics, ImplSubject, PredicateKind, TraitRef, Ty, TyCtxt}, }; -use super::ide_info::ProcDef; - pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) -> Option { let def_path_str: String = config::query_method_signature()?; println!("\n\n\n\nCollecting Information for Queried Method"); - println!("Got query for external specification template for : {}", def_path_str); - let existing = fncalls.iter().find(|procdef| procdef.name == def_path_str)?; - - // helpers: + println!( + "Got query for external specification template for : {}", + def_path_str + ); + let existing = fncalls + .iter() + .find(|procdef| procdef.name == def_path_str)?; + + // helpers: let tcx = env.tcx(); let defid: DefId = existing.defid; @@ -47,10 +42,8 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) ImplSubject::Trait(tref) => { trait_name = Some(format!("{}", tref.print_only_trait_name())); tref.self_ty() - }, - ImplSubject::Inherent(ty) => { - ty } + ImplSubject::Inherent(ty) => ty, }; println!("Self Type: {:?}", self_ty); // apprently this is dangerous, is it in our case? @@ -59,16 +52,15 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) let inputs = signature.inputs().iter(); let output = signature.output(); - + // Arg Names? We could choose them ourselves if we wanted to.. let arg_names = tcx.fn_arg_names(defid); let nr_args = arg_names.len(); // let generics = tcx.generics_of(defid); - - - let impl_generics_str = generic_params_str(impl_of_method, &tcx); + let (impl_generics_str, _opt_where) = generic_params_str(impl_of_method, &tcx, false); + let (fn_generics_str, _opt_where) = generic_params_str(defid, &tcx, false); // Generate result: let mut res = "#[extern_spec]\nimpl".to_string(); @@ -77,8 +69,8 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) res.push_str(&traitname); res.push_str(" for "); } - res.push_str(&format!("{} {{\n\tpub fn {}(", self_ty, item_name)); - for (id,(name, ty)) in arg_names.iter().zip(inputs).enumerate() { + res.push_str(&format!("{} {{\n\tpub fn {}{}(", self_ty, item_name, fn_generics_str)); + for (id, (name, ty)) in arg_names.iter().zip(inputs).enumerate() { let argument = format!("{}: {}", name.as_str(), ty); res.push_str(&argument); if id < nr_args - 1 { @@ -105,16 +97,41 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) } } -pub fn generic_params_str(defid: DefId, tcx: &TyCtxt<'_>) -> String { +enum GenArguments<'a> { + GenType(Ty<'a>), + Projection(String, String), +} + +impl<'a> fmt::Display for GenArguments<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::GenType(ty) => { + write!(f, "{}", ty) + }, + Self::Projection(item, value) => { + write!(f, "{}={}", item, value) + } + } + } +} + +pub fn generic_params_str( + defid: DefId, // defid of a function or impl block + tcx: &TyCtxt<'_>, + exclude_bounds_to_where: bool, // whether or not trait bounds should be + // moved into an extra "where" block +) -> (String, Option) { // should receive defid of either an impl block or a function definition. // should return a string of the form <'a, S, T> depending on generic parameters. let generic_params = tcx.generics_of(defid); let traitref = tcx.predicates_of(defid); println!("Traitref: {:?}", traitref); - let mut traitbounds: HashMap = HashMap::new(); - + let mut traitbounds: HashMap>> = HashMap::new(); + // an example entry: impl + Trait2<..>> + // (or what it represents) + let mut projections: Vec<(String, DefId, DefId, String)> = vec![]; for (predicate, _) in traitref.predicates { println!("Found predicate of kind: {:?}", predicate.kind()); let kind: PredicateKind = predicate.kind().skip_binder(); // i don't understand this... @@ -122,71 +139,85 @@ pub fn generic_params_str(defid: DefId, tcx: &TyCtxt<'_>) -> String { PredicateKind::Trait(t) => { println!("PredicateKind::Trait(t) with t: {:?}", t); let traitref = t.trait_ref; - let self_ty = format!("{}", traitref.self_ty()); - let traitname = format!("{}", traitref.print_only_trait_name()); - println!("self type: {self_ty:?}, traitname: {:?}", traitname); - - let typelist_opt = traitref.substs.try_as_type_list(); - let trait_args = if let Some(typelist) = typelist_opt { - if typelist.len() > 1 { - let mut trait_args: Vec = vec![]; + let self_ty_str = format!("{}", traitref.self_ty()); + + let trait_args_opt = traitref.substs.try_as_type_list(); + let trait_args: Vec = vec![]; + if let Some(typelist) = trait_args_opt { + if trait_args.len() > 1 { + let mut trait_args: Vec = vec![]; for i in 1..typelist.len() { // the first one is self - trait_args.push(format!("{}",typelist[i])); + trait_args.push(GenArguments::GenType(typelist[i])); } - format!("<{}>", trait_args.join(", ")) - } else { - "".to_string() } - } else { - // throw a warning? I guess this should never happen.. - "".to_string() - }; - let result = format!("{}{}", traitname, trait_args); - match traitbounds.get(&self_ty) { - None => { traitbounds.insert(self_ty, result); }, - Some(previous_traits) => { - let to_append = format!("{previous_traits} + {result}"); - traitbounds.insert(self_ty, to_append); - }, + } + match traitbounds.get_mut(&self_ty_str) { + None => { + traitbounds.insert(self_ty_str, HashMap::from([(traitref.def_id, trait_args)])); + } + Some(trait_vec) => { + trait_vec.insert(traitref.def_id, trait_args); + } } } PredicateKind::Projection(p) => { - let id = p.projection_ty.item_def_id; - let parent = tcx.parent(id); // I want the identifier e.g. std::ops::Add - let parent_traitref: TraitRef = TraitRef::identity(*tcx, parent).skip_binder(); - let parent_identifier = format!("{}", parent_traitref.print_only_trait_name()); - println!("Parent name: {}", parent_identifier); - let opt_proj_pred = predicate.to_opt_poly_projection_pred().unwrap(); - - - println!("PredicateKind::Projection(p) with p: {:?}", p); - println!("Item Name: {}", tcx.opt_item_ident(id).unwrap()); - println!("Trait Def: {}", tcx.opt_item_ident(opt_proj_pred.trait_def_id(*tcx)).unwrap()); + // for example, for impl>, + // - item_id identifies Output, + // - trait_defid: Trait + // - term: the type assigned to Ouptut + // - self_ty: The first T + // (not very sure about the last 2) + let item_id = p.projection_ty.item_def_id; + let self_ty_str = format!("{}", p.projection_ty.self_ty()); + + let trait_defid: DefId = p.projection_ty.trait_def_id(*tcx); // I want the identifier e.g. std::ops::Add + let term = format!("{}", p.term); // the value to be assigned as default value + projections.push((self_ty_str, trait_defid, item_id, term)); } _ => { println!("Encountered PredicateKind {:?} but we dont handle it", kind); } } - } - if generic_params.count() > 0 { - - let param_str = generic_params.params - .iter().map(|param| param.name.as_str()) - .map(|param| if let Some(traitbound) = traitbounds.get(param) { - format!("{param}: {traitbound}") + // Map the Projections into the other data-structure: + for (gen_ty_str, trait_id, item_id, value) in projections { + let bounds_list = traitbounds.get_mut(&gen_ty_str).unwrap(); + let gen_params_list = bounds_list.get_mut(&trait_id).unwrap(); + let item_name = format!("{}", tcx.item_name(item_id)); + gen_params_list.push(GenArguments::Projection(item_name, value)); + } + // Stringify + let count = generic_params.params.iter().len(); + // there is a field generic_params.count() but apparently it does not + // give us the same value, not sure what it stands for + if count > 0 { + let result = generic_params.params // iterate through all parameters + .iter() + .map(|param_ident| { + let param = param_ident.name.as_str(); + let traitboundmap_opt = traitbounds.get(param); + if let Some(traitboundmap) = traitboundmap_opt { + let trait_list_str = traitboundmap.iter().map(|(trait_id, param_list)| { + let trait_str = tcx.def_path_str(*trait_id); + if param_list.len() <= 0 { + trait_str + } else { + let param_str = param_list.iter() + .map(|gp| format!("{}", gp)) + .collect::>().join(", "); + format!("{trait_str}<{param_str}>") + } + }).collect::>().join(" + "); + format!("{param}:{trait_list_str}") } else { - format!("{param}") - }) - .collect::>() - .join(", "); - - println!("Generics Result: {}", param_str); - format!("<{}>", param_str) + param.to_string() + } + }).collect::>().join(", "); + (format!("<{result}>"), None) + } else { - "".to_string() + ("".to_string(), None) } } - From dedc17b4d8504d7bbf7e61fd5a81bdf7a7a3d852 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Fri, 6 Jan 2023 02:40:00 +0100 Subject: [PATCH 16/74] started on generating external specs for traits --- prusti/src/ide_helper/query_signature.rs | 98 ++++++++++++++++++------ 1 file changed, 74 insertions(+), 24 deletions(-) diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index f9724865d47..90f426d1ac9 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -27,7 +27,13 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) // check what kind of definition we are looking at: let def_kind = tcx.opt_def_kind(defid)?; + println!("DefKind: {:?}", def_kind); match def_kind { + DefKind::Fn => { + // TODO + println!("Still a todo :)"); + None + }, DefKind::AssocFn => { let opt_item_name = tcx.opt_item_name(defid)?; let item_name = opt_item_name.as_str(); @@ -46,38 +52,29 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) ImplSubject::Inherent(ty) => ty, }; println!("Self Type: {:?}", self_ty); - // apprently this is dangerous, is it in our case? - let signature = tcx.fn_sig(defid).skip_binder(); - println!("Signature: {:?}", signature); - - let inputs = signature.inputs().iter(); - let output = signature.output(); - - // Arg Names? We could choose them ourselves if we wanted to.. - let arg_names = tcx.fn_arg_names(defid); - let nr_args = arg_names.len(); - - // let generics = tcx.generics_of(defid); + let signature_str = fn_signature_str(defid, &tcx); let (impl_generics_str, _opt_where) = generic_params_str(impl_of_method, &tcx, false); let (fn_generics_str, _opt_where) = generic_params_str(defid, &tcx, false); // Generate result: let mut res = "#[extern_spec]\nimpl".to_string(); res.push_str(&format!("{} ", impl_generics_str)); + // if impl block is implementing a trait: if let Some(traitname) = trait_name { res.push_str(&traitname); res.push_str(" for "); } - res.push_str(&format!("{} {{\n\tpub fn {}{}(", self_ty, item_name, fn_generics_str)); - for (id, (name, ty)) in arg_names.iter().zip(inputs).enumerate() { - let argument = format!("{}: {}", name.as_str(), ty); - res.push_str(&argument); - if id < nr_args - 1 { - res.push_str(", "); - } - } - res.push_str(&format!(") -> {};\n}}", output)); + //pub fn name(arguments) + res.push_str( + &format!( + "{} {{\n\tpub fn {}{}{};\n}}", + self_ty, + item_name, + fn_generics_str, + signature_str, + ) + ); println!("Result: {}", res); @@ -85,7 +82,28 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) } None => { // we are apparently dealing with a trait, or even something else - None + // This part is not working at all so far.. + let parent = tcx.opt_parent(defid)?; + + if tcx.is_trait(parent) { + println!("Yes indeed this is a trait"); + let fn_name = tcx.def_path_str(defid); + let traitname = tcx.def_path_str(parent); + let trait_params = trait_params(parent, &tcx); + let (fn_generics, _where) = generic_params_str(defid, &tcx, false); + let fn_sig = fn_signature_str(defid, &tcx); + Some(format!( + "#[extern_spec]\ntrait {}{} {{\n\tfn {}{}{};\n}}", + traitname, + trait_params, + fn_name, + fn_generics, + fn_sig, + )) + } else { + println!("Nope it's not actually a trait, check this case"); + None + } } } } @@ -97,6 +115,39 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) } } + +// by signature we mean the arguments + return type +pub fn fn_signature_str( + defid: DefId, + tcx: &TyCtxt<'_>, +) -> String { + let signature = tcx.fn_sig(defid).skip_binder(); + let inputs = signature.inputs().iter(); + let arg_names = tcx.fn_arg_names(defid); + + let output = signature.output(); + + let args = arg_names.iter().zip(inputs) + .map(|(name, ty)| format!("{}: {}", name.as_str(), ty)) + .collect::>() + .join(", "); + format!("({}) -> {}", args, output) +} + +pub fn trait_params(defid: DefId, tcx: &TyCtxt<'_>) -> String { + let generic_params = tcx.generics_of(defid); + let result = generic_params.params.iter() + .filter(|p| p.has_default()) // types like Self will show up here,but can't be in code + .map(|p| format!("{}={:?}", p.name, p.default_value(*tcx).unwrap())) + .collect::>().join(", "); + if generic_params.params.iter().len() > 0 { + format!("<{}>", result) + } else { + "".to_string() + } + +} + enum GenArguments<'a> { GenType(Ty<'a>), Projection(String, String), @@ -114,7 +165,6 @@ impl<'a> fmt::Display for GenArguments<'a> { } } } - pub fn generic_params_str( defid: DefId, // defid of a function or impl block tcx: &TyCtxt<'_>, @@ -210,7 +260,7 @@ pub fn generic_params_str( format!("{trait_str}<{param_str}>") } }).collect::>().join(" + "); - format!("{param}:{trait_list_str}") + format!("{param}: {trait_list_str}") } else { param.to_string() } From 8812f3d30d5b62ce2c92e8dc8ee08153a26df688 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sun, 8 Jan 2023 23:21:25 +0100 Subject: [PATCH 17/74] WIP: stream messages from server to client --- prusti-server/Cargo.toml | 5 +- prusti-server/src/client.rs | 63 +++-- prusti-server/src/process_verification.rs | 310 ++++++++++------------ prusti-server/src/server.rs | 67 +++-- prusti-server/src/server_message.rs | 13 + prusti-viper/src/verifier.rs | 146 +++++----- viper/src/verification_result.rs | 4 - 7 files changed, 317 insertions(+), 291 deletions(-) create mode 100644 prusti-server/src/server_message.rs diff --git a/prusti-server/Cargo.toml b/prusti-server/Cargo.toml index db7174a0a76..1d6b2b53cb5 100644 --- a/prusti-server/Cargo.toml +++ b/prusti-server/Cargo.toml @@ -27,10 +27,13 @@ bincode = "1.0" url = "2.2.2" num_cpus = "1.8.0" serde = { version = "1.0", features = ["derive"] } -reqwest = { version = "0.11", features = ["json"] } warp = "0.3" tokio = "1.20" jni = { version = "0.19", features = ["invocation"] } +# the tungstenite version used by warp 0.3 +tokio_tungstenite = "0.13" +async_stream = "0.3.3" +futures_util = "0.3.25" [dev-dependencies] lazy_static = "1.4.0" diff --git a/prusti-server/src/client.rs b/prusti-server/src/client.rs index a502d597a53..ca5363dd4b2 100644 --- a/prusti-server/src/client.rs +++ b/prusti-server/src/client.rs @@ -6,23 +6,28 @@ use crate::VerificationRequest; use prusti_common::config; -use reqwest::Client; use url::{ParseError, Url}; use viper::VerificationResult; +use tokio_tungstenite::{client::connect_async, + tungstenite::{Message, + handshake::client::Response, + error::Error} +}; +use async_stream::stream; +use futures_core::stream::Stream; +use futures_util::{FutureExt, stream::StreamExt, sink::SinkExt, pin_mut}; pub struct PrustiClient { - client: Client, server_url: Url, } impl PrustiClient { pub fn new(server_address: S) -> Result { let mut address = server_address.to_string(); - if !address.starts_with("http") { - address = format!("http://{}", address); + if !address.starts_with("ws") { + address = format!("ws://{}", address); } Ok(Self { - client: Client::new(), server_url: Url::parse(address.as_str())?, }) } @@ -30,32 +35,34 @@ impl PrustiClient { pub async fn verify( &self, request: VerificationRequest, - ) -> reqwest::Result { + ) -> Result, Error> { let use_json = config::json_communication(); - let base = self.client.post( - self.server_url + let uri = self.server_url .join(if use_json { "json/" } else { "bincode/" }) .unwrap() .join("verify/") - .unwrap(), - ); - let response = if use_json { - base.json(&request) - .send() - .await? - .error_for_status()? - .json() - .await? - } else { - let bytes = base - .body(bincode::serialize(&request).expect("error encoding verification request")) - .send() - .await? - .error_for_status()? - .bytes() - .await?; - bincode::deserialize(&bytes).expect("error decoding verification result") - }; - Ok(response) + .unwrap(); + let (socket, _) = connect_async(uri).await?; + let msg = if use_json Message::text(serde_json::to_string(request).expect("error encoding verification request in json")) + else Message::binary(bincode::serialize(&request).expect("error encoding verification request as binary")); + pin_mut!(socket); + socket.send(msg).await?; + // TODO: do we have to care about close messages? + let json_map = |ws_msg| { + if let Message::Text(json) = ws_msg { + serde_json::from_string(&json).expect("error decoding verification result from json") + } else { + panic!("Invalid response from the server."); + } + }); + let bin_map = |ws_msg| { + if let Message::Binary(bytes) = ws_msg { + bincode::deserialize(&bytes).expect("error decoding verification result") + } else { + panic!("Invalid response from the server."); + } + }); + let s = socket.map(if use_json json_map else bin_map); + Ok(s) } } diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index 094670a8448..fff4e3d9108 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::{VerificationRequest, ViperBackendConfig, jni_utils::JniUtils}; +use crate::{VerificationRequest, ViperBackendConfig, jni_utils::JniUtils, ServerMessage}; use log::info; use prusti_common::{ config, @@ -19,205 +19,185 @@ use viper::{ use viper_sys::wrappers::viper::*; use std::time; use std::sync::mpsc; +use async_stream::stream; pub fn process_verification_request<'v, 't: 'v>( viper_arc: &Arc, mut request: VerificationRequest, cache: impl Cache, -) -> viper::VerificationResult { - let stopwatch = Stopwatch::start("prusti-server", "attach thread to JVM"); - let verification_context = viper_arc.attach_current_thread(); - stopwatch.finish(); - let ast_utils = verification_context.new_ast_utils(); +) -> impl Stream { + stream! { + let stopwatch = Stopwatch::start("prusti-server", "attach thread to JVM"); + let verification_context = viper_arc.attach_current_thread(); + stopwatch.finish(); + let ast_utils = verification_context.new_ast_utils(); - // Only for testing: Check that the normalization is reversible. - if config::print_hash() { - debug_assert!({ - let mut program = request.program.clone(); - let normalization_info = NormalizationInfo::normalize_program(&mut program); - normalization_info.denormalize_program(&mut program); - program == request.program - }); - } - - // Normalize the request before reaching the cache. - let normalization_info = NormalizationInfo::normalize_program(&mut request.program); - - let hash = request.get_hash(); - info!( - "Verification request hash: {} - for program {}", - hash, - request.program.get_name() - ); - - let build_or_dump_viper_program = || { - let mut stopwatch = Stopwatch::start("prusti-server", "construction of JVM objects"); - let ast_factory = verification_context.new_ast_factory(); - let viper_program = request - .program - .to_viper(prusti_common::vir::LoweringContext::default(), &ast_factory); - - if config::dump_viper_program() { - stopwatch.start_next("dumping viper program"); - dump_viper_program( - &ast_utils, - viper_program, - &request.program.get_name_with_check_mode(), - ); + // Only for testing: Check that the normalization is reversible. + if config::print_hash() { + debug_assert!({ + let mut program = request.program.clone(); + let normalization_info = NormalizationInfo::normalize_program(&mut program); + normalization_info.denormalize_program(&mut program); + program == request.program + }); } - viper_program - }; + // Normalize the request before reaching the cache. + let normalization_info = NormalizationInfo::normalize_program(&mut request.program); - // Only for testing: Print the hash and skip verification. - if config::print_hash() { - println!( - "Received verification request for: {}", + let hash = request.get_hash(); + info!( + "Verification request hash: {} - for program {}", + hash, request.program.get_name() ); - println!("Hash of the request is: {}", hash); - // Some tests need the dump to report a diff of the Viper programs. - if config::dump_viper_program() { - ast_utils.with_local_frame(16, || { - let _ = build_or_dump_viper_program(); - }); - } - return viper::VerificationResult::Success; - } - // Early return in case of cache hit - if config::enable_cache() { - if let Some(mut result) = cache.get(hash) { - info!( - "Using cached result {:?} for program {}", - &result, + let build_or_dump_viper_program = || { + let mut stopwatch = Stopwatch::start("prusti-server", "construction of JVM objects"); + let ast_factory = verification_context.new_ast_factory(); + let viper_program = request + .program + .to_viper(prusti_common::vir::LoweringContext::default(), &ast_factory); + + if config::dump_viper_program() { + stopwatch.start_next("dumping viper program"); + dump_viper_program( + &ast_utils, + viper_program, + &request.program.get_name_with_check_mode(), + ); + } + + viper_program + }; + + // Only for testing: Print the hash and skip verification. + if config::print_hash() { + println!( + "Received verification request for: {}", request.program.get_name() ); + println!("Hash of the request is: {}", hash); + // Some tests need the dump to report a diff of the Viper programs. if config::dump_viper_program() { ast_utils.with_local_frame(16, || { let _ = build_or_dump_viper_program(); }); } - normalization_info.denormalize_result(&mut result); - return result; + yield ServerMessage::Termination(viper::VerificationResult::Success); + return; } - }; - ast_utils.with_local_frame(16, || { - let viper_program = build_or_dump_viper_program(); - let program_name = request.program.get_name(); + // Early return in case of cache hit + if config::enable_cache() { + if let Some(mut result) = cache.get(hash) { + info!( + "Using cached result {:?} for program {}", + &result, + request.program.get_name() + ); + if config::dump_viper_program() { + ast_utils.with_local_frame(16, || { + let _ = build_or_dump_viper_program(); + }); + } + normalization_info.denormalize_result(&mut result); + yield ServerMessage::Termination(result); + return; + } + }; - // Create a new verifier each time. - // Workaround for https://github.com/viperproject/prusti-dev/issues/744 - let mut stopwatch = Stopwatch::start("prusti-server", "verifier startup"); - let mut verifier = - new_viper_verifier(program_name, &verification_context, request.backend_config); + ast_utils.with_local_frame(16, || { + let viper_program = build_or_dump_viper_program(); + let program_name = request.program.get_name(); - let mut result = VerificationResult::Success; - let mut q_insts = HashMap::<(u64, String), u64>::new(); - let normalization_info_clone = normalization_info.clone(); + // Create a new verifier each time. + // Workaround for https://github.com/viperproject/prusti-dev/issues/744 + let mut stopwatch = Stopwatch::start("prusti-server", "verifier startup"); + let mut verifier = + new_viper_verifier(program_name, &verification_context, request.backend_config); - // start thread for polling messages and print on receive - // TODO: Detach warning - thread::scope(|s| { - // get the reporter - let env = &verification_context.env(); - let jni = JniUtils::new(env); - let verifier_wrapper = silver::verifier::Verifier::with(env); - let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); - let rep_glob_ref = env.new_global_ref(reporter).unwrap(); + let mut result = VerificationResult::Success; + let normalization_info_clone = normalization_info.clone(); - let (main_tx, thread_rx) = mpsc::channel(); - let (thread_tx, main_rx) = mpsc::channel(); - let polling_thread = s.spawn(move || { - let verification_context = viper_arc.attach_current_thread(); - let env = verification_context.env(); + // start thread for polling messages and print on receive + // TODO: Detach warning + thread::scope(|s| { + // get the reporter + let env = &verification_context.env(); let jni = JniUtils::new(env); - let reporter_instance = rep_glob_ref.as_obj(); - let reporter_wrapper = silver::reporter::PollingReporter::with(env); - let mut done = false; - let mut q_insts = HashMap::<(u64, String), u64>::new(); - while !done { - while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { - let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); - match jni.class_name(msg).as_str() { - "viper.silver.reporter.QuantifierInstantiationsMessage" => { - let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); - let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); - let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); - // TODO: find out which more quantifiers are derived from the user - // quantifiers - info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); - // also matches the "-aux" quantifiers generated - // TODO: some positions have just the id 0 and cannot be denormalized... - if q_name.starts_with("quant_with_posID") { - let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); - let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); - let parsed = stripped.parse::(); - match parsed { - Ok(pos_id) => { - let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); - // insert also updates the entry - q_insts.insert((norm_pos_id, q_name), q_inst.try_into().unwrap()); - () + let verifier_wrapper = silver::verifier::Verifier::with(env); + let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); + let rep_glob_ref = env.new_global_ref(reporter).unwrap(); + + let (main_tx, thread_rx) = mpsc::channel(); + let polling_thread = s.spawn(move || { + let verification_context = viper_arc.attach_current_thread(); + let env = verification_context.env(); + let jni = JniUtils::new(env); + let reporter_instance = rep_glob_ref.as_obj(); + let reporter_wrapper = silver::reporter::PollingReporter::with(env); + let mut done = false; + while !done { + while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { + let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); + match jni.class_name(msg).as_str() { + "viper.silver.reporter.QuantifierInstantiationsMessage" => { + let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); + let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); + let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); + // TODO: find out which more quantifiers are derived from the user + // quantifiers + info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); + // also matches the "-aux" quantifiers generated + // TODO: some positions have just the id 0 and cannot be denormalized... + if q_name.starts_with("quant_with_posID") { + let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); + let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); + let parsed = stripped.parse::(); + match parsed { + Ok(pos_id) => { + let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); + yield ServerMessage::QuantifierInstantiation(q_name, q_inst, norm_pos_id); + () + } + _ => info!("Unexpected quantifier name {}", q_name) } - _ => info!("Unexpected quantifier name {}", q_name) } } + _ => () } - _ => () + } + if !thread_rx.try_recv().is_err() { + info!("Polling thread received termination signal!"); + done = true; + } else { + thread::sleep(time::Duration::from_millis(10)); } } - if !thread_rx.try_recv().is_err() { - info!("Received termination signal!"); - done = true; - } else { - thread::sleep(time::Duration::from_millis(10)); - } - } - thread_tx.send(q_insts).unwrap(); + }); + stopwatch.start_next("verification"); + result = verifier.verify(viper_program); + // send termination signal to polling thread + main_tx.send(()).unwrap(); + // FIXME: here the global ref is dropped from a detached thread + polling_thread.join().unwrap(); }); - stopwatch.start_next("verification"); - result = verifier.verify(viper_program); - - main_tx.send(true).unwrap(); - q_insts = main_rx.recv().unwrap(); - // FIXME: here the global ref is dropped from a detached thread - polling_thread.join().unwrap(); - }); - // Don't cache Java exceptions, which might be due to misconfigured paths. - if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { - info!( - "Storing new cached result {:?} for program {}", - &result, - request.program.get_name() - ); - cache.insert(hash, result.clone()); - } + // Don't cache Java exceptions, which might be due to misconfigured paths. + if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { + info!( + "Storing new cached result {:?} for program {}", + &result, + request.program.get_name() + ); + cache.insert(hash, result.clone()); + } - // denormalized positioned id, name, total number of instantiations (because we overwrite - // each time in the polling thread) - normalization_info.denormalize_result(&mut result); - let enriched_result = match result { - VerificationResult::Success => { - info!("Enriching result"); - for ((pos_id, q_name), n) in q_insts.iter() { - info!("Enriching with {} {}: {}", q_name, pos_id, n); - } - VerificationResult::EnrichedSuccess(q_insts) - }, - VerificationResult::Failure(errors)=> { - info!("Enriching result"); - for ((pos_id, q_name), n) in q_insts.iter() { - info!("Enriching with {} {}: {}", q_name, pos_id, n); - } - VerificationResult::EnrichedFailure(errors, q_insts) - }, - _ => result, - }; - enriched_result - }) + normalization_info.denormalize_result(&mut result); + yield ServerMessage::Termination(result); + }) + } } fn dump_viper_program(ast_utils: &viper::AstUtils, program: viper::Program, program_name: &str) { diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index 3576fea3276..9cf6a75074f 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -4,8 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::{process_verification_request, VerificationRequest}; -use log::info; +use crate::{process_verification_request, VerificationRequest, ServerMessage}; +use log::{info, error}; use prusti_common::{config, Stopwatch}; use std::{ net::{Ipv4Addr, SocketAddr}, @@ -14,7 +14,7 @@ use std::{ }; use tokio::runtime::Builder; use viper::{PersistentCache, Viper}; -use warp::Filter; +use futures_util::{FutureExt, StreamExt, pin_mut}; #[derive(Debug)] struct BincodeReject(bincode::Error); @@ -61,31 +61,56 @@ where let cache = Arc::new(Mutex::new(cache_data)); let build_verification_request_handler = |viper_arc: Arc, cache| { move |request: VerificationRequest| { - process_verification_request(&viper_arc, request, &cache) + process_verification_request(&viper_arc, request, &cache); } }; let json_verify = warp::path!("json" / "verify") - .and(warp::body::json()) - .map(build_verification_request_handler( - viper.clone(), - cache.clone(), - )) - .map(|response| warp::reply::json(&response)); - - let bincode_verify = warp::path!("bincode" / "verify") - .and(warp::body::bytes()) - .and_then(|buf: warp::hyper::body::Bytes| async move { - bincode::deserialize(&buf).map_err(|err| { - info!("request bincode body error: {}", err); - warp::reject::custom(BincodeReject(err)) + .and(warp::filters::ws::ws()) + .map(|ws: warp::filters::ws::Ws| { + ws.on_upgrade(|websocket| { + let (tx, rx) = websocket.split(); + let handle_req = build_verification_request_handler(viper.clone(), cache.clone()); + let handle_msg = |msg: warp::filters::ws::Message| { + match msg.to_str().and_then(|s| serde_json::from_str(s)) { + Ok(req) => handle_req(req), + Err(err) => error!("ERROR in json request: {}", err), + } + } + let stream = handle_msg(rx.next().await); + pin_mut!(stream); + while let Some(msg) = stream.next().await { + tx.send(warp::filters::ws::Message::text(serde_json::to_string(&msg))).await; + } + tx.close().await; }) - }) - .map(build_verification_request_handler(viper.clone(), cache.clone())) - .map(|result| { + }); + ) + + let bincode_send_closure = |result| { warp::http::Response::new( bincode::serialize(&result).expect("could not encode verification result"), - ) + )}; + + let bincode_verify = warp::path!("bincode" / "verify") + .and(warp::filters::ws::ws()) + .map(|ws: warp::filters::ws::Ws| { + ws.on_upgrade(|websocket| { + let (tx, rx) = websocket.split(); + let handle_req = build_verification_request_handler(viper.clone(), cache.clone()); + let handle_msg = |msg: warp::filters::ws::Message| { + match msg.as_bytes().and_then(|b| bincode::deserialize(&b)) { + Ok(req) => handle_req(req), + Err(err) => error!("ERROR in bincode request: {}", err), + } + } + let stream = handle_msg(rx.next().await); + pin_mut!(stream); + while let Some(msg) = stream.next().await { + tx.send(warp::filters::ws::Message::bytes(bincode::serialize(&msg))).await; + } + tx.close().await; + }) }); let save_cache = warp::post() diff --git a/prusti-server/src/server_message.rs b/prusti-server/src/server_message.rs new file mode 100644 index 00000000000..2dd4a83cbd0 --- /dev/null +++ b/prusti-server/src/server_message.rs @@ -0,0 +1,13 @@ +// © 2023, ETH Zurich +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use viper::VerificationResult; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Hash)] +pub enum ServerMessage { + Termination(VerificationResult), + QuantifierInstantiation { q_name: String, insts: u64, norm_pos_id: u64 }, +} diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 7b0322eaca8..a4ace5ce307 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -273,9 +273,51 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { programs.extend(self.encoder.get_core_proof_programs()); stopwatch.start_next("verifying Viper program"); - // TODO: make this not wait for all programs and after that make the quantifier - // instantiations more asynchronous also on the client/server communication - let verification_results = verify_programs(self.env, programs); + + let verification_requests = programs_to_requests(programs); + //TODO: not at all finished + if let Some(server_address) = config::server_address() { + let server_address = if server_address == "MOCK" { + spawn_server_thread().to_string() + } else { + server_address + }; + info!("Connecting to Prusti server at {}", server_address); + let client = PrustiClient::new(&server_address).unwrap_or_else(|error| { + panic!( + "Could not parse server address ({}) due to {:?}", + server_address, error + ) + }); + // Here we construct a Tokio runtime to block until completion of the futures returned by + // `client.verify`. However, to report verification errors as early as possible, + // `verify_programs` should return an asynchronous stream of verification results. + let runtime = Builder::new_current_thread() + .thread_name("prusti-viper") + .enable_all() + .build() + .expect("failed to construct Tokio runtime"); + verification_requests.map(|(program_name, request)| { + let remote_result = runtime.block_on(client.verify(request)); + let result = remote_result.unwrap_or_else(|error| { + panic!( + "Verification request of program {} failed: {:?}", + program_name, + error + ) + }); + (program_name, result) + }).collect() + } else { + let stopwatch = Stopwatch::start("prusti-viper", "JVM startup"); + let viper_arc = Arc::new(Viper::new_with_args(&config::viper_home(), config::extra_jvm_args())); + let mut cache = PersistentCache::load_cache(config::cache_path()); + stopwatch.finish(); + verification_requests.map(|(program_name, request)| { + let result = process_verification_request(&viper_arc, request, &mut cache); + (program_name, result) + }).collect() + } stopwatch.finish(); // Group verification results @@ -448,76 +490,36 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { result } -} -/// Verify a list of programs. -/// Returns a list of (program_name, verification_result) tuples. -fn verify_programs(env: &Environment, programs: Vec) - -> Vec<(String, viper::VerificationResult)> -{ - let source_path = env.name.source_path(); - let rust_program_name = source_path - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_owned(); - let verification_requests = programs.into_iter().map(|mut program| { - let program_name = program.get_name().to_string(); - let check_mode = program.get_check_mode(); - // Prepend the Rust file name to the program. - program.set_name(format!("{}_{}", rust_program_name, program_name)); - let backend = if check_mode == CheckMode::Specifications { - config::verify_specifications_backend() - } else { - config::viper_backend() - }.parse().unwrap(); - let request = VerificationRequest { - program, - backend_config: ViperBackendConfig::new(backend), - }; - (program_name, request) - }); - if let Some(server_address) = config::server_address() { - let server_address = if server_address == "MOCK" { - spawn_server_thread().to_string() - } else { - server_address - }; - info!("Connecting to Prusti server at {}", server_address); - let client = PrustiClient::new(&server_address).unwrap_or_else(|error| { - panic!( - "Could not parse server address ({}) due to {:?}", - server_address, error - ) + /// Returns a list of (program_name, verification_requests) tuples. + fn programs_to_requests(programs: Vec) + -> Vec<(String, VerificationRequest)> + { + let source_path = self.env.name.source_path(); + let rust_program_name = source_path + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + let verification_requests = programs.into_iter().map(|mut program| { + let program_name = program.get_name().to_string(); + let check_mode = program.get_check_mode(); + // Prepend the Rust file name to the program. + program.set_name(format!("{}_{}", rust_program_name, program_name)); + let backend = if check_mode == CheckMode::Specifications { + config::verify_specifications_backend() + } else { + config::viper_backend() + }.parse().unwrap(); + let request = VerificationRequest { + program, + backend_config: ViperBackendConfig::new(backend), + }; + (program_name, request) }); - // Here we construct a Tokio runtime to block until completion of the futures returned by - // `client.verify`. However, to report verification errors as early as possible, - // `verify_programs` should return an asynchronous stream of verification results. - let runtime = Builder::new_current_thread() - .thread_name("prusti-viper") - .enable_all() - .build() - .expect("failed to construct Tokio runtime"); - verification_requests.map(|(program_name, request)| { - let remote_result = runtime.block_on(client.verify(request)); - let result = remote_result.unwrap_or_else(|error| { - panic!( - "Verification request of program {} failed: {:?}", - program_name, - error - ) - }); - (program_name, result) - }).collect() - } else { - let stopwatch = Stopwatch::start("prusti-viper", "JVM startup"); - let viper_arc = Arc::new(Viper::new_with_args(&config::viper_home(), config::extra_jvm_args())); - let mut cache = PersistentCache::load_cache(config::cache_path()); - stopwatch.finish(); - verification_requests.map(|(program_name, request)| { - let result = process_verification_request(&viper_arc, request, &mut cache); - (program_name, result) - }).collect() + verification_requests } + } + diff --git a/viper/src/verification_result.rs b/viper/src/verification_result.rs index 85d96348379..815e3779a85 100644 --- a/viper/src/verification_result.rs +++ b/viper/src/verification_result.rs @@ -18,10 +18,6 @@ pub enum VerificationResult { ConsistencyErrors(Vec), /// The verification raised a Java exception. JavaException(JavaException), - /// Ships with quantifier statistics (currently only provided by Z3) that - /// map from position ID and name to the number of instantiations. - EnrichedSuccess(HashMap<(u64, String), u64>), - EnrichedFailure(Vec, HashMap<(u64, String), u64>), } impl VerificationResult { From c1cbde9d1684fc3914dfea7d56ffebb0a53150ea Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 19 Jan 2023 15:03:57 +0100 Subject: [PATCH 18/74] Managed to subst EarlyBinders --- prusti/src/ide_helper/query_signature.rs | 153 +++++++++++++++-------- 1 file changed, 99 insertions(+), 54 deletions(-) diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 90f426d1ac9..dc792d624dd 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -1,17 +1,16 @@ +use core::fmt; use prusti_common::config; use prusti_interface::environment::Environment; -use core::fmt; use std::collections::HashMap; use super::ide_info::ProcDef; use prusti_rustc_interface::{ hir::{def::DefKind, def_id::DefId}, - middle::ty::{DefIdTree, Generics, ImplSubject, PredicateKind, TraitRef, Ty, TyCtxt}, + middle::ty::{self, DefIdTree, Generics, ImplSubject, PredicateKind, TraitRef, Ty, TyCtxt}, }; pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) -> Option { let def_path_str: String = config::query_method_signature()?; - println!("\n\n\n\nCollecting Information for Queried Method"); println!( "Got query for external specification template for : {}", def_path_str @@ -31,13 +30,19 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) match def_kind { DefKind::Fn => { // TODO - println!("Still a todo :)"); - None - }, + let parent_chain = get_parent_chain(defid, &tcx); + let signature_str = fn_signature_str(defid, &tcx); + let (fn_generics_str, _) = generic_params_str(defid, &tcx, false); + let item_name = tcx.opt_item_name(defid)?; + let contents = format!( + "use prusti_contracts::*;\nfn {}{}{};", + item_name, fn_generics_str, signature_str + ); + Some(wrap_in_parent_mods(parent_chain, contents)) + } DefKind::AssocFn => { - let opt_item_name = tcx.opt_item_name(defid)?; - let item_name = opt_item_name.as_str(); - println!("Item Name: {}", item_name); + let item_name_ident = tcx.opt_item_name(defid)?; + let item_name = item_name_ident.as_str(); let impl_of_method_opt = tcx.impl_of_method(defid); match impl_of_method_opt { Some(impl_of_method) => { @@ -54,7 +59,8 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) println!("Self Type: {:?}", self_ty); let signature_str = fn_signature_str(defid, &tcx); - let (impl_generics_str, _opt_where) = generic_params_str(impl_of_method, &tcx, false); + let (impl_generics_str, _opt_where) = + generic_params_str(impl_of_method, &tcx, false); let (fn_generics_str, _opt_where) = generic_params_str(defid, &tcx, false); // Generate result: @@ -66,15 +72,10 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) res.push_str(" for "); } //pub fn name(arguments) - res.push_str( - &format!( - "{} {{\n\tpub fn {}{}{};\n}}", - self_ty, - item_name, - fn_generics_str, - signature_str, - ) - ); + res.push_str(&format!( + "{} {{\n\tpub fn {}{}{};\n}}", + self_ty, item_name, fn_generics_str, signature_str, + )); println!("Result: {}", res); @@ -87,18 +88,13 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) if tcx.is_trait(parent) { println!("Yes indeed this is a trait"); - let fn_name = tcx.def_path_str(defid); let traitname = tcx.def_path_str(parent); let trait_params = trait_params(parent, &tcx); let (fn_generics, _where) = generic_params_str(defid, &tcx, false); let fn_sig = fn_signature_str(defid, &tcx); Some(format!( "#[extern_spec]\ntrait {}{} {{\n\tfn {}{}{};\n}}", - traitname, - trait_params, - fn_name, - fn_generics, - fn_sig, + traitname, trait_params, item_name, fn_generics, fn_sig, )) } else { println!("Nope it's not actually a trait, check this case"); @@ -115,19 +111,17 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) } } - // by signature we mean the arguments + return type -pub fn fn_signature_str( - defid: DefId, - tcx: &TyCtxt<'_>, -) -> String { +pub fn fn_signature_str(defid: DefId, tcx: &TyCtxt<'_>) -> String { let signature = tcx.fn_sig(defid).skip_binder(); let inputs = signature.inputs().iter(); let arg_names = tcx.fn_arg_names(defid); let output = signature.output(); - let args = arg_names.iter().zip(inputs) + let args = arg_names + .iter() + .zip(inputs) .map(|(name, ty)| format!("{}: {}", name.as_str(), ty)) .collect::>() .join(", "); @@ -136,16 +130,20 @@ pub fn fn_signature_str( pub fn trait_params(defid: DefId, tcx: &TyCtxt<'_>) -> String { let generic_params = tcx.generics_of(defid); - let result = generic_params.params.iter() - .filter(|p| p.has_default()) // types like Self will show up here,but can't be in code - .map(|p| format!("{}={:?}", p.name, p.default_value(*tcx).unwrap())) - .collect::>().join(", "); + let substs = ty::subst::InternalSubsts::identity_for_item(*tcx, defid); + + let result = generic_params + .params + .iter() + .filter(|p| p.has_default()) // types like Self will show up here,but can't be in code + .map(|p| format!("{}={:?}", p.name, p.default_value(*tcx).unwrap().subst(*tcx, substs))) + .collect::>() + .join(", "); if generic_params.params.iter().len() > 0 { format!("<{}>", result) } else { "".to_string() } - } enum GenArguments<'a> { @@ -158,7 +156,7 @@ impl<'a> fmt::Display for GenArguments<'a> { match self { Self::GenType(ty) => { write!(f, "{}", ty) - }, + } Self::Projection(item, value) => { write!(f, "{}={}", item, value) } @@ -168,7 +166,7 @@ impl<'a> fmt::Display for GenArguments<'a> { pub fn generic_params_str( defid: DefId, // defid of a function or impl block tcx: &TyCtxt<'_>, - exclude_bounds_to_where: bool, // whether or not trait bounds should be + exclude_bounds_to_where: bool, // whether or not trait bounds should be // moved into an extra "where" block ) -> (String, Option) { // should receive defid of either an impl block or a function definition. @@ -204,7 +202,8 @@ pub fn generic_params_str( } match traitbounds.get_mut(&self_ty_str) { None => { - traitbounds.insert(self_ty_str, HashMap::from([(traitref.def_id, trait_args)])); + traitbounds + .insert(self_ty_str, HashMap::from([(traitref.def_id, trait_args)])); } Some(trait_vec) => { trait_vec.insert(traitref.def_id, trait_args); @@ -240,34 +239,80 @@ pub fn generic_params_str( } // Stringify let count = generic_params.params.iter().len(); - // there is a field generic_params.count() but apparently it does not + // there is a field generic_params.count() but apparently it does not // give us the same value, not sure what it stands for if count > 0 { - let result = generic_params.params // iterate through all parameters + let result = generic_params + .params // iterate through all parameters .iter() .map(|param_ident| { let param = param_ident.name.as_str(); let traitboundmap_opt = traitbounds.get(param); if let Some(traitboundmap) = traitboundmap_opt { - let trait_list_str = traitboundmap.iter().map(|(trait_id, param_list)| { - let trait_str = tcx.def_path_str(*trait_id); - if param_list.len() <= 0 { - trait_str - } else { - let param_str = param_list.iter() - .map(|gp| format!("{}", gp)) - .collect::>().join(", "); - format!("{trait_str}<{param_str}>") - } - }).collect::>().join(" + "); + let trait_list_str = traitboundmap + .iter() + .map(|(trait_id, param_list)| { + let trait_str = tcx.def_path_str(*trait_id); + if param_list.len() <= 0 { + trait_str + } else { + let param_str = param_list + .iter() + .map(|gp| format!("{}", gp)) + .collect::>() + .join(", "); + format!("{trait_str}<{param_str}>") + } + }) + .collect::>() + .join(" + "); format!("{param}: {trait_list_str}") } else { param.to_string() } - }).collect::>().join(", "); + }) + .collect::>() + .join(", "); (format!("<{result}>"), None) - } else { ("".to_string(), None) } } + +fn get_parent_chain(defid: DefId, tcx: &TyCtxt<'_>) -> Vec { + let mut result = vec![]; + let mut parent_opt = tcx.opt_parent(defid); + while let Some(parent_id) = parent_opt { + let name = tcx.item_name(parent_id).to_string(); + result.push(name); + parent_opt = tcx.opt_parent(parent_id); // check for parent of parent + } + println!("Get Parent Chain: {:?}", result); + result.reverse(); + result +} + +fn wrap_in_parent_mods(parent_chain: Vec, contents: String) -> String { + let mut tab_level = 0; + let chain_length = parent_chain.len(); + let mut result = "#[extern_spec]\n".to_string(); + for parent_mod in &parent_chain { + result.push_str(&"\t".repeat(tab_level)); + result.push_str(&format!("mod {} {{\n", parent_mod)); + tab_level += 1; + } + result.push_str( + &contents + .split('\n') + .map(|line| "\t".repeat(tab_level) + line) + .collect::>() + .join("\n"), + ); // appends the indented contents of the mod + for _ in 0..chain_length { + tab_level -= 1; + result.push('\n'); + result.push_str(&"\t".repeat(tab_level)); + result.push('}'); // close the curly braces at the right tab_level + } + result +} From 6facfce78548b6b1d5b99585ce6e4b1553547dd5 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 19 Jan 2023 15:25:25 +0100 Subject: [PATCH 19/74] upgraded my parts to current rust version --- prusti/src/ide_helper/query_signature.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index dc792d624dd..8598ca9bf63 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use super::ide_info::ProcDef; use prusti_rustc_interface::{ hir::{def::DefKind, def_id::DefId}, - middle::ty::{self, DefIdTree, Generics, ImplSubject, PredicateKind, TraitRef, Ty, TyCtxt}, + middle::ty::{self, DefIdTree, Generics, ImplSubject, PredicateKind, TraitRef, Ty, TyCtxt, Clause}, }; pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) -> Option { @@ -89,7 +89,7 @@ pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) if tcx.is_trait(parent) { println!("Yes indeed this is a trait"); let traitname = tcx.def_path_str(parent); - let trait_params = trait_params(parent, &tcx); + let trait_params = trait_params(parent, tcx); let (fn_generics, _where) = generic_params_str(defid, &tcx, false); let fn_sig = fn_signature_str(defid, &tcx); Some(format!( @@ -128,15 +128,15 @@ pub fn fn_signature_str(defid: DefId, tcx: &TyCtxt<'_>) -> String { format!("({}) -> {}", args, output) } -pub fn trait_params(defid: DefId, tcx: &TyCtxt<'_>) -> String { +pub fn trait_params(defid: DefId, tcx: TyCtxt<'_>) -> String { let generic_params = tcx.generics_of(defid); - let substs = ty::subst::InternalSubsts::identity_for_item(*tcx, defid); + let substs = ty::subst::InternalSubsts::identity_for_item(tcx, defid); let result = generic_params .params .iter() - .filter(|p| p.has_default()) // types like Self will show up here,but can't be in code - .map(|p| format!("{}={:?}", p.name, p.default_value(*tcx).unwrap().subst(*tcx, substs))) + .filter(|p| (*p).default_value(tcx).is_some()) // types like Self will show up here,but can't be in code, + .map(|p| format!("{}={:?}", p.name, p.default_value(tcx).unwrap().subst(tcx, substs))) .collect::>() .join(", "); if generic_params.params.iter().len() > 0 { @@ -184,7 +184,7 @@ pub fn generic_params_str( println!("Found predicate of kind: {:?}", predicate.kind()); let kind: PredicateKind = predicate.kind().skip_binder(); // i don't understand this... match kind { - PredicateKind::Trait(t) => { + PredicateKind::Clause(Clause::Trait(t)) => { println!("PredicateKind::Trait(t) with t: {:?}", t); let traitref = t.trait_ref; let self_ty_str = format!("{}", traitref.self_ty()); @@ -210,14 +210,14 @@ pub fn generic_params_str( } } } - PredicateKind::Projection(p) => { + PredicateKind::Clause(Clause::Projection(p)) => { // for example, for impl>, // - item_id identifies Output, // - trait_defid: Trait // - term: the type assigned to Ouptut // - self_ty: The first T // (not very sure about the last 2) - let item_id = p.projection_ty.item_def_id; + let item_id = p.projection_ty.def_id; let self_ty_str = format!("{}", p.projection_ty.self_ty()); let trait_defid: DefId = p.projection_ty.trait_def_id(*tcx); // I want the identifier e.g. std::ops::Add From 3ac17ee3d2af8c36ca485e991022bd138b35929b Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Tue, 31 Jan 2023 16:49:59 +0100 Subject: [PATCH 20/74] Asynchronously report messages from the server to the client via WebSocket. Report quantifier instantiations split by methods. --- Cargo.lock | 197 ++++++------ prusti-server/Cargo.toml | 9 +- prusti-server/src/client.rs | 65 ++-- prusti-server/src/lib.rs | 2 + prusti-server/src/process_verification.rs | 352 +++++++++++--------- prusti-server/src/server.rs | 85 ++--- prusti-server/src/server_message.rs | 2 +- prusti-viper/Cargo.toml | 2 + prusti-viper/src/verifier.rs | 373 ++++++++++------------ viper/src/verification_result.rs | 1 - 10 files changed, 528 insertions(+), 560 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 355b7e61a16..b9301c2e401 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,6 +192,27 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-task" version = "4.3.0" @@ -864,15 +885,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if", -] - [[package]] name = "env_logger" version = "0.9.1" @@ -998,9 +1010,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", @@ -1013,9 +1025,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", @@ -1023,15 +1035,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", @@ -1040,9 +1052,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-lite" @@ -1061,9 +1073,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", @@ -1072,21 +1084,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", @@ -1322,19 +1334,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "iana-time-zone" version = "0.1.48" @@ -1378,6 +1377,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "input_buffer" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" +dependencies = [ + "bytes", +] + [[package]] name = "instant" version = "0.1.12" @@ -1387,12 +1395,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "ipnet" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" - [[package]] name = "itertools" version = "0.10.4" @@ -1741,9 +1743,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.14.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -2048,17 +2050,22 @@ version = "0.1.0" name = "prusti-server" version = "0.1.0" dependencies = [ + "async-stream", "bincode", "clap", "env_logger", + "futures", + "futures-util", "jni", "lazy_static", "log", "num_cpus", + "once_cell", "prusti-common", - "reqwest", "serde", + "serde_json", "tokio", + "tokio-tungstenite 0.13.0", "url", "viper", "viper-sys", @@ -2121,9 +2128,11 @@ dependencies = [ name = "prusti-viper" version = "0.1.0" dependencies = [ + "async-stream", "backtrace", "derive_more", "diffy", + "futures-util", "itertools", "lazy_static", "log", @@ -2274,43 +2283,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "reqwest" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - [[package]] name = "ring" version = "0.16.20" @@ -2862,24 +2834,27 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.3.0" +name = "tokio-stream" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" dependencies = [ - "native-tls", + "futures-core", + "pin-project-lite", "tokio", ] [[package]] -name = "tokio-stream" -version = "0.1.10" +name = "tokio-tungstenite" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" +checksum = "e1a5f475f1b9d077ea1017ecbc60890fda8e54942d680ca0b1d2b47cfa2d861b" dependencies = [ - "futures-core", - "pin-project-lite", + "futures-util", + "log", + "pin-project", "tokio", + "tungstenite 0.12.0", ] [[package]] @@ -2892,7 +2867,7 @@ dependencies = [ "log", "pin-project", "tokio", - "tungstenite", + "tungstenite 0.14.0", ] [[package]] @@ -2978,6 +2953,25 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "tungstenite" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ada8297e8d70872fa9a551d93250a9f407beb9f37ef86494eb20012a2ff7c24" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "input_buffer", + "log", + "rand", + "sha-1", + "url", + "utf-8", +] + [[package]] name = "tungstenite" version = "0.14.0" @@ -3253,7 +3247,7 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-stream", - "tokio-tungstenite", + "tokio-tungstenite 0.15.0", "tokio-util 0.6.10", "tower-service", "tracing", @@ -3455,15 +3449,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - [[package]] name = "xattr" version = "0.2.3" diff --git a/prusti-server/Cargo.toml b/prusti-server/Cargo.toml index 1d6b2b53cb5..9a4fc630005 100644 --- a/prusti-server/Cargo.toml +++ b/prusti-server/Cargo.toml @@ -27,13 +27,16 @@ bincode = "1.0" url = "2.2.2" num_cpus = "1.8.0" serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0" } warp = "0.3" tokio = "1.20" jni = { version = "0.19", features = ["invocation"] } # the tungstenite version used by warp 0.3 -tokio_tungstenite = "0.13" -async_stream = "0.3.3" -futures_util = "0.3.25" +tokio-tungstenite = "0.13" +futures = "0.3.25" +futures-util = "0.3.25" +async-stream = "0.3.3" +once_cell = "1.17.0" [dev-dependencies] lazy_static = "1.4.0" diff --git a/prusti-server/src/client.rs b/prusti-server/src/client.rs index ca5363dd4b2..cc62bae8eab 100644 --- a/prusti-server/src/client.rs +++ b/prusti-server/src/client.rs @@ -4,65 +4,60 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::VerificationRequest; +use crate::{VerificationRequest, ServerMessage}; use prusti_common::config; -use url::{ParseError, Url}; -use viper::VerificationResult; -use tokio_tungstenite::{client::connect_async, - tungstenite::{Message, - handshake::client::Response, - error::Error} -}; -use async_stream::stream; -use futures_core::stream::Stream; -use futures_util::{FutureExt, stream::StreamExt, sink::SinkExt, pin_mut}; +use url::Url; +use tokio_tungstenite::{connect_async, + tungstenite::{Message, error::Error}}; +use futures_util::{stream::{Stream, StreamExt}, sink::SinkExt}; -pub struct PrustiClient { - server_url: Url, -} +pub struct PrustiClient; impl PrustiClient { - pub fn new(server_address: S) -> Result { + pub async fn verify( + server_address: S, + request: VerificationRequest, + ) -> impl Stream { + // TODO: do proper error handling let mut address = server_address.to_string(); if !address.starts_with("ws") { address = format!("ws://{}", address); } - Ok(Self { - server_url: Url::parse(address.as_str())?, - }) - } + let server_url = Url::parse(address.as_str()).unwrap(); - pub async fn verify( - &self, - request: VerificationRequest, - ) -> Result, Error> { let use_json = config::json_communication(); - let uri = self.server_url + + let uri = server_url .join(if use_json { "json/" } else { "bincode/" }) .unwrap() .join("verify/") .unwrap(); - let (socket, _) = connect_async(uri).await?; - let msg = if use_json Message::text(serde_json::to_string(request).expect("error encoding verification request in json")) - else Message::binary(bincode::serialize(&request).expect("error encoding verification request as binary")); - pin_mut!(socket); - socket.send(msg).await?; - // TODO: do we have to care about close messages? + let (mut socket, _) = connect_async(uri).await.unwrap(); + let msg = if use_json { Message::text(serde_json::to_string(&request).expect("error encoding verification request in json")) } + else { Message::binary(bincode::serialize(&request).expect("error encoding verification request as binary")) }; + socket.send(msg).await.unwrap(); let json_map = |ws_msg| { if let Message::Text(json) = ws_msg { - serde_json::from_string(&json).expect("error decoding verification result from json") + serde_json::from_str(&json).expect("error decoding verification result from json") } else { panic!("Invalid response from the server."); } - }); + }; let bin_map = |ws_msg| { if let Message::Binary(bytes) = ws_msg { bincode::deserialize(&bytes).expect("error decoding verification result") } else { panic!("Invalid response from the server."); } - }); - let s = socket.map(if use_json json_map else bin_map); - Ok(s) + }; + let filter_close = |msg_result: Result| async { + let msg = msg_result.unwrap(); + match msg { + Message::Close(_) => None, + _ => Some(msg), + } + }; + let s = socket.filter_map(filter_close).map(if use_json { json_map } else { bin_map }); + s } } diff --git a/prusti-server/src/lib.rs b/prusti-server/src/lib.rs index f166b66985d..e1ee796d536 100644 --- a/prusti-server/src/lib.rs +++ b/prusti-server/src/lib.rs @@ -7,6 +7,7 @@ mod client; mod process_verification; mod server; +mod server_message; mod verification_request; mod jni_utils; mod java_exception; @@ -14,6 +15,7 @@ mod java_exception; pub use client::*; pub use process_verification::*; pub use server::*; +pub use server_message::*; pub use verification_request::*; pub use jni_utils::*; pub use java_exception::*; diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index fff4e3d9108..365e15ec7e1 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -12,192 +12,248 @@ use prusti_common::{ vir::{program_normalization::NormalizationInfo, ToViper}, Stopwatch, }; -use std::{fs::create_dir_all, path::PathBuf, thread, sync::Arc, collections::HashMap}; +use std::{fs::create_dir_all, path::PathBuf, thread, sync::{mpsc, Arc, self}}; use viper::{ - smt_manager::SmtManager, Cache, VerificationBackend, VerificationResult, Viper, + smt_manager::SmtManager, PersistentCache, Cache, VerificationBackend, VerificationResult, Viper, VerificationContext }; use viper_sys::wrappers::viper::*; use std::time; -use std::sync::mpsc; -use async_stream::stream; +use futures::{stream::Stream, lock}; -pub fn process_verification_request<'v, 't: 'v>( +pub struct VerificationRequestProcessing { + mtx_rx_servermsg: lock::Mutex>, + mtx_tx_verreq: sync::Mutex>, +} + +// one structure that lives for all the requests and has a single thread working on all the +// requests sequentially +// on reception of a verification request, we send it through a channel to the already running +// thread +impl VerificationRequestProcessing { + pub fn new() -> Self { + let (tx_servermsg, rx_servermsg) = mpsc::channel(); + let (tx_verreq, rx_verreq) = mpsc::channel(); + let mtx_rx_servermsg = lock::Mutex::new(rx_servermsg); + let mtx_tx_verreq = sync::Mutex::new(tx_verreq); + let ret = Self {mtx_rx_servermsg: mtx_rx_servermsg, mtx_tx_verreq: mtx_tx_verreq}; + thread::spawn(|| { Self::verification_thread(rx_verreq, tx_servermsg) }); + ret + } + + fn verification_thread(rx_verreq: mpsc::Receiver, tx_servermsg: mpsc::Sender) { + let mut stopwatch = Stopwatch::start("verification_request_processing", "JVM startup"); + let viper = Arc::new(Viper::new_with_args(&config::viper_home(), config::extra_jvm_args())); + let mut cache = PersistentCache::load_cache(config::cache_path()); + stopwatch.start_next("attach thread to JVM"); + let verification_context = viper.attach_current_thread(); + stopwatch.finish(); + loop { + match rx_verreq.recv() { + Ok(request) => { + process_verification_request(&viper, &mut cache, &verification_context, &tx_servermsg, request); + } + Err(_) => break, + } + } + } + + pub fn verify<'a>(&'a self, request: VerificationRequest) -> impl Stream + 'a { + self.mtx_tx_verreq + .lock() + .unwrap() + .send(request) + .unwrap(); + futures::stream::unfold(false, move |done: bool| async move { + if done { + return None; + } + let msg = self.mtx_rx_servermsg + .lock() + .await + .recv() + .unwrap(); + let mut done = false; + if let ServerMessage::Termination(_) = msg { + done = true; + } + Some((msg, done)) + }) + } +} +pub fn process_verification_request( viper_arc: &Arc, - mut request: VerificationRequest, cache: impl Cache, -) -> impl Stream { - stream! { - let stopwatch = Stopwatch::start("prusti-server", "attach thread to JVM"); - let verification_context = viper_arc.attach_current_thread(); - stopwatch.finish(); - let ast_utils = verification_context.new_ast_utils(); + verification_context: &VerificationContext, + sender: &mpsc::Sender, + mut request: VerificationRequest, +) { + let ast_utils = verification_context.new_ast_utils(); - // Only for testing: Check that the normalization is reversible. - if config::print_hash() { - debug_assert!({ - let mut program = request.program.clone(); - let normalization_info = NormalizationInfo::normalize_program(&mut program); - normalization_info.denormalize_program(&mut program); - program == request.program - }); - } + // Only for testing: Check that the normalization is reversible. + if config::print_hash() { + debug_assert!({ + let mut program = request.program.clone(); + let normalization_info = NormalizationInfo::normalize_program(&mut program); + normalization_info.denormalize_program(&mut program); + program == request.program + }); + } - // Normalize the request before reaching the cache. - let normalization_info = NormalizationInfo::normalize_program(&mut request.program); + // Normalize the request before reaching the cache. + let normalization_info = NormalizationInfo::normalize_program(&mut request.program); - let hash = request.get_hash(); - info!( - "Verification request hash: {} - for program {}", - hash, - request.program.get_name() - ); + let hash = request.get_hash(); + info!( + "Verification request hash: {} - for program {}", + hash, + request.program.get_name() + ); - let build_or_dump_viper_program = || { - let mut stopwatch = Stopwatch::start("prusti-server", "construction of JVM objects"); - let ast_factory = verification_context.new_ast_factory(); - let viper_program = request - .program - .to_viper(prusti_common::vir::LoweringContext::default(), &ast_factory); + let build_or_dump_viper_program = || { + let mut stopwatch = Stopwatch::start("prusti-server", "construction of JVM objects"); + let ast_factory = verification_context.new_ast_factory(); + let viper_program = request + .program + .to_viper(prusti_common::vir::LoweringContext::default(), &ast_factory); - if config::dump_viper_program() { - stopwatch.start_next("dumping viper program"); - dump_viper_program( - &ast_utils, - viper_program, - &request.program.get_name_with_check_mode(), - ); - } + if config::dump_viper_program() { + stopwatch.start_next("dumping viper program"); + dump_viper_program( + &ast_utils, + viper_program, + &request.program.get_name_with_check_mode(), + ); + } - viper_program - }; + viper_program + }; - // Only for testing: Print the hash and skip verification. - if config::print_hash() { - println!( - "Received verification request for: {}", + // Only for testing: Print the hash and skip verification. + if config::print_hash() { + println!( + "Received verification request for: {}", + request.program.get_name() + ); + println!("Hash of the request is: {}", hash); + // Some tests need the dump to report a diff of the Viper programs. + if config::dump_viper_program() { + ast_utils.with_local_frame(16, || { + let _ = build_or_dump_viper_program(); + }); + } + sender.send(ServerMessage::Termination(viper::VerificationResult::Success)).unwrap(); + return; + } + + // Early return in case of cache hit + if config::enable_cache() { + if let Some(mut result) = cache.get(hash) { + info!( + "Using cached result {:?} for program {}", + &result, request.program.get_name() ); - println!("Hash of the request is: {}", hash); - // Some tests need the dump to report a diff of the Viper programs. if config::dump_viper_program() { ast_utils.with_local_frame(16, || { let _ = build_or_dump_viper_program(); }); } - yield ServerMessage::Termination(viper::VerificationResult::Success); + normalization_info.denormalize_result(&mut result); + sender.send(ServerMessage::Termination(result)).unwrap(); return; } + }; - // Early return in case of cache hit - if config::enable_cache() { - if let Some(mut result) = cache.get(hash) { - info!( - "Using cached result {:?} for program {}", - &result, - request.program.get_name() - ); - if config::dump_viper_program() { - ast_utils.with_local_frame(16, || { - let _ = build_or_dump_viper_program(); - }); - } - normalization_info.denormalize_result(&mut result); - yield ServerMessage::Termination(result); - return; - } - }; + ast_utils.with_local_frame(16, || { + let viper_program = build_or_dump_viper_program(); + let program_name = request.program.get_name(); - ast_utils.with_local_frame(16, || { - let viper_program = build_or_dump_viper_program(); - let program_name = request.program.get_name(); + // Create a new verifier each time. + // Workaround for https://github.com/viperproject/prusti-dev/issues/744 + let mut stopwatch = Stopwatch::start("prusti-server", "verifier startup"); + let mut verifier = + new_viper_verifier(program_name, &verification_context, request.backend_config); - // Create a new verifier each time. - // Workaround for https://github.com/viperproject/prusti-dev/issues/744 - let mut stopwatch = Stopwatch::start("prusti-server", "verifier startup"); - let mut verifier = - new_viper_verifier(program_name, &verification_context, request.backend_config); + let mut result = VerificationResult::Success; + let normalization_info_clone = normalization_info.clone(); + let sender_clone = sender.clone(); - let mut result = VerificationResult::Success; - let normalization_info_clone = normalization_info.clone(); + // start thread for polling messages and print on receive + // TODO: Detach warning + thread::scope(|scope| { + // get the reporter + let env = &verification_context.env(); + let jni = JniUtils::new(env); + let verifier_wrapper = silver::verifier::Verifier::with(env); + let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); + let rep_glob_ref = env.new_global_ref(reporter).unwrap(); - // start thread for polling messages and print on receive - // TODO: Detach warning - thread::scope(|s| { - // get the reporter - let env = &verification_context.env(); + let (main_tx, thread_rx) = mpsc::channel(); + let polling_thread = scope.spawn(move || { + let verification_context = viper_arc.attach_current_thread(); + let env = verification_context.env(); let jni = JniUtils::new(env); - let verifier_wrapper = silver::verifier::Verifier::with(env); - let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); - let rep_glob_ref = env.new_global_ref(reporter).unwrap(); - - let (main_tx, thread_rx) = mpsc::channel(); - let polling_thread = s.spawn(move || { - let verification_context = viper_arc.attach_current_thread(); - let env = verification_context.env(); - let jni = JniUtils::new(env); - let reporter_instance = rep_glob_ref.as_obj(); - let reporter_wrapper = silver::reporter::PollingReporter::with(env); - let mut done = false; - while !done { - while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { - let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); - match jni.class_name(msg).as_str() { - "viper.silver.reporter.QuantifierInstantiationsMessage" => { - let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); - let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); - let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); - // TODO: find out which more quantifiers are derived from the user - // quantifiers - info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); - // also matches the "-aux" quantifiers generated - // TODO: some positions have just the id 0 and cannot be denormalized... - if q_name.starts_with("quant_with_posID") { - let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); - let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); - let parsed = stripped.parse::(); - match parsed { - Ok(pos_id) => { - let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); - yield ServerMessage::QuantifierInstantiation(q_name, q_inst, norm_pos_id); - () - } - _ => info!("Unexpected quantifier name {}", q_name) + let reporter_instance = rep_glob_ref.as_obj(); + let reporter_wrapper = silver::reporter::PollingReporter::with(env); + let mut done = false; + while !done { + while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { + let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); + match jni.class_name(msg).as_str() { + "viper.silver.reporter.QuantifierInstantiationsMessage" => { + let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); + let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); + let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); + // TODO: find out which more quantifiers are derived from the user + // quantifiers + info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); + // also matches the "-aux" quantifiers generated + // TODO: some positions have just the id 0 and cannot be denormalized... + if q_name.starts_with("quant_with_posID") { + let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); + let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); + let parsed = stripped.parse::(); + match parsed { + Ok(pos_id) => { + let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); + sender_clone.send(ServerMessage::QuantifierInstantiation{q_name: q_name, insts: u64::try_from(q_inst).unwrap(), norm_pos_id: norm_pos_id}).unwrap(); } + _ => info!("Unexpected quantifier name {}", q_name) } } - _ => () } - } - if !thread_rx.try_recv().is_err() { - info!("Polling thread received termination signal!"); - done = true; - } else { - thread::sleep(time::Duration::from_millis(10)); + _ => () } } - }); - stopwatch.start_next("verification"); - result = verifier.verify(viper_program); - // send termination signal to polling thread - main_tx.send(()).unwrap(); - // FIXME: here the global ref is dropped from a detached thread - polling_thread.join().unwrap(); + if !thread_rx.try_recv().is_err() { + info!("Polling thread received termination signal!"); + done = true; + } else { + thread::sleep(time::Duration::from_millis(10)); + } + } }); + stopwatch.start_next("verification"); + result = verifier.verify(viper_program); + // send termination signal to polling thread + main_tx.send(()).unwrap(); + // FIXME: here the global ref is dropped from a detached thread + polling_thread.join().unwrap(); + }); - // Don't cache Java exceptions, which might be due to misconfigured paths. - if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { - info!( - "Storing new cached result {:?} for program {}", - &result, - request.program.get_name() - ); - cache.insert(hash, result.clone()); - } + // Don't cache Java exceptions, which might be due to misconfigured paths. + if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { + info!( + "Storing new cached result {:?} for program {}", + &result, + request.program.get_name() + ); + cache.insert(hash, result.clone()); + } - normalization_info.denormalize_result(&mut result); - yield ServerMessage::Termination(result); - }) - } + normalization_info.denormalize_result(&mut result); + sender.send(ServerMessage::Termination(result)).unwrap(); + }) } fn dump_viper_program(ast_utils: &viper::AstUtils, program: viper::Program, program_name: &str) { diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index 9cf6a75074f..40964b0fc35 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -4,17 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::{process_verification_request, VerificationRequest, ServerMessage}; -use log::{info, error}; -use prusti_common::{config, Stopwatch}; +use crate::{VerificationRequestProcessing}; +use log::info; use std::{ net::{Ipv4Addr, SocketAddr}, - sync::{mpsc, Arc, Mutex}, + sync::mpsc, thread, }; use tokio::runtime::Builder; -use viper::{PersistentCache, Viper}; -use futures_util::{FutureExt, StreamExt, pin_mut}; +use futures_util::{pin_mut, StreamExt, SinkExt}; +use warp::Filter; +use once_cell::sync::Lazy; #[derive(Debug)] struct BincodeReject(bincode::Error); @@ -46,73 +46,45 @@ pub fn spawn_server_thread() -> SocketAddr { receiver.recv().unwrap() } +// initialize on first access +static VERIFICATION_REQUEST_PROCESSING: Lazy = Lazy::new(|| { VerificationRequestProcessing::new() }); + fn listen_on_port_with_address_callback(port: u16, address_callback: F) -> ! where F: FnOnce(SocketAddr), { - let stopwatch = Stopwatch::start("prusti-server", "JVM startup"); - let viper = Arc::new(Viper::new_with_args( - &config::viper_home(), - config::extra_jvm_args(), - )); - stopwatch.finish(); - - let cache_data = PersistentCache::load_cache(config::cache_path()); - let cache = Arc::new(Mutex::new(cache_data)); - let build_verification_request_handler = |viper_arc: Arc, cache| { - move |request: VerificationRequest| { - process_verification_request(&viper_arc, request, &cache); - } - }; - let json_verify = warp::path!("json" / "verify") .and(warp::filters::ws::ws()) .map(|ws: warp::filters::ws::Ws| { - ws.on_upgrade(|websocket| { - let (tx, rx) = websocket.split(); - let handle_req = build_verification_request_handler(viper.clone(), cache.clone()); - let handle_msg = |msg: warp::filters::ws::Message| { - match msg.to_str().and_then(|s| serde_json::from_str(s)) { - Ok(req) => handle_req(req), - Err(err) => error!("ERROR in json request: {}", err), - } - } - let stream = handle_msg(rx.next().await); + ws.on_upgrade(|websocket| async { + let (mut ws_send, mut ws_recv) = websocket.split(); + let req_msg = ws_recv.next().await.unwrap().unwrap(); + let verification_request = req_msg.to_str().and_then(|s: &str| serde_json::from_str(s).unwrap()).unwrap(); + let stream = VERIFICATION_REQUEST_PROCESSING.verify(verification_request); pin_mut!(stream); - while let Some(msg) = stream.next().await { - tx.send(warp::filters::ws::Message::text(serde_json::to_string(&msg))).await; + while let Some(server_msg) = stream.next().await { + ws_send.send(warp::filters::ws::Message::text(serde_json::to_string(&server_msg).unwrap())).await.unwrap(); } - tx.close().await; + ws_send.close().await.unwrap(); }) }); - ) - - let bincode_send_closure = |result| { - warp::http::Response::new( - bincode::serialize(&result).expect("could not encode verification result"), - )}; let bincode_verify = warp::path!("bincode" / "verify") .and(warp::filters::ws::ws()) .map(|ws: warp::filters::ws::Ws| { - ws.on_upgrade(|websocket| { - let (tx, rx) = websocket.split(); - let handle_req = build_verification_request_handler(viper.clone(), cache.clone()); - let handle_msg = |msg: warp::filters::ws::Message| { - match msg.as_bytes().and_then(|b| bincode::deserialize(&b)) { - Ok(req) => handle_req(req), - Err(err) => error!("ERROR in bincode request: {}", err), - } - } - let stream = handle_msg(rx.next().await); + ws.on_upgrade(|websocket| async { + let (mut ws_send, mut ws_recv) = websocket.split(); + let req_msg = ws_recv.next().await.unwrap().unwrap(); + let verification_request = bincode::deserialize(req_msg.as_bytes()).unwrap(); + let stream = VERIFICATION_REQUEST_PROCESSING.verify(verification_request); pin_mut!(stream); - while let Some(msg) = stream.next().await { - tx.send(warp::filters::ws::Message::bytes(bincode::serialize(&msg))).await; + while let Some(server_msg) = stream.next().await { + ws_send.send(warp::filters::ws::Message::binary(bincode::serialize(&server_msg).unwrap())).await.unwrap(); } - tx.close().await; + ws_send.close().await.unwrap(); }) }); - + /* let save_cache = warp::post() .and(warp::path("save")) .and(warp::path::end()) @@ -120,9 +92,8 @@ where cache.lock().unwrap().save(); warp::reply::html("Saved") }); - - let endpoints = json_verify.or(bincode_verify).or(save_cache); - +*/ + let endpoints = json_verify.or(bincode_verify); // Here we use a single thread because // 1. Viper is not thread safe yet (Silicon issue #578), and // 2. By default Silicon already uses as many cores as possible. diff --git a/prusti-server/src/server_message.rs b/prusti-server/src/server_message.rs index 2dd4a83cbd0..d39d4f06aa5 100644 --- a/prusti-server/src/server_message.rs +++ b/prusti-server/src/server_message.rs @@ -6,7 +6,7 @@ use viper::VerificationResult; -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Hash)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum ServerMessage { Termination(VerificationResult), QuantifierInstantiation { q_name: String, insts: u64, norm_pos_id: u64 }, diff --git a/prusti-viper/Cargo.toml b/prusti-viper/Cargo.toml index 588775c4ca1..ab267e6cdae 100644 --- a/prusti-viper/Cargo.toml +++ b/prusti-viper/Cargo.toml @@ -26,6 +26,8 @@ backtrace = "0.3" rustc-hash = "1.1.0" derive_more = "0.99.16" itertools = "0.10.3" +async-stream = "0.3.3" +futures-util = "0.3.25" [dev-dependencies] lazy_static = "1.4" diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index a4ace5ce307..fe82710c6be 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -19,18 +19,18 @@ use prusti_interface::environment::Environment; use prusti_interface::PrustiError; // use prusti_interface::specifications::TypedSpecificationMap; -use viper::{self, PersistentCache, Viper}; - +use viper; use prusti_interface::specs::typed; use ::log::{info, debug, error}; -use prusti_server::{VerificationRequest, PrustiClient, process_verification_request, spawn_server_thread, ViperBackendConfig}; +use prusti_server::{VerificationRequest, PrustiClient, VerificationRequestProcessing, spawn_server_thread, ViperBackendConfig, ServerMessage}; use prusti_rustc_interface::span::DUMMY_SP; use prusti_server::tokio::runtime::Builder; -use std::sync::Arc; use std::collections::HashMap; use serde_json::json; +use async_stream::stream; +use futures_util::{Stream, StreamExt, pin_mut}; // /// A verifier builder is an object that lives entire program's // /// lifetime, has no mutable state, and is responsible for constructing @@ -255,8 +255,6 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } self.encoder.process_encoding_queue(); - let encoding_errors_count = self.encoder.count_encoding_errors(); - let polymorphic_programs = self.encoder.get_viper_programs(); let mut programs: Vec = if config::simplify_encoding() { @@ -274,227 +272,156 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { stopwatch.start_next("verifying Viper program"); - let verification_requests = programs_to_requests(programs); - //TODO: not at all finished - if let Some(server_address) = config::server_address() { - let server_address = if server_address == "MOCK" { - spawn_server_thread().to_string() + let requests = self.programs_to_requests(programs); + + // runtime used either for client connecting to server sequentially + // or to sequentially verify the requests -> single thread is sufficient + // why single thread if everything is asynchronous? VerificationRequestProcessing in + // prusti-server/src/process_verification.rs only has a single thread and the underlying + // viper instance already uses as many cores as possible + let rt = Builder::new_current_thread() + .thread_name("prusti_viper_verifier_verify") + .enable_all() + .build() + .unwrap(); + + let mut overall_result = VerificationResult::Success; + rt.block_on(async { + overall_result = if let Some(server_address) = config::server_address() { + let verification_messages = Self::verify_requests_server(requests, server_address); + self.handle_stream(verification_messages).await } else { - server_address - }; - info!("Connecting to Prusti server at {}", server_address); - let client = PrustiClient::new(&server_address).unwrap_or_else(|error| { - panic!( - "Could not parse server address ({}) due to {:?}", - server_address, error - ) - }); - // Here we construct a Tokio runtime to block until completion of the futures returned by - // `client.verify`. However, to report verification errors as early as possible, - // `verify_programs` should return an asynchronous stream of verification results. - let runtime = Builder::new_current_thread() - .thread_name("prusti-viper") - .enable_all() - .build() - .expect("failed to construct Tokio runtime"); - verification_requests.map(|(program_name, request)| { - let remote_result = runtime.block_on(client.verify(request)); - let result = remote_result.unwrap_or_else(|error| { - panic!( - "Verification request of program {} failed: {:?}", - program_name, - error - ) - }); - (program_name, result) - }).collect() - } else { - let stopwatch = Stopwatch::start("prusti-viper", "JVM startup"); - let viper_arc = Arc::new(Viper::new_with_args(&config::viper_home(), config::extra_jvm_args())); - let mut cache = PersistentCache::load_cache(config::cache_path()); - stopwatch.finish(); - verification_requests.map(|(program_name, request)| { - let result = process_verification_request(&viper_arc, request, &mut cache); - (program_name, result) - }).collect() - } + let vrp = VerificationRequestProcessing::new(); + let verification_messages = Self::verify_requests_local(requests, &vrp); + self.handle_stream(verification_messages).await + } + }); stopwatch.finish(); + overall_result + } - // Group verification results - let mut verification_errors : Vec<_> = vec![]; - let mut consistency_errors : Vec<_> = vec![]; - let mut java_exceptions : Vec<_> = vec![]; - // the quantifier instantiations among different programs are independent and thus are - // aggregated - // (denormalized ID, name (with normalized ID)), num_instantiations for each file - let mut quantifier_instantiations: HashMap::<(u64, String), u64> = HashMap::new(); - for (method_name, result) in verification_results.into_iter() { - match result { - viper::VerificationResult::Success => { - info!("Result: Normal Success"); - } - viper::VerificationResult::ConsistencyErrors(errors) => { - info!("Result: ConsistencyErrors"); - for error in errors.into_iter() { - consistency_errors.push((method_name.clone(), error)); - } - } - viper::VerificationResult::Failure(errors) => { - info!("Result: Failure"); - for error in errors.into_iter() { - verification_errors.push((method_name.clone(), error)); - } - } - viper::VerificationResult::JavaException(exception) => { - info!("Result: JavaException"); - java_exceptions.push((method_name, exception)); - } - viper::VerificationResult::EnrichedSuccess(q_insts) => { - info!("Result: EnrichedSuccess"); - for (key, n) in q_insts { - match quantifier_instantiations.get(&key) { - Some(before) => { - quantifier_instantiations.insert(key, n+before); - () - }, - None => { - quantifier_instantiations.insert(key, n); - () - }, + async fn handle_stream(&self, verification_messages: impl Stream) -> VerificationResult { + let mut overall_result = VerificationResult::Success; + let encoding_errors_count = self.encoder.count_encoding_errors(); + let error_manager = self.encoder.error_manager(); + // we want quantifier_pos_ID + program_name + q_name as identifier because there are + // different q_names for the same ID and each program reports independent results + // key: (norm_pos_id, program_name), key to result: q_name result: num_instantiations + let mut quantifier_instantiations: HashMap::<(u64, String), HashMap::> = HashMap::new(); + + pin_mut!(verification_messages); + + while let Some((program_name, server_msg)) = verification_messages.next().await { + match server_msg { + ServerMessage::Termination(result) => match result { + // nothing to do + viper::VerificationResult::Success => (), + viper::VerificationResult::ConsistencyErrors(errors) => { + for error in errors { + PrustiError::internal( + format!("consistency error in {}: {}", program_name.clone(), error), DUMMY_SP.into() + ).emit(&self.env.diagnostic); } + overall_result = VerificationResult::Failure; } - } - viper::VerificationResult::EnrichedFailure(errors, q_insts) => { - info!("Result: EnrichedFailure"); - for (key, n) in q_insts { - match quantifier_instantiations.get(&key) { - Some(before) => { - quantifier_instantiations.insert(key, n+before); - () - }, - None => { - quantifier_instantiations.insert(key, n); - () - }, + viper::VerificationResult::Failure(errors) => { + for verification_error in errors { + debug!("Verification error in {}: {:?}", program_name.clone(), verification_error); + let mut prusti_error = error_manager.translate_verification_error(&verification_error); + + // annotate with counterexample, if requested + if config::counterexample() { + if config::unsafe_core_proof(){ + if let Some(silicon_counterexample) = &verification_error.counterexample { + if let Some(def_id) = error_manager.get_def_id(&verification_error) { + let counterexample = counterexample_translation_refactored::backtranslate( + &self.encoder, + error_manager.position_manager(), + def_id, + silicon_counterexample, + ); + prusti_error = counterexample.annotate_error(prusti_error); + } else { + prusti_error = prusti_error.add_note( + format!( + "the verifier produced a counterexample for {}, but it could not be mapped to source code", + program_name + ), + None, + ); + } + } + } else if let Some(silicon_counterexample) = &verification_error.counterexample { + if let Some(def_id) = error_manager.get_def_id(&verification_error) { + let counterexample = counterexample_translation::backtranslate( + &self.encoder, + def_id, + silicon_counterexample, + ); + prusti_error = counterexample.annotate_error(prusti_error); + } else { + prusti_error = prusti_error.add_note( + format!( + "the verifier produced a counterexample for {}, but it could not be mapped to source code", + program_name + ), + None, + ); + } + } + } + debug!("Prusti error: {:?}", prusti_error); + if prusti_error.is_disabled() { + prusti_error.cancel(); + } else { + prusti_error.emit(&self.env.diagnostic); + } } + overall_result = VerificationResult::Failure; } - for error in errors.into_iter() { - verification_errors.push((method_name.clone(), error)); - } - } - } - } - - let error_manager = self.encoder.error_manager(); - - // report the quantifier instantiations - if config::report_qi_profile() { - info!("Reporting qi_profile"); - for ((pos_id, q_name), n) in quantifier_instantiations { - match error_manager.position_manager().get_span_from_id(pos_id) { - Some(span) => { - PrustiError::message( - format!("quantifier_instantiations_message{}", - json!({"q_name": q_name, "instantiations": n}), - ), span.clone() + viper::VerificationResult::JavaException(exception) => { + error!("Java exception: {}", exception.get_stack_trace()); + PrustiError::internal( + format!("in {}: {}", program_name.clone(), exception), DUMMY_SP.into() ).emit(&self.env.diagnostic); - }, - None => info!("Quantifier instantiations for unknown position id {} {} {}", q_name, n, pos_id), + overall_result = VerificationResult::Failure; + } } - } - } - - // Convert verification results to Prusti errors - let mut result = VerificationResult::Success; - - for (method, error) in consistency_errors.into_iter() { - PrustiError::internal( - format!("consistency error in {}: {}", method, error), DUMMY_SP.into() - ).emit(&self.env.diagnostic); - result = VerificationResult::Failure; - } - - for (method, exception) in java_exceptions.into_iter() { - error!("Java exception: {}", exception.get_stack_trace()); - PrustiError::internal( - format!("in {}: {}", method, exception), DUMMY_SP.into() - ).emit(&self.env.diagnostic); - result = VerificationResult::Failure; - } - - // Report verification errors - let mut prusti_errors: Vec<_> = vec![]; - for (method, verification_error) in verification_errors.into_iter() { - debug!("Verification error in {}: {:?}", method, verification_error); - let mut prusti_error = error_manager.translate_verification_error(&verification_error); - - // annotate with counterexample, if requested - if config::counterexample() { - if config::unsafe_core_proof(){ - if let Some(silicon_counterexample) = &verification_error.counterexample { - if let Some(def_id) = error_manager.get_def_id(&verification_error) { - let counterexample = counterexample_translation_refactored::backtranslate( - &self.encoder, - error_manager.position_manager(), - def_id, - silicon_counterexample, - ); - prusti_error = counterexample.annotate_error(prusti_error); - } else { - prusti_error = prusti_error.add_note( - format!( - "the verifier produced a counterexample for {}, but it could not be mapped to source code", - method - ), - None, - ); + ServerMessage::QuantifierInstantiation{q_name, insts, norm_pos_id} => { + if config::report_qi_profile() { + match error_manager.position_manager().get_span_from_id(norm_pos_id) { + Some(span) => { + let key = (norm_pos_id, program_name.clone()); + if !quantifier_instantiations.contains_key(&key) { + quantifier_instantiations.insert(key.clone(), HashMap::new()); + } + let map = quantifier_instantiations.get_mut(&key).unwrap(); + // this replaces the old entry which is exactly what we want + map.insert(q_name, insts); + let mut n: u64 = 0; + for (_, insts) in map { + n += *insts; + } + PrustiError::message( + format!("quantifier_instantiations_message{}", + json!({"instantiations": n, "method": program_name}), + ), span.clone() + ).emit(&self.env.diagnostic); + }, + None => info!("#{} quantifier instantiations of {} for unknown position id {} in verification of {}", insts, q_name, norm_pos_id, program_name), } } - } else if let Some(silicon_counterexample) = &verification_error.counterexample { - if let Some(def_id) = error_manager.get_def_id(&verification_error) { - let counterexample = counterexample_translation::backtranslate( - &self.encoder, - def_id, - silicon_counterexample, - ); - prusti_error = counterexample.annotate_error(prusti_error); - } else { - prusti_error = prusti_error.add_note( - format!( - "the verifier produced a counterexample for {}, but it could not be mapped to source code", - method - ), - None, - ); - } } } - - prusti_errors.push(prusti_error); - } - prusti_errors.sort(); - - for prusti_error in prusti_errors { - debug!("Prusti error: {:?}", prusti_error); - if prusti_error.is_disabled() { - prusti_error.cancel(); - } else { - prusti_error.emit(&self.env.diagnostic); - } - result = VerificationResult::Failure; } if encoding_errors_count != 0 { - result = VerificationResult::Failure; + overall_result = VerificationResult::Failure; } - - result + overall_result } - /// Returns a list of (program_name, verification_requests) tuples. - fn programs_to_requests(programs: Vec) - -> Vec<(String, VerificationRequest)> - { + fn programs_to_requests(&self, programs: Vec) -> Vec<(String, VerificationRequest)> { let source_path = self.env.name.source_path(); let rust_program_name = source_path .file_name() @@ -518,8 +445,36 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { }; (program_name, request) }); - verification_requests + verification_requests.collect() } + /// Returns a list of (program_name, verification_requests) tuples. + fn verify_requests_server(verification_requests: Vec<(String, VerificationRequest)>, server_address: String) + -> impl Stream + { + let server_address = if server_address == "MOCK" { + spawn_server_thread().to_string() + } else { + server_address + }; + info!("Connecting to Prusti server at {}", server_address); + let verification_stream = stream! { + for (program_name, request) in verification_requests { + yield PrustiClient::verify(server_address.clone(), request).await.map(move |msg| (program_name.clone(), msg)); + } + }; + verification_stream.flatten() + } + + fn verify_requests_local<'a>(verification_requests: Vec<(String, VerificationRequest)>, vrp: &'a VerificationRequestProcessing) + -> impl Stream + 'a + { + let verification_stream = stream! { + for (program_name, request) in verification_requests { + yield vrp.verify(request).map(move |msg| (program_name.clone(), msg)); + } + }; + return verification_stream.flatten(); + } } diff --git a/viper/src/verification_result.rs b/viper/src/verification_result.rs index 815e3779a85..ff57b543722 100644 --- a/viper/src/verification_result.rs +++ b/viper/src/verification_result.rs @@ -5,7 +5,6 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::{silicon_counterexample::SiliconCounterexample, JavaException}; -use std::collections::HashMap; /// The result of a verification request on a Viper program. #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] From 24ad83866c0ce4469718d48c275d0df99377341a Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sat, 4 Feb 2023 23:10:14 +0100 Subject: [PATCH 21/74] refactored extern spec generation to try and make it more readable. --- prusti/src/ide_helper/ide_info.rs | 15 +- prusti/src/ide_helper/query_signature.rs | 631 +++++++++++++---------- 2 files changed, 373 insertions(+), 273 deletions(-) diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/ide_info.rs index ca7c7f33c98..cbb7f8f07d6 100644 --- a/prusti/src/ide_helper/ide_info.rs +++ b/prusti/src/ide_helper/ide_info.rs @@ -1,11 +1,10 @@ +use super::{call_finder, query_signature}; use prusti_interface::environment::Environment; use prusti_rustc_interface::{ hir::def_id::DefId, span::{source_map::SourceMap, Span}, }; -use super::call_finder; -use super::query_signature; -use serde::{Serialize, ser::SerializeStruct}; +use serde::{ser::SerializeStruct, Serialize}; // create some struct storing all the information the IDE will ever need. // needs to be transformable into json! @@ -18,7 +17,6 @@ pub struct IdeInfo { // additionally this will contain: // function_calls: // ... we'll see - } impl IdeInfo { @@ -35,7 +33,7 @@ impl IdeInfo { .collect(); // For declaring external specifications: - let queried_source = query_signature::collect_queried_signatures(env, &fncalls); + let queried_source = query_signature::collect_queried_signature(env.tcx(), &fncalls); Self { procedure_defs: procs, function_calls: fncalls, @@ -53,8 +51,9 @@ pub struct ProcDef { // defid is not needed for VSCode and not serializable as of now.. impl Serialize for ProcDef { fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer { + where + S: serde::Serializer, + { let mut state = serializer.serialize_struct("ProcDef", 2)?; state.serialize_field("name", &self.name)?; state.serialize_field("span", &self.span)?; @@ -100,7 +99,7 @@ fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, DefId, Span)> { let mut fnvisitor = call_finder::CallSpanFinder::new(env); - // let mut adjusted_visitor = + // let mut adjusted_visitor = // fnvisitor.visit_body(hir_body); env.tcx() .hir() diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 8598ca9bf63..1698b630d47 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -1,318 +1,419 @@ -use core::fmt; use prusti_common::config; -use prusti_interface::environment::Environment; -use std::collections::HashMap; +use std::{collections::HashMap, fmt}; use super::ide_info::ProcDef; use prusti_rustc_interface::{ hir::{def::DefKind, def_id::DefId}, - middle::ty::{self, DefIdTree, Generics, ImplSubject, PredicateKind, TraitRef, Ty, TyCtxt, Clause}, + middle::ty::{self, Clause, DefIdTree, ImplSubject, PredicateKind, Ty, TyCtxt}, }; -pub fn collect_queried_signatures(env: &Environment<'_>, fncalls: &Vec) -> Option { - let def_path_str: String = config::query_method_signature()?; - println!( - "Got query for external specification template for : {}", - def_path_str - ); - let existing = fncalls - .iter() - .find(|procdef| procdef.name == def_path_str)?; +// data structure to represent what we want to generate +// since we didnt manage to this with syn etc.. - // helpers: - let tcx = env.tcx(); - let defid: DefId = existing.defid; +#[derive(Debug)] +enum ExternSpecBlock { + StandaloneFn { + parent_chain: String, + function_sig: FunctionSignature, + }, + Trait { + name: String, + path: String, + generics: Vec, + function_sig: FunctionSignature, + }, + Impl { + name: String, // in this case the name also includes + // the path + trait_name: Option, + generics: Vec, + function_sig: FunctionSignature, + }, +} - // check what kind of definition we are looking at: - - let def_kind = tcx.opt_def_kind(defid)?; - println!("DefKind: {:?}", def_kind); - match def_kind { - DefKind::Fn => { - // TODO - let parent_chain = get_parent_chain(defid, &tcx); - let signature_str = fn_signature_str(defid, &tcx); - let (fn_generics_str, _) = generic_params_str(defid, &tcx, false); - let item_name = tcx.opt_item_name(defid)?; - let contents = format!( - "use prusti_contracts::*;\nfn {}{}{};", - item_name, fn_generics_str, signature_str - ); - Some(wrap_in_parent_mods(parent_chain, contents)) - } - DefKind::AssocFn => { - let item_name_ident = tcx.opt_item_name(defid)?; - let item_name = item_name_ident.as_str(); - let impl_of_method_opt = tcx.impl_of_method(defid); - match impl_of_method_opt { - Some(impl_of_method) => { - println!("Impl of method : {:?}", impl_of_method); - let mut trait_name = None; - let impl_subj = tcx.impl_subject(impl_of_method); - let self_ty = match impl_subj { - ImplSubject::Trait(tref) => { - trait_name = Some(format!("{}", tref.print_only_trait_name())); - tref.self_ty() +impl ExternSpecBlock { + fn from_defid(tcx: TyCtxt<'_>, defid: DefId) -> Option { + let def_kind = tcx.opt_def_kind(defid)?; + let signature = FunctionSignature::from_defid(tcx, defid)?; + match def_kind { + DefKind::Fn => { + let parent_chain = get_parent_chain(defid, tcx); + Some(ExternSpecBlock::StandaloneFn { + parent_chain, + function_sig: signature, + }) + } + DefKind::AssocFn => { + // this will be None for traits + let impl_defid_opt = tcx.impl_of_method(defid); + match impl_defid_opt { + Some(impl_defid) => { + // function is part of impl block + let mut trait_name = None; + let impl_subj = tcx.impl_subject(impl_defid); + let self_ty = match impl_subj { + ImplSubject::Trait(tref) => { + trait_name = Some(format!("{}", tref.print_only_trait_name())); + tref.self_ty() + } + ImplSubject::Inherent(ty) => ty, } - ImplSubject::Inherent(ty) => ty, - }; - println!("Self Type: {:?}", self_ty); - - let signature_str = fn_signature_str(defid, &tcx); - let (impl_generics_str, _opt_where) = - generic_params_str(impl_of_method, &tcx, false); - let (fn_generics_str, _opt_where) = generic_params_str(defid, &tcx, false); - - // Generate result: - let mut res = "#[extern_spec]\nimpl".to_string(); - res.push_str(&format!("{} ", impl_generics_str)); - // if impl block is implementing a trait: - if let Some(traitname) = trait_name { - res.push_str(&traitname); - res.push_str(" for "); - } - //pub fn name(arguments) - res.push_str(&format!( - "{} {{\n\tpub fn {}{}{};\n}}", - self_ty, item_name, fn_generics_str, signature_str, - )); + .to_string(); + let generics = generic_params(tcx, impl_defid); - println!("Result: {}", res); + Some(ExternSpecBlock::Impl { + name: self_ty, + trait_name, + generics, + function_sig: signature, + }) + } + None => { + // function is a Trait (or something else?) + // are there other cases for AssocFns? + let parent = tcx.opt_parent(defid)?; + if tcx.is_trait(parent) { + // indeed a trait + let traitname = tcx.opt_item_name(parent)?.to_string(); + let parent_defpath = get_parent_chain(parent, tcx); + let trait_params = generic_params(tcx, parent); - Some(res) - } - None => { - // we are apparently dealing with a trait, or even something else - // This part is not working at all so far.. - let parent = tcx.opt_parent(defid)?; - - if tcx.is_trait(parent) { - println!("Yes indeed this is a trait"); - let traitname = tcx.def_path_str(parent); - let trait_params = trait_params(parent, tcx); - let (fn_generics, _where) = generic_params_str(defid, &tcx, false); - let fn_sig = fn_signature_str(defid, &tcx); - Some(format!( - "#[extern_spec]\ntrait {}{} {{\n\tfn {}{}{};\n}}", - traitname, trait_params, item_name, fn_generics, fn_sig, - )) - } else { - println!("Nope it's not actually a trait, check this case"); - None + Some(ExternSpecBlock::Trait { + name: traitname, + path: parent_defpath, + generics: trait_params, + function_sig: signature, + }) + } else { + None + } } } } + _ => { + // another case to be handled? + None + } } - // as far as I can tell this is all that's supported for extern_spec - _ => { - println!("Apparently not an associated function to some impl block.."); - None + } +} + +impl fmt::Display for ExternSpecBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ExternSpecBlock::Impl { + name, + trait_name, + generics, + function_sig, + } => { + let generics_str = generic_args_str(&generics, false); + let where_block = bounds_where_block(&generics); + + write!(f, "#[extern_spec]\nimpl{} ", generics_str)?; + if let Some(trait_name) = trait_name { + write!(f, "{} for ", trait_name)?; + } + write!(f, "{} {}{{\n", name, where_block)?; + let fn_sig = indent(&function_sig.to_string()); + write!(f, "{}\n}}", fn_sig) + } + ExternSpecBlock::Trait { + name, + path, + generics, + function_sig, + } => { + let fn_sig = indent(&function_sig.to_string()); + let generics_str = generic_args_str(&generics, false); + let where_block = bounds_where_block(&generics); + // do traits specify traitbounds too? + write!(f, "#[extern_spec({})]\n", path)?; + write!(f, "trait {}{} {}{{\n", name, generics_str, where_block)?; + write!(f, "{}\n}}", fn_sig) + } + ExternSpecBlock::StandaloneFn { + parent_chain, + function_sig, + } => { + write!(f, "#[extern_spec({})]\n", parent_chain)?; + write!(f, "{}", function_sig) + } } } } -// by signature we mean the arguments + return type -pub fn fn_signature_str(defid: DefId, tcx: &TyCtxt<'_>) -> String { - let signature = tcx.fn_sig(defid).skip_binder(); - let inputs = signature.inputs().iter(); - let arg_names = tcx.fn_arg_names(defid); +#[derive(Debug, Clone)] +struct GenericArg { + name: String, + default_value: Option, + bounds: Vec, + projections: HashMap, +} - let output = signature.output(); +impl fmt::Display for GenericArg { + // default formatter will include the traitbounds, + // otherwise just take .name field.. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let bounds_str = traitbounds_string(&self.bounds); + write!(f, "{}: {}", self.name, bounds_str) + // for now we ignore defaults since prusti doesnt accept them in + // some cases.. + // if self.default_value.is_some() { + // write!(f, "={}", self.default_value.as_ref().unwrap())?; + // } + } +} - let args = arg_names +// the string for the where clause +fn bounds_where_block(arglist: &Vec) -> String { + let bounds_vec = arglist .iter() - .zip(inputs) - .map(|(name, ty)| format!("{}: {}", name.as_str(), ty)) - .collect::>() - .join(", "); - format!("({}) -> {}", args, output) + .filter(|arg| arg.bounds.len() > 0) + .map(|arg| format!("\t{}: {}", arg.name, traitbounds_string(&arg.bounds))) + .collect::>(); + if bounds_vec.len() > 0 { + format!("where\n{}\n", bounds_vec.join(",\n")) + } else { + "".to_string() + } } -pub fn trait_params(defid: DefId, tcx: TyCtxt<'_>) -> String { - let generic_params = tcx.generics_of(defid); - let substs = ty::subst::InternalSubsts::identity_for_item(tcx, defid); +fn generic_args_str(arglist: &Vec, include_bounds: bool) -> String { + if arglist.len() > 0 { + let res = arglist + .iter() + .map(|genarg| { + if include_bounds { + genarg.to_string() + } else { + genarg.name.clone() + } + }) // if we wanted to include defaults this should + // probably happen here + .collect::>() + .join(", "); + format!("<{}>", res) + } else { + "".to_string() + } +} - let result = generic_params - .params - .iter() - .filter(|p| (*p).default_value(tcx).is_some()) // types like Self will show up here,but can't be in code, - .map(|p| format!("{}={:?}", p.name, p.default_value(tcx).unwrap().subst(tcx, substs))) - .collect::>() - .join(", "); - if generic_params.params.iter().len() > 0 { - format!("<{}>", result) +// example result: Sized + PartialEq + Eq +fn traitbounds_string(boundlist: &Vec) -> String { + if boundlist.len() > 0 { + let res = boundlist + .iter() + .map(|bound| bound.to_string()) + .collect::>() + .join(" + "); + res } else { "".to_string() } } -enum GenArguments<'a> { - GenType(Ty<'a>), - Projection(String, String), +#[derive(Debug, Clone)] +struct TraitBound { + name: String, + args: Vec, } -impl<'a> fmt::Display for GenArguments<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::GenType(ty) => { - write!(f, "{}", ty) - } - Self::Projection(item, value) => { - write!(f, "{}={}", item, value) - } +impl fmt::Display for TraitBound { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name)?; // todo + if self.args.len() >= 1 { + let args_str = self.args.join(", "); + write!(f, "<{}>", args_str)?; + } + Ok(()) + } +} + + +#[derive(Debug)] +struct FunctionSignature { + name: String, + generics: Vec, + arguments: Vec<(String, String)>, // (name, type) + return_type: Option, +} + +impl FunctionSignature { + fn from_defid(tcx: TyCtxt<'_>, defid: DefId) -> Option { + let name = tcx.opt_item_name(defid)?.to_string(); + let sig = tcx.fn_sig(defid).skip_binder(); + let arg_types = sig.inputs().iter(); + let arg_names = tcx.fn_arg_names(defid); + let output = sig.output(); + let return_type = if output.is_unit() { None } else { Some(output.to_string()) }; + + let arguments: Vec<(String, String)> = arg_names + .iter() + .zip(arg_types) + .map(|(name, ty)| (name.to_string(), ty.to_string())) + .collect(); + + let generics = generic_params(tcx, defid); + Some(Self { + name, + generics, + arguments, + return_type, + }) + } + + fn arguments_string(&self) -> String { + let args = self.arguments + .iter() + .map(|(name, ty)| format!("{}: {}", name.to_string(), ty)) + .collect::>() + .join(", "); + format!("({})", args) + } +} + +impl fmt::Display for FunctionSignature { + // fn(args) -> output where + // bounds; + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let generics_str = generic_args_str(&self.generics, true); + // let where_block = bounds_where_block(&self.generics); + let args_str = self.arguments_string(); + + write!(f, "fn {}{}{}", self.name, generics_str, args_str)?; + if let Some(ret_ty) = self.return_type.clone() { + write!(f, " -> {}", ret_ty)?; } + write!(f, ";") } + } -pub fn generic_params_str( - defid: DefId, // defid of a function or impl block - tcx: &TyCtxt<'_>, - exclude_bounds_to_where: bool, // whether or not trait bounds should be - // moved into an extra "where" block -) -> (String, Option) { - // should receive defid of either an impl block or a function definition. - // should return a string of the form <'a, S, T> depending on generic parameters. - let generic_params = tcx.generics_of(defid); - let traitref = tcx.predicates_of(defid); - println!("Traitref: {:?}", traitref); - - let mut traitbounds: HashMap>> = HashMap::new(); - // an example entry: impl + Trait2<..>> - // (or what it represents) - - let mut projections: Vec<(String, DefId, DefId, String)> = vec![]; - for (predicate, _) in traitref.predicates { - println!("Found predicate of kind: {:?}", predicate.kind()); - let kind: PredicateKind = predicate.kind().skip_binder(); // i don't understand this... + +fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { + let generics = tcx.generics_of(defid); + let mut generic_params: HashMap = HashMap::new(); + let substs = ty::subst::InternalSubsts::identity_for_item(tcx, defid); + + // first we just collect all generic parameters + for param in generics.params.iter() { + let ident = param.name.to_string(); + if ident == "Self" { continue } + let mut generic_arg = GenericArg { + name: ident.clone(), + default_value: None, + bounds: vec![], + projections: HashMap::new(), + }; + let default_opt = param.default_value(tcx); + if let Some(default_value) = default_opt { + generic_arg.default_value = Some(format!("{}", default_value.subst(tcx, substs))); + } + + generic_params.insert(ident, generic_arg); + } + + // now add traitbounds and default types: + let predicates = tcx.predicates_of(defid); + + for (predicate, _) in predicates.predicates { + let kind: PredicateKind = predicate.kind().skip_binder(); match kind { PredicateKind::Clause(Clause::Trait(t)) => { - println!("PredicateKind::Trait(t) with t: {:?}", t); - let traitref = t.trait_ref; - let self_ty_str = format!("{}", traitref.self_ty()); - - let trait_args_opt = traitref.substs.try_as_type_list(); - let trait_args: Vec = vec![]; - if let Some(typelist) = trait_args_opt { - if trait_args.len() > 1 { - let mut trait_args: Vec = vec![]; - for i in 1..typelist.len() { - // the first one is self - trait_args.push(GenArguments::GenType(typelist[i])); - } - } - } - match traitbounds.get_mut(&self_ty_str) { - None => { - traitbounds - .insert(self_ty_str, HashMap::from([(traitref.def_id, trait_args)])); - } - Some(trait_vec) => { - trait_vec.insert(traitref.def_id, trait_args); - } - } + let bound_traitref = t.trait_ref; + let trait_name = tcx.def_path_str(bound_traitref.def_id); + let self_ty = format!("{}", bound_traitref.self_ty()); + if self_ty == "Self" { continue } + let trait_args_opt = bound_traitref.substs.try_as_type_list(); + let trait_args = if let Some(typelist) = trait_args_opt { + typelist + .iter() + .skip(1) // the first one is the self type + .map(|ty| format!("{}", ty)) + .collect::>() + } else { + vec![] + }; + let bound = TraitBound { + name: trait_name, + args: trait_args, + }; + // add this bound to the given type. + generic_params + .get_mut(&self_ty) + .unwrap() // is failing appropriate? + .bounds + .push(bound); } + // not sure if we should even handle this: PredicateKind::Clause(Clause::Projection(p)) => { - // for example, for impl>, - // - item_id identifies Output, - // - trait_defid: Trait - // - term: the type assigned to Ouptut - // - self_ty: The first T - // (not very sure about the last 2) let item_id = p.projection_ty.def_id; - let self_ty_str = format!("{}", p.projection_ty.self_ty()); + let self_ty = format!("{}", p.projection_ty.self_ty()); + let trait_defid: DefId = p.projection_ty.trait_def_id(tcx); + let trait_defpath = tcx.def_path_str(trait_defid); - let trait_defid: DefId = p.projection_ty.trait_def_id(*tcx); // I want the identifier e.g. std::ops::Add - let term = format!("{}", p.term); // the value to be assigned as default value - projections.push((self_ty_str, trait_defid, item_id, term)); + let item_name = tcx.item_name(item_id); + + let projection_term = format!("{}={}", item_name, p.term); + let genarg = generic_params + .get_mut(&self_ty) + .unwrap(); + // not sure if this is actually completely correct. + // can a single generic type have the same traitbound + // more than once with different arguments? + // I would imagine yes.. may be a todo + genarg.projections.insert(trait_defpath, projection_term); } - _ => { - println!("Encountered PredicateKind {:?} but we dont handle it", kind); + _ => {} + } + } + + // handling projections here is simpler than when printing: + // could not do this directly since projections + for el in generic_params.values_mut() { + for tb in &mut el.bounds { + if let Some(projection_term) = el.projections.get(&tb.name) { + tb.args.push(projection_term.clone()); } } } - // Map the Projections into the other data-structure: - for (gen_ty_str, trait_id, item_id, value) in projections { - let bounds_list = traitbounds.get_mut(&gen_ty_str).unwrap(); - let gen_params_list = bounds_list.get_mut(&trait_id).unwrap(); - let item_name = format!("{}", tcx.item_name(item_id)); - gen_params_list.push(GenArguments::Projection(item_name, value)); - } - // Stringify - let count = generic_params.params.iter().len(); - // there is a field generic_params.count() but apparently it does not - // give us the same value, not sure what it stands for - if count > 0 { - let result = generic_params - .params // iterate through all parameters - .iter() - .map(|param_ident| { - let param = param_ident.name.as_str(); - let traitboundmap_opt = traitbounds.get(param); - if let Some(traitboundmap) = traitboundmap_opt { - let trait_list_str = traitboundmap - .iter() - .map(|(trait_id, param_list)| { - let trait_str = tcx.def_path_str(*trait_id); - if param_list.len() <= 0 { - trait_str - } else { - let param_str = param_list - .iter() - .map(|gp| format!("{}", gp)) - .collect::>() - .join(", "); - format!("{trait_str}<{param_str}>") - } - }) - .collect::>() - .join(" + "); - format!("{param}: {trait_list_str}") - } else { - param.to_string() - } - }) - .collect::>() - .join(", "); - (format!("<{result}>"), None) - } else { - ("".to_string(), None) - } + generic_params.values().cloned().collect() } -fn get_parent_chain(defid: DefId, tcx: &TyCtxt<'_>) -> Vec { - let mut result = vec![]; - let mut parent_opt = tcx.opt_parent(defid); - while let Some(parent_id) = parent_opt { - let name = tcx.item_name(parent_id).to_string(); - result.push(name); - parent_opt = tcx.opt_parent(parent_id); // check for parent of parent - } - println!("Get Parent Chain: {:?}", result); - result.reverse(); - result +pub fn collect_queried_signature(tcx: TyCtxt<'_>, fncalls: &Vec) -> Option { + let def_path_str: String = config::query_method_signature()?; + let existing = fncalls + .iter() + .find(|procdef| procdef.name == def_path_str)?; + + // helpers: + let defid: DefId = existing.defid; + let extern_spec_block = ExternSpecBlock::from_defid(tcx, defid); + return extern_spec_block.map(|x| x.to_string()); +} + +fn get_parent_chain(defid: DefId, tcx: TyCtxt<'_>) -> String { + // let mut parent_opt = tcx.opt_parent(defid); + // this (above) apparently doesn't work. E.g. for std::ops::Add + // it will return std::ops::arith which is problematic + let defpath_str = tcx.def_path_str(defid); + let mut defpath: Vec<&str> = defpath_str.split("::").collect(); + defpath.pop(); // drop the name itself + defpath.join("::") } -fn wrap_in_parent_mods(parent_chain: Vec, contents: String) -> String { - let mut tab_level = 0; - let chain_length = parent_chain.len(); - let mut result = "#[extern_spec]\n".to_string(); - for parent_mod in &parent_chain { - result.push_str(&"\t".repeat(tab_level)); - result.push_str(&format!("mod {} {{\n", parent_mod)); - tab_level += 1; - } - result.push_str( - &contents - .split('\n') - .map(|line| "\t".repeat(tab_level) + line) - .collect::>() - .join("\n"), - ); // appends the indented contents of the mod - for _ in 0..chain_length { - tab_level -= 1; - result.push('\n'); - result.push_str(&"\t".repeat(tab_level)); - result.push('}'); // close the curly braces at the right tab_level +fn indent(input: &String) -> String { + // indent all lines that are not empty by one tab + let mut res = String::from("\t"); + let len = input.len(); + for (i, c) in input.chars().enumerate() { + res.push(c); + if c == '\n' && i != len - 1 { + // insert a tab after every newline that is not terminating + // the string + res.push('\t'); + } } - result + res } From 5d141eb30b4d945bcc963f0bddfab9935d6ec7ee Mon Sep 17 00:00:00 2001 From: hegglinc Date: Tue, 7 Feb 2023 17:17:38 +0100 Subject: [PATCH 22/74] renamed stuff --- prusti/src/callbacks.rs | 6 +++--- prusti/src/ide_helper/call_finder.rs | 13 +++++++------ .../ide_helper/{ide_info.rs => compiler_info.rs} | 0 prusti/src/ide_helper/mod.rs | 2 +- prusti/src/ide_helper/query_signature.rs | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) rename prusti/src/ide_helper/{ide_info.rs => compiler_info.rs} (100%) diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index c80974c341c..48b1aa5b98c 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -1,5 +1,5 @@ use crate::{ - ide_helper::{fake_error::fake_error, ide_info}, + ide_helper::{fake_error::fake_error, compiler_info}, verifier::verify, }; use prusti_common::config; @@ -123,8 +123,8 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // part of verification_task if config::show_ide_info() && !config::no_verify() { - let ideinf = ide_info::IdeInfo::collect(&env, &annotated_procedures); - let out = serde_json::to_string(&ideinf).unwrap(); + let compiler_info = compiler_info::IdeInfo::collect(&env, &annotated_procedures); + let out = serde_json::to_string(&compiler_info).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... println!("{}", out); diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 3ad3a73535e..ef6761b43e1 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -55,13 +55,14 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { match expr.kind { ExprKind::Call(e1, _e2) => { println!("found a call: resolving!"); + if let ExprKind::Path(ref qself) = e1.kind { let tyck_res = self.tcx.typeck(e1.hir_id.owner.def_id); let res = tyck_res.qpath_res(qself, e1.hir_id); if let prusti_rustc_interface::hir::def::Res::Def(_, def_id) = res { - let defpath = self.tcx.def_path_debug_str(def_id); + let defpath = self.tcx.def_path_str(def_id); println!("Call DefPath: {}", defpath); - self.called_functions.push((defpath, def_id, expr.span)) + self.called_functions.push((defpath, def_id, expr.span)); } else { println!("Resolving a call failed!\n\n\n"); } @@ -74,8 +75,8 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { match resolve_res { Ok((method_def_id, resolved_def_id)) => { let _is_local = method_def_id.as_local().is_some(); - let defpath_unresolved = self.tcx.def_path_debug_str(method_def_id); - let defpath_resolved = self.tcx.def_path_debug_str(resolved_def_id); + let defpath_unresolved = self.tcx.def_path_str(method_def_id); + let defpath_resolved = self.tcx.def_path_str(resolved_def_id); if true { // TODO: replace with is_local once we are not debugging anymore @@ -98,8 +99,8 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { match resolve_res { Ok((method_def_id, resolved_def_id)) => { let _is_local = method_def_id.as_local().is_some(); - let defpath_unresolved = self.tcx.def_path_debug_str(method_def_id); - let defpath_resolved = self.tcx.def_path_debug_str(resolved_def_id); + let defpath_unresolved = self.tcx.def_path_str(method_def_id); + let defpath_resolved = self.tcx.def_path_str(resolved_def_id); if true { // TODO: replace with is_local once we are not debugging anymore diff --git a/prusti/src/ide_helper/ide_info.rs b/prusti/src/ide_helper/compiler_info.rs similarity index 100% rename from prusti/src/ide_helper/ide_info.rs rename to prusti/src/ide_helper/compiler_info.rs diff --git a/prusti/src/ide_helper/mod.rs b/prusti/src/ide_helper/mod.rs index d3e95a45c7e..df7bd8b4dfb 100644 --- a/prusti/src/ide_helper/mod.rs +++ b/prusti/src/ide_helper/mod.rs @@ -1,4 +1,4 @@ -pub mod ide_info; +pub mod compiler_info; mod call_finder; pub mod fake_error; mod query_signature; diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 1698b630d47..0a3c8b43227 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -1,7 +1,7 @@ use prusti_common::config; use std::{collections::HashMap, fmt}; -use super::ide_info::ProcDef; +use super::compiler_info::ProcDef; use prusti_rustc_interface::{ hir::{def::DefKind, def_id::DefId}, middle::ty::{self, Clause, DefIdTree, ImplSubject, PredicateKind, Ty, TyCtxt}, From bb6ddb23988aa90626e8e988e63d8947e7627f78 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Wed, 8 Feb 2023 13:20:08 +0100 Subject: [PATCH 23/74] Adjusted viper VerificationResult so we can passe times, and whether is was cached to clients. --- .../src/vir/program_normalization.rs | 6 +- prusti-server/src/process_verification.rs | 21 ++-- prusti-viper/src/ide/mod.rs | 1 + prusti-viper/src/ide/verification_summary.rs | 47 ++++++++ prusti-viper/src/lib.rs | 1 + prusti-viper/src/verifier.rs | 111 ++++++++++-------- prusti/src/callbacks.rs | 2 +- prusti/src/ide_helper/mod.rs | 2 +- viper/src/verification_result.rs | 26 +++- viper/src/verifier.rs | 12 +- 10 files changed, 158 insertions(+), 71 deletions(-) create mode 100644 prusti-viper/src/ide/mod.rs create mode 100644 prusti-viper/src/ide/verification_summary.rs diff --git a/prusti-common/src/vir/program_normalization.rs b/prusti-common/src/vir/program_normalization.rs index ab767466ef4..bea3001d397 100644 --- a/prusti-common/src/vir/program_normalization.rs +++ b/prusti-common/src/vir/program_normalization.rs @@ -7,7 +7,7 @@ use crate::vir::{program::Program, Position}; use log::{debug, trace}; use rustc_hash::{FxHashMap, FxHashSet}; -use viper::VerificationResult; +use viper::VerificationResultType; #[derive(Clone)] pub enum NormalizationInfo { @@ -102,8 +102,8 @@ impl NormalizationInfo { } /// Denormalize a verification result. - pub fn denormalize_result(&self, result: &mut VerificationResult) { - if let VerificationResult::Failure(ref mut ver_errors) = result { + pub fn denormalize_result(&self, result: &mut VerificationResultType) { + if let VerificationResultType::Failure(ref mut ver_errors) = result { ver_errors.iter_mut().for_each(|ver_error| { if let Some(pos) = ver_error.pos_id.as_mut() { self.denormalize_position_string(pos); diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index bee20716e2d..39f976dd2bc 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -14,7 +14,7 @@ use prusti_common::{ }; use std::{fs::create_dir_all, path::PathBuf, thread, sync::{mpsc, Arc, self}}; use viper::{ - smt_manager::SmtManager, PersistentCache, Cache, VerificationBackend, VerificationResult, Viper, VerificationContext + smt_manager::SmtManager, PersistentCache, Cache, VerificationBackend, VerificationResult, VerificationResultType, Viper, VerificationContext }; use viper_sys::wrappers::viper::*; use std::time; @@ -141,7 +141,7 @@ pub fn process_verification_request( let _ = build_or_dump_viper_program(); }); } - sender.send(ServerMessage::Termination(viper::VerificationResult::Success)).unwrap(); + sender.send(ServerMessage::Termination(VerificationResult::dummy_success())).unwrap(); return; } @@ -158,7 +158,8 @@ pub fn process_verification_request( let _ = build_or_dump_viper_program(); }); } - normalization_info.denormalize_result(&mut result); + result.cached = true; + normalization_info.denormalize_result(&mut result.result_type); sender.send(ServerMessage::Termination(result)).unwrap(); return; } @@ -174,7 +175,12 @@ pub fn process_verification_request( let mut verifier = new_viper_verifier(program_name, &verification_context, request.backend_config); - let mut result = VerificationResult::Success; + let mut result = VerificationResult{ + item_name : program_name.to_string(), + result_type: VerificationResultType::Success, + cached: false, + time_ms: 0 + }; let normalization_info_clone = normalization_info.clone(); let sender_clone = sender.clone(); @@ -234,7 +240,8 @@ pub fn process_verification_request( } }); stopwatch.start_next("verification"); - result = verifier.verify(viper_program); + result.result_type = verifier.verify(viper_program); + result.time_ms = stopwatch.finish().as_millis(); // send termination signal to polling thread main_tx.send(()).unwrap(); // FIXME: here the global ref is dropped from a detached thread @@ -242,7 +249,7 @@ pub fn process_verification_request( }); // Don't cache Java exceptions, which might be due to misconfigured paths. - if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { + if config::enable_cache() && !matches!(result.result_type, VerificationResultType::JavaException(_)) { info!( "Storing new cached result {:?} for program {}", &result, @@ -251,7 +258,7 @@ pub fn process_verification_request( cache.insert(hash, result.clone()); } - normalization_info.denormalize_result(&mut result); + normalization_info.denormalize_result(&mut result.result_type); sender.send(ServerMessage::Termination(result)).unwrap(); }) } diff --git a/prusti-viper/src/ide/mod.rs b/prusti-viper/src/ide/mod.rs new file mode 100644 index 00000000000..2df6ce4c269 --- /dev/null +++ b/prusti-viper/src/ide/mod.rs @@ -0,0 +1 @@ +pub mod verification_summary; diff --git a/prusti-viper/src/ide/verification_summary.rs b/prusti-viper/src/ide/verification_summary.rs new file mode 100644 index 00000000000..e42a0c92906 --- /dev/null +++ b/prusti-viper/src/ide/verification_summary.rs @@ -0,0 +1,47 @@ +use viper::VerificationResult; +use serde::Serialize; + +#[derive(Serialize)] +pub struct VerificationSummary { + result_list: Vec +} + +impl VerificationSummary { + pub fn new() -> Self { + Self { + result_list: vec![], + } + } + pub fn add(&mut self, res: &VerificationResult) { + let ide_res = IdeVerificationResult::from_res(res); + self.result_list.push(ide_res); + } + pub fn to_json_string(self) -> String { + serde_json::to_string(&self).unwrap() + } +} + +// since the existing verification result +// is not as trivially passed in json +#[derive(Serialize)] +struct IdeVerificationResult { + item_name: String, + success: bool, + time_ms: u128, + cached: bool +} + +impl IdeVerificationResult { + pub fn from_res(res: &VerificationResult) -> Self { + match res { + _ => { + Self { + item_name: res.item_name.clone(), + success: res.is_success(), + time_ms: res.time_ms, + cached: res.cached, + } + } + } + } +} diff --git a/prusti-viper/src/lib.rs b/prusti-viper/src/lib.rs index f423994ada7..b4a2a348a09 100644 --- a/prusti-viper/src/lib.rs +++ b/prusti-viper/src/lib.rs @@ -26,3 +26,4 @@ pub mod encoder; mod utils; pub mod verifier; +mod ide; diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 10d9a4e99c6..55eed4dc6af 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -12,6 +12,7 @@ use vir_crate::common::check_mode::CheckMode; use crate::encoder::Encoder; use crate::encoder::counterexamples::counterexample_translation; use crate::encoder::counterexamples::counterexample_translation_refactored; +use crate::ide; use prusti_interface::data::VerificationResult; use prusti_interface::data::VerificationTask; use prusti_interface::environment::Environment; @@ -132,35 +133,55 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { // different q_names for the same ID and each program reports independent results // key: (norm_pos_id, program_name), key to result: q_name result: num_instantiations let mut quantifier_instantiations: HashMap::<(u64, String), HashMap::> = HashMap::new(); - + let mut verification_summary = ide::verification_summary::VerificationSummary::new(); pin_mut!(verification_messages); + while let Some((program_name, server_msg)) = verification_messages.next().await { match server_msg { - ServerMessage::Termination(result) => match result { - // nothing to do - viper::VerificationResult::Success => (), - viper::VerificationResult::ConsistencyErrors(errors) => { - for error in errors { - PrustiError::internal( - format!("consistency error in {program_name}: {error}"), DUMMY_SP.into() - ).emit(&self.env.diagnostic); + ServerMessage::Termination(result) => { + verification_summary.add(&result); + match result.result_type { + // nothing to do + viper::VerificationResultType::Success => (), + viper::VerificationResultType::ConsistencyErrors(errors) => { + for error in errors { + PrustiError::internal( + format!("consistency error in {program_name}: {error}"), DUMMY_SP.into() + ).emit(&self.env.diagnostic); + } + overall_result = VerificationResult::Failure; } - overall_result = VerificationResult::Failure; - } - viper::VerificationResult::Failure(errors) => { - for verification_error in errors { - debug!("Verification error in {}: {:?}", program_name.clone(), verification_error); - let mut prusti_error = error_manager.translate_verification_error(&verification_error); + viper::VerificationResultType::Failure(errors) => { + for verification_error in errors { + debug!("Verification error in {}: {:?}", program_name.clone(), verification_error); + let mut prusti_error = error_manager.translate_verification_error(&verification_error); - // annotate with counterexample, if requested - if config::counterexample() { - if config::unsafe_core_proof(){ - if let Some(silicon_counterexample) = &verification_error.counterexample { + // annotate with counterexample, if requested + if config::counterexample() { + if config::unsafe_core_proof(){ + if let Some(silicon_counterexample) = &verification_error.counterexample { + if let Some(def_id) = error_manager.get_def_id(&verification_error) { + let counterexample = counterexample_translation_refactored::backtranslate( + &self.encoder, + error_manager.position_manager(), + def_id, + silicon_counterexample, + ); + prusti_error = counterexample.annotate_error(prusti_error); + } else { + prusti_error = prusti_error.add_note( + format!( + "the verifier produced a counterexample for {program_name}, but it could not be mapped to source code" + ), + None, + ); + } + } + } else if let Some(silicon_counterexample) = &verification_error.counterexample { if let Some(def_id) = error_manager.get_def_id(&verification_error) { - let counterexample = counterexample_translation_refactored::backtranslate( + let counterexample = counterexample_translation::backtranslate( &self.encoder, - error_manager.position_manager(), def_id, silicon_counterexample, ); @@ -174,39 +195,23 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ); } } - } else if let Some(silicon_counterexample) = &verification_error.counterexample { - if let Some(def_id) = error_manager.get_def_id(&verification_error) { - let counterexample = counterexample_translation::backtranslate( - &self.encoder, - def_id, - silicon_counterexample, - ); - prusti_error = counterexample.annotate_error(prusti_error); - } else { - prusti_error = prusti_error.add_note( - format!( - "the verifier produced a counterexample for {program_name}, but it could not be mapped to source code" - ), - None, - ); - } + } + debug!("Prusti error: {:?}", prusti_error); + if prusti_error.is_disabled() { + prusti_error.cancel(); + } else { + prusti_error.emit(&self.env.diagnostic); } } - debug!("Prusti error: {:?}", prusti_error); - if prusti_error.is_disabled() { - prusti_error.cancel(); - } else { - prusti_error.emit(&self.env.diagnostic); - } + overall_result = VerificationResult::Failure; + } + viper::VerificationResultType::JavaException(exception) => { + error!("Java exception: {}", exception.get_stack_trace()); + PrustiError::internal( + format!("in {program_name}: {exception}"), DUMMY_SP.into() + ).emit(&self.env.diagnostic); + overall_result = VerificationResult::Failure; } - overall_result = VerificationResult::Failure; - } - viper::VerificationResult::JavaException(exception) => { - error!("Java exception: {}", exception.get_stack_trace()); - PrustiError::internal( - format!("in {program_name}: {exception}"), DUMMY_SP.into() - ).emit(&self.env.diagnostic); - overall_result = VerificationResult::Failure; } } ServerMessage::QuantifierInstantiation{q_name, insts, norm_pos_id} => { @@ -237,6 +242,10 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } } + if config::show_ide_info() { + println!("VerificationSummary: {}", verification_summary.to_json_string()); + } + if encoding_errors_count != 0 { overall_result = VerificationResult::Failure; } diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 48b1aa5b98c..0c6403701e6 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -127,7 +127,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { let out = serde_json::to_string(&compiler_info).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... - println!("{}", out); + println!("IdeInfo {}", out); } // collect and output Information used by IDE: diff --git a/prusti/src/ide_helper/mod.rs b/prusti/src/ide_helper/mod.rs index df7bd8b4dfb..b57ce271f8a 100644 --- a/prusti/src/ide_helper/mod.rs +++ b/prusti/src/ide_helper/mod.rs @@ -1,4 +1,4 @@ pub mod compiler_info; -mod call_finder; pub mod fake_error; +mod call_finder; mod query_signature; diff --git a/viper/src/verification_result.rs b/viper/src/verification_result.rs index ff57b543722..6e0107bbafc 100644 --- a/viper/src/verification_result.rs +++ b/viper/src/verification_result.rs @@ -6,9 +6,30 @@ use crate::{silicon_counterexample::SiliconCounterexample, JavaException}; +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct VerificationResult { + pub item_name: String, + pub result_type: VerificationResultType, + pub cached: bool, + pub time_ms: u128, +} + +impl VerificationResult { + pub fn is_success(&self) -> bool { + matches!(self.result_type, VerificationResultType::Success) + } + pub fn dummy_success() -> Self { + VerificationResult { + item_name: "".to_string(), + result_type: VerificationResultType::Success, + cached: false, + time_ms: 0, + } + } +} /// The result of a verification request on a Viper program. #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub enum VerificationResult { +pub enum VerificationResultType{ /// The program verified. Success, /// The program did not verify. Again with quantifier statistics. @@ -19,7 +40,8 @@ pub enum VerificationResult { JavaException(JavaException), } -impl VerificationResult { + +impl VerificationResultType { pub fn is_success(&self) -> bool { matches!(self, Self::Success) } diff --git a/viper/src/verifier.rs b/viper/src/verifier.rs index e0ffed868d2..0ea55efc624 100644 --- a/viper/src/verifier.rs +++ b/viper/src/verifier.rs @@ -11,7 +11,7 @@ use crate::{ silicon_counterexample::SiliconCounterexample, smt_manager::SmtManager, verification_backend::VerificationBackend, - verification_result::{VerificationError, VerificationResult}, + verification_result::{VerificationError, VerificationResult, VerificationResultType}, }; use jni::{objects::JObject, JNIEnv}; use log::{debug, error, info}; @@ -107,7 +107,7 @@ impl<'a> Verifier<'a> { self } - pub fn verify(&mut self, program: Program) -> VerificationResult { + pub fn verify(&mut self, program: Program) -> VerificationResultType { self.ast_utils.with_local_frame(16, || { debug!( "Program to be verified:\n{}", @@ -118,7 +118,7 @@ impl<'a> Verifier<'a> { let consistency_errors = match self.ast_utils.check_consistency(program) { Ok(errors) => errors, Err(java_exception) => { - return VerificationResult::JavaException(java_exception); + return VerificationResultType::JavaException(java_exception); } }; ); @@ -128,7 +128,7 @@ impl<'a> Verifier<'a> { "The provided Viper program has {} consistency errors.", consistency_errors.len() ); - return VerificationResult::ConsistencyErrors( + return VerificationResultType::ConsistencyErrors( consistency_errors .into_iter() .map(|e| self.jni.to_string(e)) @@ -331,9 +331,9 @@ impl<'a> Verifier<'a> { )) } - VerificationResult::Failure(errors) + VerificationResultType::Failure(errors) } else { - VerificationResult::Success + VerificationResultType::Success } }) } From a4dce760d646c3b5247de1f2a9cf32f512fb68c0 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Wed, 8 Feb 2023 14:04:12 +0100 Subject: [PATCH 24/74] WIP: restructure the process verification a bit --- prusti-server/src/java_exception.rs | 41 ---- prusti-server/src/jni_utils.rs | 217 ---------------------- prusti-server/src/lib.rs | 2 - prusti-server/src/process_verification.rs | 168 ++++++++++------- prusti-server/src/server.rs | 5 +- prusti-viper/src/verifier.rs | 22 ++- viper/src/lib.rs | 4 +- 7 files changed, 123 insertions(+), 336 deletions(-) delete mode 100644 prusti-server/src/java_exception.rs delete mode 100644 prusti-server/src/jni_utils.rs diff --git a/prusti-server/src/java_exception.rs b/prusti-server/src/java_exception.rs deleted file mode 100644 index 2bbc7fa20d6..00000000000 --- a/prusti-server/src/java_exception.rs +++ /dev/null @@ -1,41 +0,0 @@ -// © 2020, ETH Zurich -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -/// The structure describing a Java exception -#[derive(Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct JavaException { - message: String, - stack_trace: String, -} - -impl JavaException { - pub fn new(message: String, stack_trace: String) -> Self { - JavaException { - message, - stack_trace, - } - } - - pub fn get_message(&self) -> &str { - &self.message - } - - pub fn get_stack_trace(&self) -> &str { - &self.stack_trace - } -} - -impl std::fmt::Display for JavaException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Java exception: {}", self.message) - } -} - -impl std::fmt::Debug for JavaException { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Java exception: {} - {}", self.message, self.stack_trace) - } -} diff --git a/prusti-server/src/jni_utils.rs b/prusti-server/src/jni_utils.rs deleted file mode 100644 index f99623611b1..00000000000 --- a/prusti-server/src/jni_utils.rs +++ /dev/null @@ -1,217 +0,0 @@ -// © 2019, ETH Zurich -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use crate::java_exception::JavaException; -use jni::{ - errors::{Error, Result as JniResult}, - objects::{JObject, JString}, - strings::JNIString, - sys::jsize, - JNIEnv, -}; -use log::error; -use rustc_hash::FxHashMap; -use viper_sys::wrappers::*; - -#[derive(Clone, Copy)] -pub struct JniUtils<'a> { - env: &'a JNIEnv<'a>, -} - -impl<'a> JniUtils<'a> { - pub fn new(env: &'a JNIEnv) -> Self { - JniUtils { env } - } - - /// Generates the stack trace from a Java Exception - pub fn get_stack_trace(&self, t: JObject<'a>) -> JniResult { - let sw = java::io::StringWriter::with(self.env).new()?; - let pw = java::io::PrintWriter::with(self.env).new(sw)?; - java::lang::Throwable::with(self.env).call_printStackTrace(t, pw)?; - Ok(self.to_string(sw)) - } - - /// Unwrap a JniResult, retrieving information on raised Java exceptions. - pub fn unwrap_or_exception(&self, res: JniResult) -> Result { - res.map_err(|error| { - if let Error::JavaException = error { - let exception = self.env.exception_occurred().unwrap_or_else(|e| { - error!("{} source: {:?}", e, std::error::Error::source(&e)); - panic!("Failed to get the raised Java exception."); - }); - self.env.exception_clear().unwrap_or_else(|e| { - error!("{} source: {:?}", e, std::error::Error::source(&e)); - panic!("Failed to clear an exception in the process of being thrown."); - }); - // Retrieve information on the Java exception. - // The internal calls `unwrap_result` might lead to infinite recursion. - let exception_message = self.to_string(*exception); - let stack_trace = self.unwrap_result(self.get_stack_trace(*exception)); - JavaException::new(exception_message, stack_trace) - } else { - // This is not a Java exception - error!("{} source: {:?}", error, std::error::Error::source(&error)); - panic!("{}", error); - } - }) - } - - /// Unwrap a JniResult. - pub fn unwrap_result(&self, res: JniResult) -> T { - self.unwrap_or_exception(res) - .unwrap_or_else(|java_exception| panic!("{java_exception:?}")) - } - - /// Converts a Rust Option to a Scala Option - pub fn new_option(&self, opt: Option>) -> JObject<'a> { - match opt { - Some(o) => self.unwrap_result(scala::Some::with(self.env).new(o)), - None => self.unwrap_result(scala::None_object::with(self.env).singleton()), - } - } - - /// Converts a Rust String to a Java String - pub fn new_string>(&self, string: S) -> JObject<'a> { - self.unwrap_result(self.env.new_string(string)).into() - } - - pub fn new_map(&self, items: &[(JObject<'a>, JObject<'a>)]) -> JObject<'a> { - let hash_map_wrapper = scala::collection::immutable::HashMap::with(self.env); - let mut result = self.unwrap_result(hash_map_wrapper.new()); - for &(k, v) in items { - result = self.unwrap_result(hash_map_wrapper.call_updated(result, k, v)); - } - result - } - - /// Converts a Rust number to a Java BigInt - pub fn new_big_int(&self, number: &dyn ToString) -> JObject { - let number_string = self.new_string(number.to_string()); - - let java_big_integer = - self.unwrap_result(java::math::BigInteger::with(self.env).new(number_string)); - - self.unwrap_result(scala::math::BigInt::with(self.env).new(java_big_integer)) - } - - /// Converts a Rust Vec to a Scala Seq - pub fn new_seq(&self, objects: &[JObject]) -> JObject { - let array_buffer_wrapper = scala::collection::mutable::ArrayBuffer::with(self.env); - let len = objects.len(); - let res = self.unwrap_result(array_buffer_wrapper.new(len as i32)); - for obj in objects.iter() { - self.unwrap_result(array_buffer_wrapper.call_append(res, *obj)); - } - self.unwrap_result(array_buffer_wrapper.call_toSeq(res)) - } - - /// Converts a Java String to a Rust String - pub fn get_string(&self, string_object: JObject) -> String { - self.unwrap_result(self.env.get_string(JString::from(string_object))) - .into() - } - - /// Calls the "toString" method on a Java object and returns the result as a Rust String - pub fn to_string(self, object: JObject) -> String { - let object_wrapper = java::lang::Object::with(self.env); - let string_object = self.unwrap_result(object_wrapper.call_toString(object)); - self.get_string(string_object) - } - - /// Returns the name of the class of a Java object as Rust String - pub fn class_name(&self, object: JObject) -> String { - let class: JObject = self.unwrap_result(self.env.get_object_class(object)).into(); - let class_name_object = - self.unwrap_result(java::lang::Class::with(self.env).call_getName(class)); - self.get_string(class_name_object) - } - - /// Convert a Scala Seq to a Rust Vec - pub fn seq_to_vec(&self, sequence: JObject<'a>) -> Vec> { - let mut res: Vec = vec![]; - let seq_wrapper = scala::collection::Seq::with(self.env); - let length = self.unwrap_result(seq_wrapper.call_length(sequence)); - for i in 0..length { - let item: JObject = self.unwrap_result(seq_wrapper.call_apply(sequence, i)); - res.push(item); - } - res - } - - pub fn list_to_vec(&self, list: JObject<'a>) -> Vec> { - let list_wrapper = scala::collection::immutable::List::with(self.env); - let seq = self.unwrap_result(list_wrapper.call_toSeq(list)); - self.seq_to_vec(seq) - } - - pub fn vec_to_vec(&self, list: JObject<'a>) -> Vec> { - let vec_wrapper = scala::collection::immutable::Vector::with(self.env); - let seq = self.unwrap_result(vec_wrapper.call_toSeq(list)); - self.seq_to_vec(seq) - } - - /// Converts a Scala Map (using strings! JObjects are not hashable) to a Rust HashMap - pub fn stringmap_to_hashmap(&self, map: JObject<'a>) -> FxHashMap> { - let iter_wrapper = scala::collection::Iterable::with(self.env); - let product_wrapper = scala::Product::with(self.env); - let seq = self.unwrap_result(iter_wrapper.call_toSeq(map)); - self.seq_to_vec(seq) - .into_iter() - .map(|item| { - let item1 = self.unwrap_result(product_wrapper.call_productElement(item, 0)); - let item2 = self.unwrap_result(product_wrapper.call_productElement(item, 1)); - (self.to_string(item1), item2) - }) - .collect::>() - } - - /// Converts a Scala Map to a Vec of its keys. The order of keys is - /// maintained for map types with a consistent iteration order. - pub fn stringmap_to_keyvec(&self, map: JObject<'a>) -> Vec { - let iter_wrapper = scala::collection::Iterable::with(self.env); - let product_wrapper = scala::Product::with(self.env); - let seq = self.unwrap_result(iter_wrapper.call_toSeq(map)); - self.seq_to_vec(seq) - .into_iter() - .map(|item| { - let item = self.unwrap_result(product_wrapper.call_productElement(item, 0)); - self.to_string(item) - }) - .collect::>() - } - - /// Checks if an object is a subtype of a Java class - pub fn is_instance_of(&self, object: JObject, class: &str) -> bool { - let object_class = self.unwrap_result(self.env.get_object_class(object)); - let super_class = self.unwrap_result(self.env.find_class(class)); - self.unwrap_result(self.env.is_assignable_from(object_class, super_class)) - } - - /// Returns a new Java array of objects, initialised with null values - pub fn new_object_array(&self, length: jsize) -> JObject { - let object_class = self.unwrap_result(self.env.find_class("java/lang/Object")); - let object_array = self.unwrap_result(self.env.new_object_array( - length, - object_class, - JObject::null(), - )); - unsafe { JObject::from_raw(object_array) } - } - - /// Converts a Scala Seq to a Java Array - #[allow(dead_code)] - pub fn seq_to_array(&self, sequence: JObject<'a>, elements_class_ref: &str) -> JObject<'a> { - let elements_class = self.unwrap_result(self.env.find_class(elements_class_ref)); - let class_tag_object_wrapper = scala::reflect::ClassTag_object::with(self.env); - let class_tag_object = self.unwrap_result(class_tag_object_wrapper.singleton()); - let elements_class_tag = self.unwrap_result( - class_tag_object_wrapper.call_apply(class_tag_object, elements_class.into()), - ); - self.unwrap_result( - scala::collection::Seq::with(self.env).call_toArray(sequence, elements_class_tag), - ) - } -} diff --git a/prusti-server/src/lib.rs b/prusti-server/src/lib.rs index 4d61b3814e9..35e024c596e 100644 --- a/prusti-server/src/lib.rs +++ b/prusti-server/src/lib.rs @@ -11,8 +11,6 @@ mod process_verification; mod server; mod server_message; mod verification_request; -mod jni_utils; -mod java_exception; pub use client::*; pub use process_verification::*; diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index bee20716e2d..44e77c2716a 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -4,17 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::{VerificationRequest, ViperBackendConfig, jni_utils::JniUtils, ServerMessage}; +use crate::{VerificationRequest, ViperBackendConfig, ServerMessage}; use log::info; use prusti_common::{ config, report::log::{report, to_legal_file_name}, - vir::{program_normalization::NormalizationInfo, ToViper}, + vir::{program_normalization::NormalizationInfo, ToViper, program::Program}, Stopwatch, }; use std::{fs::create_dir_all, path::PathBuf, thread, sync::{mpsc, Arc, self}}; use viper::{ - smt_manager::SmtManager, PersistentCache, Cache, VerificationBackend, VerificationResult, Viper, VerificationContext + smt_manager::SmtManager, PersistentCache, Cache, VerificationBackend, VerificationResult, Viper, VerificationContext, jni_utils::JniUtils }; use viper_sys::wrappers::viper::*; use std::time; @@ -23,6 +23,7 @@ use futures::{stream::Stream, lock}; pub struct VerificationRequestProcessing { mtx_rx_servermsg: lock::Mutex>, mtx_tx_verreq: sync::Mutex>, + cache: sync::Mutex, } // one structure that lives for all the requests and has a single thread working on all the @@ -35,7 +36,11 @@ impl VerificationRequestProcessing { let (tx_verreq, rx_verreq) = mpsc::channel(); let mtx_rx_servermsg = lock::Mutex::new(rx_servermsg); let mtx_tx_verreq = sync::Mutex::new(tx_verreq); - let ret = Self {mtx_rx_servermsg: mtx_rx_servermsg, mtx_tx_verreq: mtx_tx_verreq}; + + let ret = Self {mtx_rx_servermsg: mtx_rx_servermsg, + mtx_tx_verreq: mtx_tx_verreq, + cache: sync::Mutex::new(PersistentCache::load_cache(config::cache_path())), + }; thread::spawn(|| { Self::verification_thread(rx_verreq, tx_servermsg) }); ret } @@ -43,14 +48,13 @@ impl VerificationRequestProcessing { fn verification_thread(rx_verreq: mpsc::Receiver, tx_servermsg: mpsc::Sender) { let mut stopwatch = Stopwatch::start("verification_request_processing", "JVM startup"); let viper = Arc::new(Viper::new_with_args(&config::viper_home(), config::extra_jvm_args())); - let mut cache = PersistentCache::load_cache(config::cache_path()); stopwatch.start_next("attach thread to JVM"); let verification_context = viper.attach_current_thread(); stopwatch.finish(); loop { match rx_verreq.recv() { Ok(request) => { - process_verification_request(&viper, &mut cache, &verification_context, &tx_servermsg, request); + process_verification_request(&viper, &mut self.cache.lock().unwrap(), &verification_context, &tx_servermsg, request); } Err(_) => break, } @@ -79,6 +83,10 @@ impl VerificationRequestProcessing { Some((msg, done)) }) } + + pub fn save_cache() { + self.cache.lock().unwrap().save(); + } } pub fn process_verification_request( viper_arc: &Arc, @@ -174,72 +182,15 @@ pub fn process_verification_request( let mut verifier = new_viper_verifier(program_name, &verification_context, request.backend_config); - let mut result = VerificationResult::Success; let normalization_info_clone = normalization_info.clone(); let sender_clone = sender.clone(); - // start thread for polling messages and print on receive - // TODO: Detach warning - thread::scope(|scope| { - // get the reporter - let env = &verification_context.env(); - let jni = JniUtils::new(env); - let verifier_wrapper = silver::verifier::Verifier::with(env); - let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); - let rep_glob_ref = env.new_global_ref(reporter).unwrap(); - - let (main_tx, thread_rx) = mpsc::channel(); - let polling_thread = scope.spawn(move || { - let verification_context = viper_arc.attach_current_thread(); - let env = verification_context.env(); - let jni = JniUtils::new(env); - let reporter_instance = rep_glob_ref.as_obj(); - let reporter_wrapper = silver::reporter::PollingReporter::with(env); - let mut done = false; - while !done { - while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { - let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); - match jni.class_name(msg).as_str() { - "viper.silver.reporter.QuantifierInstantiationsMessage" => { - let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); - let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); - let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); - // TODO: find out which more quantifiers are derived from the user - // quantifiers - info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); - // also matches the "-aux" quantifiers generated - // TODO: some positions have just the id 0 and cannot be denormalized... - if q_name.starts_with("quant_with_posID") { - let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); - let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); - let parsed = stripped.parse::(); - match parsed { - Ok(pos_id) => { - let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); - sender_clone.send(ServerMessage::QuantifierInstantiation{q_name: q_name, insts: u64::try_from(q_inst).unwrap(), norm_pos_id: norm_pos_id}).unwrap(); - } - _ => info!("Unexpected quantifier name {}", q_name) - } - } - } - _ => () - } - } - if !thread_rx.try_recv().is_err() { - info!("Polling thread received termination signal!"); - done = true; - } else { - thread::sleep(time::Duration::from_millis(10)); - } - } - }); - stopwatch.start_next("verification"); - result = verifier.verify(viper_program); - // send termination signal to polling thread - main_tx.send(()).unwrap(); - // FIXME: here the global ref is dropped from a detached thread - polling_thread.join().unwrap(); - }); + stopwatch.start_next("verification"); + let mut result = if config::report_qi_profile() { + verify_and_poll_qi(verification_context, verifier, viper_program) + } else { + verifier.verify(viper_program) + } // Don't cache Java exceptions, which might be due to misconfigured paths. if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { @@ -256,6 +207,85 @@ pub fn process_verification_request( }) } +fn verify_and_poll_qi(verification_context: &viper::VerificationContext, + verifier: &viper::Verifier, + viper_program: &Program, + viper_arc: &Arc, + sender: mpsc::Sender, + normalization_info: NormalizationInfo) + -> VerificationResult { + let mut result = VerificationResult::Success; + // start thread for polling messages + thread::scope(|scope| { + // get the reporter + let env = &verification_context.env(); + let jni = JniUtils::new(env); + let verifier_wrapper = silver::verifier::Verifier::with(env); + let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); + let rep_glob_ref = env.new_global_ref(reporter).unwrap(); + + let (main_tx, thread_rx) = mpsc::channel(); + let polling_thread = scope.spawn(polling_function(viper_arc, rep_glob_ref, sender, normalization_info)); + result = verifier.verify(viper_program); + // send termination signal to polling thread + main_tx.send(()).unwrap(); + // FIXME: here the global ref is dropped from a detached thread + polling_thread.join().unwrap(); + }); + result +} + +fn polling_function(viper_arc: &Arc, + rep_glob_ref: jni::objects::GlobalRef, + sender: mpsc::Sender, + normalization_info: NormalizationInfo) { + let verification_context = viper_arc.attach_current_thread(); + let env = verification_context.env(); + let jni = JniUtils::new(env); + let reporter_instance = rep_glob_ref.as_obj(); + let reporter_wrapper = silver::reporter::PollingReporter::with(env); + let mut done = false; + while !done { + while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { + let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); + match jni.class_name(msg).as_str() { + "viper.silver.reporter.QuantifierInstantiationsMessage" => { + let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); + let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); + let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); + // TODO: find out which more quantifiers are derived from the user + // quantifiers + info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); + // also matches the "-aux" quantifiers generated + // some positions have just the id 0 and cannot be denormalized... + if q_name.starts_with("quant_with_posID") { + let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); + let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); + let parsed = stripped.parse::(); + match parsed { + Ok(pos_id) => { + let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); + sender.send(ServerMessage::QuantifierInstantiation{q_name: q_name, + insts: u64::try_from(q_inst).unwrap(), + norm_pos_id: norm_pos_id} + ).unwrap(); + } + _ => info!("Unexpected quantifier name {}", q_name) + } + } + } + _ => () + } + } + if !thread_rx.try_recv().is_err() { + info!("Polling thread received termination signal!"); + done = true; + } else { + thread::sleep(time::Duration::from_millis(10)); + } + } +} + fn dump_viper_program(ast_utils: &viper::AstUtils, program: viper::Program, program_name: &str) { let namespace = "viper_program"; let filename = format!("{program_name}.vpr"); diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index 412d9edb763..3ac8e31ca00 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -79,15 +79,14 @@ where ws_send.close().await.unwrap(); }) }); - /* let save_cache = warp::post() .and(warp::path("save")) .and(warp::path::end()) .map(move || { - cache.lock().unwrap().save(); + VERIFICATION_REQUEST_PROCESSING.save_cache(); warp::reply::html("Saved") }); -*/ + let endpoints = json_verify.or(bincode_verify); // Here we use a single thread because // 1. Viper is not thread safe yet (Silicon issue #578), and diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 10d9a4e99c6..126a2a24371 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -133,6 +133,9 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { // key: (norm_pos_id, program_name), key to result: q_name result: num_instantiations let mut quantifier_instantiations: HashMap::<(u64, String), HashMap::> = HashMap::new(); + // if we are not running in an ide, we want the errors to be reported sortedly + let prusti_errors: Vec<_> = vec![]; + pin_mut!(verification_messages); while let Some((program_name, server_msg)) = verification_messages.next().await { @@ -196,7 +199,11 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { if prusti_error.is_disabled() { prusti_error.cancel(); } else { - prusti_error.emit(&self.env.diagnostic); + if config::show_ide_info() { + prusti_error.emit(&self.env.diagnostic); + } else { + prusti_errors.push_back(prusti_error); + } } } overall_result = VerificationResult::Failure; @@ -230,13 +237,24 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ), span.clone() ).emit(&self.env.diagnostic); }, - None => info!("#{} quantifier instantiations of {} for unknown position id {} in verification of {}", insts, q_name, norm_pos_id, program_name), + None => info!("#{insts} quantifier instantiations of {q_name} for unknown position id {norm_pos_id} in verification of {program_name}"), } } } } } + // if we are in an ide, we already emit the errors asynchronously, otherwise we wait for + // all of them in order to sort them + if !config::show_ide_info() { + prusti_errors.sort(); + + for prusti_error in prusti_errors { + debug!("Prusti error: {:?}", prusti_error); + prusti_error.emit(&self.env.diagnostic); + } + } + if encoding_errors_count != 0 { overall_result = VerificationResult::Failure; } diff --git a/viper/src/lib.rs b/viper/src/lib.rs index 747f7fc1261..97d0a524634 100644 --- a/viper/src/lib.rs +++ b/viper/src/lib.rs @@ -10,11 +10,11 @@ mod ast_factory; mod ast_utils; pub mod errors; -mod jni_utils; +pub mod jni_utils; #[macro_use] pub mod utils; mod cache; -mod java_exception; +pub mod java_exception; pub mod silicon_counterexample; pub mod smt_manager; mod verification_backend; From f84219cdb89cda5a312f6dcf5b34b92b54309868 Mon Sep 17 00:00:00 2001 From: cedric Date: Wed, 8 Feb 2023 20:17:27 +0100 Subject: [PATCH 25/74] Fixed warnings and a bug. --- prusti-viper/src/verifier.rs | 2 +- prusti/src/callbacks.rs | 2 +- prusti/src/ide_helper/query_signature.rs | 2 +- viper/src/verifier.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 55eed4dc6af..99dad182638 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -243,7 +243,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } if config::show_ide_info() { - println!("VerificationSummary: {}", verification_summary.to_json_string()); + println!("\n\n\n\nVerificationSummary: {}", verification_summary.to_json_string()); } if encoding_errors_count != 0 { diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 0c6403701e6..48b1aa5b98c 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -127,7 +127,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { let out = serde_json::to_string(&compiler_info).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... - println!("IdeInfo {}", out); + println!("{}", out); } // collect and output Information used by IDE: diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 0a3c8b43227..bbb52ede5a7 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -4,7 +4,7 @@ use std::{collections::HashMap, fmt}; use super::compiler_info::ProcDef; use prusti_rustc_interface::{ hir::{def::DefKind, def_id::DefId}, - middle::ty::{self, Clause, DefIdTree, ImplSubject, PredicateKind, Ty, TyCtxt}, + middle::ty::{self, Clause, DefIdTree, ImplSubject, PredicateKind, TyCtxt}, }; // data structure to represent what we want to generate diff --git a/viper/src/verifier.rs b/viper/src/verifier.rs index 0ea55efc624..1864b9f2469 100644 --- a/viper/src/verifier.rs +++ b/viper/src/verifier.rs @@ -11,7 +11,7 @@ use crate::{ silicon_counterexample::SiliconCounterexample, smt_manager::SmtManager, verification_backend::VerificationBackend, - verification_result::{VerificationError, VerificationResult, VerificationResultType}, + verification_result::{VerificationError, VerificationResultType}, }; use jni::{objects::JObject, JNIEnv}; use log::{debug, error, info}; From d9e067c6bfcac456079355b631285622023834df Mon Sep 17 00:00:00 2001 From: cedric Date: Sun, 12 Feb 2023 02:31:13 +0100 Subject: [PATCH 26/74] renamed a file and adjusted text for IDE information output --- prusti-viper/src/ide/mod.rs | 2 +- .../ide/{verification_summary.rs => verification_info.rs} | 4 ++-- prusti-viper/src/verifier.rs | 6 +++--- prusti/src/callbacks.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename prusti-viper/src/ide/{verification_summary.rs => verification_info.rs} (94%) diff --git a/prusti-viper/src/ide/mod.rs b/prusti-viper/src/ide/mod.rs index 2df6ce4c269..3791604496c 100644 --- a/prusti-viper/src/ide/mod.rs +++ b/prusti-viper/src/ide/mod.rs @@ -1 +1 @@ -pub mod verification_summary; +pub mod verification_info; diff --git a/prusti-viper/src/ide/verification_summary.rs b/prusti-viper/src/ide/verification_info.rs similarity index 94% rename from prusti-viper/src/ide/verification_summary.rs rename to prusti-viper/src/ide/verification_info.rs index e42a0c92906..9019c51f790 100644 --- a/prusti-viper/src/ide/verification_summary.rs +++ b/prusti-viper/src/ide/verification_info.rs @@ -2,11 +2,11 @@ use viper::VerificationResult; use serde::Serialize; #[derive(Serialize)] -pub struct VerificationSummary { +pub struct VerificationInfo { result_list: Vec } -impl VerificationSummary { +impl VerificationInfo { pub fn new() -> Self { Self { result_list: vec![], diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 99dad182638..4ae5b424211 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -133,14 +133,14 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { // different q_names for the same ID and each program reports independent results // key: (norm_pos_id, program_name), key to result: q_name result: num_instantiations let mut quantifier_instantiations: HashMap::<(u64, String), HashMap::> = HashMap::new(); - let mut verification_summary = ide::verification_summary::VerificationSummary::new(); + let mut verification_info = ide::verification_info::VerificationInfo::new(); pin_mut!(verification_messages); while let Some((program_name, server_msg)) = verification_messages.next().await { match server_msg { ServerMessage::Termination(result) => { - verification_summary.add(&result); + verification_info.add(&result); match result.result_type { // nothing to do viper::VerificationResultType::Success => (), @@ -243,7 +243,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } if config::show_ide_info() { - println!("\n\n\n\nVerificationSummary: {}", verification_summary.to_json_string()); + println!("VerificationInfo {}", verification_info.to_json_string()); } if encoding_errors_count != 0 { diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 48b1aa5b98c..609f0b1e36c 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -127,7 +127,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { let out = serde_json::to_string(&compiler_info).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... - println!("{}", out); + println!("CompilerInfo {}", out); } // collect and output Information used by IDE: From a069cefe63563c2d0b4d3c573c9069159b3fe7d4 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sun, 12 Feb 2023 21:11:54 +0100 Subject: [PATCH 27/74] Finish process_verification restructuring. Add a parameter for qi.profile_freq. Encode the quantifier ID in the line of the position in order to not having to modify the silver way of encoding a quantifier. --- prusti-server/src/lib.rs | 2 - prusti-server/src/process_verification.rs | 64 +++++++++++-------- prusti-server/src/server.rs | 2 +- prusti-server/src/server_message.rs | 2 +- prusti-server/src/verification_request.rs | 5 +- prusti-utils/src/config.rs | 6 ++ .../mir/pure/specifications/encoder_poly.rs | 5 +- prusti-viper/src/verifier.rs | 14 ++-- prusti/src/ide_helper/query_signature.rs | 2 +- prusti/src/verifier.rs | 4 +- 10 files changed, 60 insertions(+), 46 deletions(-) diff --git a/prusti-server/src/lib.rs b/prusti-server/src/lib.rs index 35e024c596e..e31f96cbb74 100644 --- a/prusti-server/src/lib.rs +++ b/prusti-server/src/lib.rs @@ -17,8 +17,6 @@ pub use process_verification::*; pub use server::*; pub use server_message::*; pub use verification_request::*; -pub use jni_utils::*; -pub use java_exception::*; // Futures returned by `Client` need to be executed in a compatible tokio runtime. pub use tokio; diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index 44e77c2716a..e59d5c372d6 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -9,7 +9,7 @@ use log::info; use prusti_common::{ config, report::log::{report, to_legal_file_name}, - vir::{program_normalization::NormalizationInfo, ToViper, program::Program}, + vir::{program_normalization::NormalizationInfo, ToViper}, Stopwatch, }; use std::{fs::create_dir_all, path::PathBuf, thread, sync::{mpsc, Arc, self}}; @@ -20,10 +20,14 @@ use viper_sys::wrappers::viper::*; use std::time; use futures::{stream::Stream, lock}; +enum ServerRequest { + Verification(VerificationRequest), + SaveCache, +} + pub struct VerificationRequestProcessing { mtx_rx_servermsg: lock::Mutex>, - mtx_tx_verreq: sync::Mutex>, - cache: sync::Mutex, + mtx_tx_verreq: sync::Mutex>, } // one structure that lives for all the requests and has a single thread working on all the @@ -39,22 +43,26 @@ impl VerificationRequestProcessing { let ret = Self {mtx_rx_servermsg: mtx_rx_servermsg, mtx_tx_verreq: mtx_tx_verreq, - cache: sync::Mutex::new(PersistentCache::load_cache(config::cache_path())), }; thread::spawn(|| { Self::verification_thread(rx_verreq, tx_servermsg) }); ret } - fn verification_thread(rx_verreq: mpsc::Receiver, tx_servermsg: mpsc::Sender) { + fn verification_thread(rx_verreq: mpsc::Receiver, tx_servermsg: mpsc::Sender) { let mut stopwatch = Stopwatch::start("verification_request_processing", "JVM startup"); let viper = Arc::new(Viper::new_with_args(&config::viper_home(), config::extra_jvm_args())); + let mut cache = PersistentCache::load_cache(config::cache_path()); stopwatch.start_next("attach thread to JVM"); let verification_context = viper.attach_current_thread(); stopwatch.finish(); loop { match rx_verreq.recv() { Ok(request) => { - process_verification_request(&viper, &mut self.cache.lock().unwrap(), &verification_context, &tx_servermsg, request); + match request { + ServerRequest::Verification(verification_request) => + process_verification_request(&viper, &mut cache, &verification_context, &tx_servermsg, verification_request), + ServerRequest::SaveCache => cache.save(), + } } Err(_) => break, } @@ -65,7 +73,7 @@ impl VerificationRequestProcessing { self.mtx_tx_verreq .lock() .unwrap() - .send(request) + .send(ServerRequest::Verification(request)) .unwrap(); futures::stream::unfold(false, move |done: bool| async move { if done { @@ -84,8 +92,12 @@ impl VerificationRequestProcessing { }) } - pub fn save_cache() { - self.cache.lock().unwrap().save(); + pub fn save_cache(&self) { + self.mtx_tx_verreq + .lock() + .unwrap() + .send(ServerRequest::SaveCache) + .unwrap(); } } pub fn process_verification_request( @@ -182,15 +194,12 @@ pub fn process_verification_request( let mut verifier = new_viper_verifier(program_name, &verification_context, request.backend_config); - let normalization_info_clone = normalization_info.clone(); - let sender_clone = sender.clone(); - stopwatch.start_next("verification"); let mut result = if config::report_qi_profile() { - verify_and_poll_qi(verification_context, verifier, viper_program) + verify_and_poll_qi(verification_context, verifier, viper_program, viper_arc, sender.clone()) } else { verifier.verify(viper_program) - } + }; // Don't cache Java exceptions, which might be due to misconfigured paths. if config::enable_cache() && !matches!(result, VerificationResult::JavaException(_)) { @@ -208,13 +217,12 @@ pub fn process_verification_request( } fn verify_and_poll_qi(verification_context: &viper::VerificationContext, - verifier: &viper::Verifier, - viper_program: &Program, + mut verifier: viper::Verifier, + viper_program: viper::Program, viper_arc: &Arc, - sender: mpsc::Sender, - normalization_info: NormalizationInfo) + sender: mpsc::Sender) -> VerificationResult { - let mut result = VerificationResult::Success; + let mut result = VerificationResult::Success; // start thread for polling messages thread::scope(|scope| { // get the reporter @@ -225,7 +233,7 @@ fn verify_and_poll_qi(verification_context: &viper::VerificationContext, let rep_glob_ref = env.new_global_ref(reporter).unwrap(); let (main_tx, thread_rx) = mpsc::channel(); - let polling_thread = scope.spawn(polling_function(viper_arc, rep_glob_ref, sender, normalization_info)); + let polling_thread = scope.spawn(|| polling_function(viper_arc, rep_glob_ref, sender, thread_rx)); result = verifier.verify(viper_program); // send termination signal to polling thread main_tx.send(()).unwrap(); @@ -238,7 +246,7 @@ fn verify_and_poll_qi(verification_context: &viper::VerificationContext, fn polling_function(viper_arc: &Arc, rep_glob_ref: jni::objects::GlobalRef, sender: mpsc::Sender, - normalization_info: NormalizationInfo) { + thread_rx: mpsc::Receiver<()>) { let verification_context = viper_arc.attach_current_thread(); let env = verification_context.env(); let jni = JniUtils::new(env); @@ -257,18 +265,18 @@ fn polling_function(viper_arc: &Arc, // quantifiers info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); // also matches the "-aux" quantifiers generated - // some positions have just the id 0 and cannot be denormalized... - if q_name.starts_with("quant_with_posID") { - let no_pref = q_name.strip_prefix("quant_with_posID").unwrap(); + // we encoded the position id in the line number since this is not used by + // prusti either way + if q_name.starts_with("prog.l") { + let no_pref = q_name.strip_prefix("prog.l").unwrap(); let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); let parsed = stripped.parse::(); match parsed { Ok(pos_id) => { - let norm_pos_id = normalization_info_clone.denormalize_position_id(pos_id); sender.send(ServerMessage::QuantifierInstantiation{q_name: q_name, - insts: u64::try_from(q_inst).unwrap(), - norm_pos_id: norm_pos_id} - ).unwrap(); + insts: u64::try_from(q_inst).unwrap(), + pos_id: pos_id} + ).unwrap(); } _ => info!("Unexpected quantifier name {}", q_name) } diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index 3ac8e31ca00..866ce157385 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -87,7 +87,7 @@ where warp::reply::html("Saved") }); - let endpoints = json_verify.or(bincode_verify); + let endpoints = json_verify.or(bincode_verify).or(save_cache); // Here we use a single thread because // 1. Viper is not thread safe yet (Silicon issue #578), and // 2. By default Silicon already uses as many cores as possible. diff --git a/prusti-server/src/server_message.rs b/prusti-server/src/server_message.rs index d39d4f06aa5..ca5ce13bbf2 100644 --- a/prusti-server/src/server_message.rs +++ b/prusti-server/src/server_message.rs @@ -9,5 +9,5 @@ use viper::VerificationResult; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum ServerMessage { Termination(VerificationResult), - QuantifierInstantiation { q_name: String, insts: u64, norm_pos_id: u64 }, + QuantifierInstantiation { q_name: String, insts: u64, pos_id: u64 }, } diff --git a/prusti-server/src/verification_request.rs b/prusti-server/src/verification_request.rs index 2ef30e4e0f1..9b04d060c0e 100644 --- a/prusti-server/src/verification_request.rs +++ b/prusti-server/src/verification_request.rs @@ -56,10 +56,11 @@ impl ViperBackendConfig { // model.partial changes the default case of functions in counterexamples // to #unspecified format!( - "smt.qi.eager_threshold={} model.partial={} smt.qi.profile={}", + "smt.qi.eager_threshold={} model.partial={} smt.qi.profile={} smt.qi.profile_freq={}", config::smt_qi_eager_threshold(), config::counterexample(), - config::smt_qi_profile() + config::smt_qi_profile(), + config::smt_qi_profile_freq() ), "--logLevel".to_string(), "ERROR".to_string(), diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index bdfc3b68eb4..f0ca94469f9 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -103,6 +103,7 @@ lazy_static::lazy_static! { settings.set_default("assert_timeout", 10_000).unwrap(); settings.set_default("smt_qi_eager_threshold", 1000).unwrap(); settings.set_default("smt_qi_profile", true).unwrap(); + settings.set_default("smt_qi_profile_freq", 10000).unwrap(); settings.set_default("report_qi_profile", true).unwrap(); settings.set_default("use_more_complete_exhale", true).unwrap(); settings.set_default("skip_unsupported_features", false).unwrap(); @@ -508,6 +509,11 @@ pub fn smt_qi_profile() -> bool { read_setting("smt_qi_profile") } +/// Set `qi.profile_freq` value to the given one. +pub fn smt_qi_profile_freq() -> u64 { + read_setting("smt_qi_profile_freq") +} + /// Whether to report the quantifier instantiations (done as json). pub fn report_qi_profile() -> bool { read_setting("report_qi_profile") diff --git a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs index 1b6fe087ecf..bcbf94be1c1 100644 --- a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs +++ b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs @@ -241,7 +241,6 @@ pub(super) fn encode_quantifier<'tcx>( body_substs, )?; - // FIXME: we get the wrong span here let pos = encoder.error_manager().register_span(parent_def_id, span); // replace qvars with a nicer name based on quantifier depth to ensure that @@ -289,7 +288,9 @@ pub(super) fn encode_quantifier<'tcx>( fixed_qvars, encoded_trigger_sets, final_body, - pos, + // we encode the position ID in the line number because that is used to name the + // quantifiers in z3 + vir_crate::polymorphic::Position::new(i32::try_from(pos.id()).unwrap(), i32::try_from(pos.id()).unwrap(), pos.id()), )) } } diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 126a2a24371..3af49c61799 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -130,11 +130,11 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { let error_manager = self.encoder.error_manager(); // we want quantifier_pos_ID + program_name + q_name as identifier because there are // different q_names for the same ID and each program reports independent results - // key: (norm_pos_id, program_name), key to result: q_name result: num_instantiations + // key: (pos_id, program_name), key to result: q_name result: num_instantiations let mut quantifier_instantiations: HashMap::<(u64, String), HashMap::> = HashMap::new(); // if we are not running in an ide, we want the errors to be reported sortedly - let prusti_errors: Vec<_> = vec![]; + let mut prusti_errors: Vec<_> = vec![]; pin_mut!(verification_messages); @@ -202,7 +202,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { if config::show_ide_info() { prusti_error.emit(&self.env.diagnostic); } else { - prusti_errors.push_back(prusti_error); + prusti_errors.push(prusti_error); } } } @@ -216,11 +216,11 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { overall_result = VerificationResult::Failure; } } - ServerMessage::QuantifierInstantiation{q_name, insts, norm_pos_id} => { + ServerMessage::QuantifierInstantiation{q_name, insts, pos_id} => { if config::report_qi_profile() { - match error_manager.position_manager().get_span_from_id(norm_pos_id) { + match error_manager.position_manager().get_span_from_id(pos_id) { Some(span) => { - let key = (norm_pos_id, program_name.clone()); + let key = (pos_id, program_name.clone()); if !quantifier_instantiations.contains_key(&key) { quantifier_instantiations.insert(key.clone(), HashMap::new()); } @@ -237,7 +237,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ), span.clone() ).emit(&self.env.diagnostic); }, - None => info!("#{insts} quantifier instantiations of {q_name} for unknown position id {norm_pos_id} in verification of {program_name}"), + None => info!("#{insts} quantifier instantiations of {q_name} for unknown position id {pos_id} in verification of {program_name}"), } } } diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 0a3c8b43227..bbb52ede5a7 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -4,7 +4,7 @@ use std::{collections::HashMap, fmt}; use super::compiler_info::ProcDef; use prusti_rustc_interface::{ hir::{def::DefKind, def_id::DefId}, - middle::ty::{self, Clause, DefIdTree, ImplSubject, PredicateKind, Ty, TyCtxt}, + middle::ty::{self, Clause, DefIdTree, ImplSubject, PredicateKind, TyCtxt}, }; // data structure to represent what we want to generate diff --git a/prusti/src/verifier.rs b/prusti/src/verifier.rs index 77d33ea9e8c..b94c2a87614 100644 --- a/prusti/src/verifier.rs +++ b/prusti/src/verifier.rs @@ -10,7 +10,7 @@ use prusti_interface::{ use prusti_viper::verifier::Verifier; pub fn verify<'tcx>( - env: Environment<'tcx>, + env: Environment<'tcx>, def_spec: typed::DefSpecificationMap, verification_task: VerificationTask<'tcx>, ) { @@ -43,7 +43,7 @@ pub fn verify<'tcx>( ); } } - + let verification_result = if verification_task.procedures.is_empty() && verification_task.types.is_empty() { From ca86115bae8c1ba9b539233648dc4911d7c4e404 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Tue, 14 Feb 2023 00:47:56 +0100 Subject: [PATCH 28/74] Changed message for fake errors and adjusted names of CompilerInfo procedures. --- prusti/src/callbacks.rs | 2 +- prusti/src/ide_helper/compiler_info.rs | 2 +- prusti/src/ide_helper/fake_error.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 609f0b1e36c..cf89220d69b 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -148,7 +148,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { ); let procedures = annotated_procedures .into_iter() - .filter(|x| env.name.get_item_def_path(*x) == target_def_path) + .filter(|x| env.name.get_unique_item_name(*x) == target_def_path) .collect(); let selective_task = VerificationTask { procedures, types }; verify(env, def_spec, selective_task); diff --git a/prusti/src/ide_helper/compiler_info.rs b/prusti/src/ide_helper/compiler_info.rs index cbb7f8f07d6..1fa279a3f79 100644 --- a/prusti/src/ide_helper/compiler_info.rs +++ b/prusti/src/ide_helper/compiler_info.rs @@ -79,7 +79,7 @@ fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec(env: Environment<'tcx>) { let sp = MultiSpan::from_span(DUMMY_SP); - let message = String::from("Not actually an error"); + let message = String::from("[Prusti: FakeError]"); let help = None; let notes = []; From 26ea0afcf08b7d26666a256087381e11506a2ffa Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Wed, 15 Feb 2023 17:22:57 +0100 Subject: [PATCH 29/74] Add QuantifierChosenTriggersMessage reporting --- prusti-server/src/process_verification.rs | 51 ++++++++++++++--------- prusti-server/src/server_message.rs | 1 + prusti-utils/src/config.rs | 9 ++-- prusti-viper/src/ide/verification_info.rs | 4 +- prusti-viper/src/verifier.rs | 21 ++++++++-- prusti/src/callbacks.rs | 4 +- viper-sys/build.rs | 15 ++----- 7 files changed, 62 insertions(+), 43 deletions(-) diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index b39fb1f528a..caa7b338e60 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -17,6 +17,7 @@ use viper::{ smt_manager::SmtManager, PersistentCache, Cache, VerificationBackend, VerificationResult, VerificationResultType, Viper, VerificationContext, jni_utils::JniUtils }; use viper_sys::wrappers::viper::*; +use viper_sys::wrappers::java; use std::time; use futures::{stream::Stream, lock}; @@ -203,8 +204,8 @@ pub fn process_verification_request( new_viper_verifier(program_name, &verification_context, request.backend_config); stopwatch.start_next("verification"); - result.result_type = if config::report_qi_profile() { - verify_and_poll_qi(verification_context, verifier, viper_program, viper_arc, sender.clone()) + result.result_type = if config::report_viper_messages() { + verify_and_poll_msgs(verification_context, verifier, viper_program, viper_arc, sender.clone()) } else { verifier.verify(viper_program) }; @@ -225,7 +226,7 @@ pub fn process_verification_request( }) } -fn verify_and_poll_qi(verification_context: &viper::VerificationContext, +fn verify_and_poll_msgs(verification_context: &viper::VerificationContext, mut verifier: viper::Verifier, viper_program: viper::Program, viper_arc: &Arc, @@ -241,11 +242,8 @@ fn verify_and_poll_qi(verification_context: &viper::VerificationContext, let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); let rep_glob_ref = env.new_global_ref(reporter).unwrap(); - let (main_tx, thread_rx) = mpsc::channel(); - let polling_thread = scope.spawn(|| polling_function(viper_arc, rep_glob_ref, sender, thread_rx)); + let polling_thread = scope.spawn(|| polling_function(viper_arc, rep_glob_ref, sender)); result_type = verifier.verify(viper_program); - // send termination signal to polling thread - main_tx.send(()).unwrap(); // FIXME: here the global ref is dropped from a detached thread polling_thread.join().unwrap(); }); @@ -254,15 +252,13 @@ fn verify_and_poll_qi(verification_context: &viper::VerificationContext, fn polling_function(viper_arc: &Arc, rep_glob_ref: jni::objects::GlobalRef, - sender: mpsc::Sender, - thread_rx: mpsc::Receiver<()>) { + sender: mpsc::Sender) { let verification_context = viper_arc.attach_current_thread(); let env = verification_context.env(); let jni = JniUtils::new(env); let reporter_instance = rep_glob_ref.as_obj(); let reporter_wrapper = silver::reporter::PollingReporter::with(env); - let mut done = false; - while !done { + loop { while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); match jni.class_name(msg).as_str() { @@ -270,11 +266,9 @@ fn polling_function(viper_arc: &Arc, let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); - // TODO: find out which more quantifiers are derived from the user - // quantifiers info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); // also matches the "-aux" quantifiers generated - // we encoded the position id in the line number since this is not used by + // we encoded the position id in the line and column number since this is not used by // prusti either way if q_name.starts_with("prog.l") { let no_pref = q_name.strip_prefix("prog.l").unwrap(); @@ -291,15 +285,32 @@ fn polling_function(viper_arc: &Arc, } } } + "viper.silver.reporter.QuantifierChosenTriggersMessage" => { + let obj_wrapper = java::lang::Object::with(env); + let positioned_wrapper = silver::ast::Positioned::with(env); + let msg_wrapper = silver::reporter::QuantifierChosenTriggersMessage::with(env); + + let viper_quant = jni.unwrap_result(msg_wrapper.call_quantifier(msg)); + let viper_quant_str = jni.get_string(jni.unwrap_result(obj_wrapper.call_toString(viper_quant))); + // we encoded the position id in the line and column number since this is not used by + // prusti either way + let pos = jni.unwrap_result(positioned_wrapper.call_pos(viper_quant)); + let pos_string = jni.get_string(jni.unwrap_result(obj_wrapper.call_toString(pos))); + let pos_id_index = pos_string.rfind(".").unwrap(); + let pos_id = pos_string[pos_id_index+1..].parse::().unwrap(); + + let viper_triggers = jni.get_string(jni.unwrap_result(msg_wrapper.call_triggers__string(msg))); + info!("QuantifierChosenTriggersMessage: {} {} {}", viper_quant_str, viper_triggers, pos_id); + sender.send(ServerMessage::QuantifierChosenTriggers{viper_quant: viper_quant_str, + triggers: viper_triggers, + pos_id: pos_id} + ).unwrap(); + } + "viper.silver.reporter.VerificationTerminationMessage" => return, _ => () } } - if !thread_rx.try_recv().is_err() { - info!("Polling thread received termination signal!"); - done = true; - } else { - thread::sleep(time::Duration::from_millis(10)); - } + thread::sleep(time::Duration::from_millis(10)); } } diff --git a/prusti-server/src/server_message.rs b/prusti-server/src/server_message.rs index ca5ce13bbf2..bcc973ec0bc 100644 --- a/prusti-server/src/server_message.rs +++ b/prusti-server/src/server_message.rs @@ -10,4 +10,5 @@ use viper::VerificationResult; pub enum ServerMessage { Termination(VerificationResult), QuantifierInstantiation { q_name: String, insts: u64, pos_id: u64 }, + QuantifierChosenTriggers { viper_quant: String, triggers: String, pos_id: u64 }, } diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index f0ca94469f9..e47478b84a0 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -104,7 +104,7 @@ lazy_static::lazy_static! { settings.set_default("smt_qi_eager_threshold", 1000).unwrap(); settings.set_default("smt_qi_profile", true).unwrap(); settings.set_default("smt_qi_profile_freq", 10000).unwrap(); - settings.set_default("report_qi_profile", true).unwrap(); + settings.set_default("report_viper_messages", true).unwrap(); settings.set_default("use_more_complete_exhale", true).unwrap(); settings.set_default("skip_unsupported_features", false).unwrap(); settings.set_default("internal_errors_as_warnings", false).unwrap(); @@ -514,9 +514,10 @@ pub fn smt_qi_profile_freq() -> u64 { read_setting("smt_qi_profile_freq") } -/// Whether to report the quantifier instantiations (done as json). -pub fn report_qi_profile() -> bool { - read_setting("report_qi_profile") +/// Whether to report the messages produced by the viper backend (e.g. quantifier instantiations, +/// quantifier triggers) +pub fn report_viper_messages() -> bool { + read_setting("report_viper_messages") } /// Maximum time (in milliseconds) for the verifier to spend on checks. diff --git a/prusti-viper/src/ide/verification_info.rs b/prusti-viper/src/ide/verification_info.rs index 9019c51f790..5aefe7bcbd8 100644 --- a/prusti-viper/src/ide/verification_info.rs +++ b/prusti-viper/src/ide/verification_info.rs @@ -24,10 +24,10 @@ impl VerificationInfo { // since the existing verification result // is not as trivially passed in json #[derive(Serialize)] -struct IdeVerificationResult { +pub struct IdeVerificationResult { item_name: String, success: bool, - time_ms: u128, + time_ms: u128, cached: bool } diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index f57ede18ee6..ede296ffe63 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -136,7 +136,6 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { // if we are not running in an ide, we want the errors to be reported sortedly let mut prusti_errors: Vec<_> = vec![]; - let mut verification_info = ide::verification_info::VerificationInfo::new(); pin_mut!(verification_messages); @@ -223,7 +222,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } } ServerMessage::QuantifierInstantiation{q_name, insts, pos_id} => { - if config::report_qi_profile() { + if config::report_viper_messages() { match error_manager.position_manager().get_span_from_id(pos_id) { Some(span) => { let key = (pos_id, program_name.clone()); @@ -243,7 +242,23 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ), span.clone() ).emit(&self.env.diagnostic); }, - None => info!("#{insts} quantifier instantiations of {q_name} for unknown position id {pos_id} in verification of {program_name}"), + None => error!("#{insts} quantifier instantiations of {q_name} for unknown position id {pos_id} in verification of {program_name}"), + } + } + } + ServerMessage::QuantifierChosenTriggers{viper_quant, triggers, pos_id} => { + if config::report_viper_messages() { + if pos_id != 0 { + match error_manager.position_manager().get_span_from_id(pos_id) { + Some(span) => { + PrustiError::message( + format!("quantifier_chosen_triggers_message{}", + json!({"viper_quant": viper_quant, "triggers": triggers}), + ), span.clone() + ).emit(&self.env.diagnostic); + }, + None => error!("Invalid position id {pos_id} for viper quantifier {viper_quant} in verification of {program_name}"), + } } } } diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 609f0b1e36c..2e52dfe1fc2 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -19,6 +19,7 @@ use prusti_rustc_interface::{ }, session::Session, }; +use ::log::info; #[derive(Default)] pub struct PrustiCompilerCalls; @@ -134,7 +135,6 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { if !config::no_verify() && !config::skip_verification() { if config::selective_verify().is_none() { - println!("verifying everything\n\n\n"); let verification_task = VerificationTask { procedures: annotated_procedures, types, @@ -142,7 +142,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { verify(env, def_spec, verification_task); } else { let target_def_path = config::selective_verify().unwrap(); - println!( + info!( "Selective Verification invoked for method {}\n\n\n", target_def_path ); diff --git a/viper-sys/build.rs b/viper-sys/build.rs index 57e0b9bdebd..9c4c259c18a 100644 --- a/viper-sys/build.rs +++ b/viper-sys/build.rs @@ -148,23 +148,14 @@ fn main() { method!("hasNewMessage"), method!("getNewMessage"), ]), - java_class!("viper.silver.reporter.EntitySuccessMessage", vec![ - method!("verificationTime"), - method!("cached"), - method!("concerning"), - ]), - java_class!("viper.silver.reporter.EntityFailureMessage", vec![ - method!("verificationTime"), - method!("cached"), - method!("concerning"), + java_class!("viper.silver.reporter.QuantifierChosenTriggersMessage", vec![ + method!("quantifier"), + method!("triggers_string"), ]), java_class!("viper.silver.reporter.QuantifierInstantiationsMessage", vec![ method!("quantifier"), method!("instantiations"), ]), - java_class!("viper.silver.ast.Member", vec![ - method!("name"), - ]), java_class!("viper.silver.verifier.Verifier", vec![ method!("name"), method!("buildVersion"), From 2cbc865f1cae2fe5c3946223955f33dd03a73883 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 16 Feb 2023 02:23:54 +0100 Subject: [PATCH 30/74] passing information about specifications of calls to IDE now, called EncodingInfo --- prusti-viper/src/encoder/encoder.rs | 4 ++ prusti-viper/src/encoder/procedure_encoder.rs | 24 ++++++++++ prusti-viper/src/ide/encoding_info.rs | 47 +++++++++++++++++++ prusti-viper/src/ide/mod.rs | 2 + prusti-viper/src/ide/vsc_span.rs | 40 ++++++++++++++++ prusti-viper/src/lib.rs | 2 +- prusti-viper/src/verifier.rs | 15 ++++++ prusti/src/ide_helper/compiler_info.rs | 42 ++--------------- 8 files changed, 136 insertions(+), 40 deletions(-) create mode 100644 prusti-viper/src/ide/encoding_info.rs create mode 100644 prusti-viper/src/ide/vsc_span.rs diff --git a/prusti-viper/src/encoder/encoder.rs b/prusti-viper/src/encoder/encoder.rs index c1f0c4fcbed..cb0d340d4de 100644 --- a/prusti-viper/src/encoder/encoder.rs +++ b/prusti-viper/src/encoder/encoder.rs @@ -7,6 +7,7 @@ use ::log::{info, debug, trace}; use prusti_common::utils::identifiers::encode_identifier; use vir_crate::common::check_mode::CheckMode; +use vir_crate::low::ast::statement::MethodCall; use crate::encoder::builtin_encoder::BuiltinEncoder; use crate::encoder::builtin_encoder::BuiltinMethodKind; use crate::encoder::errors::{ErrorManager, SpannedEncodingError, EncodingError}; @@ -62,6 +63,7 @@ use super::mir::{ use super::high::types::{HighTypeEncoderState, HighTypeEncoderInterface}; use super::counterexamples::{MirProcedureMappingInterface, MirProcedureMapping}; use super::counterexamples::DiscriminantsState; +use crate::ide::encoding_info::SpanOfCallContracts; use super::high::to_typed::types::HighToTypedTypeEncoderState; pub struct Encoder<'v, 'tcx: 'v> { @@ -103,6 +105,7 @@ pub struct Encoder<'v, 'tcx: 'v> { /// this requires special care when encoding array/slice accesses which may come with /// bound checks included in the MIR. pub(super) is_encoding_trigger: Cell, + pub spans_of_call_contracts: RefCell>, } pub enum EncodingTask<'tcx> { @@ -181,6 +184,7 @@ impl<'v, 'tcx> Encoder<'v, 'tcx> { specifications_state: SpecificationsState::new(def_spec), mir_procedure_mapping: Default::default(), discriminants_state: Default::default(), + spans_of_call_contracts: Default::default(), } } diff --git a/prusti-viper/src/encoder/procedure_encoder.rs b/prusti-viper/src/encoder/procedure_encoder.rs index cbbd0767652..01260166508 100644 --- a/prusti-viper/src/encoder/procedure_encoder.rs +++ b/prusti-viper/src/encoder/procedure_encoder.rs @@ -23,6 +23,7 @@ use crate::encoder::Encoder; use crate::encoder::snapshot::interface::SnapshotEncoderInterface; use crate::encoder::mir::procedures::encoder::specification_blocks::SpecificationBlocks; use crate::error_unsupported; +use crate::ide::encoding_info::SpanOfCallContracts; use prusti_common::{ config, utils::to_string::ToString, @@ -3387,6 +3388,29 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { substs, ).with_span(call_site_span)? }; + + let mut contracts_spans: Vec = procedure_contract + .functional_precondition(self.encoder.env(), substs) + .iter() + .map(|(ts, _)| self.encoder.env().query.get_def_span(ts)) + .collect(); + let mut postcond = procedure_contract + .functional_postcondition(self.encoder.env(), substs) + .iter() + .map(|(ts, _)| self.encoder.env().query.get_def_span(ts)) + .collect(); + contracts_spans.append(&mut postcond); + let tcx = self.encoder.env().tcx(); + let contract_spans_opt = SpanOfCallContracts::new( + tcx.def_path_str(called_def_id), + call_site_span, + contracts_spans, + &tcx.sess.source_map(), + ); + if let Some(contract_spans) = contract_spans_opt { + self.encoder.spans_of_call_contracts.borrow_mut().push(contract_spans); + } + assert_one_magic_wand(procedure_contract.borrow_infos.len()).with_span(call_site_span)?; // Store a label for the pre state diff --git a/prusti-viper/src/ide/encoding_info.rs b/prusti-viper/src/ide/encoding_info.rs new file mode 100644 index 00000000000..4af86516da9 --- /dev/null +++ b/prusti-viper/src/ide/encoding_info.rs @@ -0,0 +1,47 @@ +use prusti_rustc_interface::hir::def_id::DefId; +use prusti_rustc_interface::span::{source_map::SourceMap, Span}; +use serde::Serialize; + +use super::vsc_span::VscSpan; +/// stores the spans of a calls contracts. +/// obtained during encoding + +#[derive(Serialize, Clone)] +pub struct SpanOfCallContracts { + pub defpath: String, + pub call_span: VscSpan, + pub contracts_spans: Vec, +} + +impl SpanOfCallContracts { + pub fn new( + defpath: String, + call_span: Span, + contracts_spans: Vec, + source_map: &SourceMap + ) -> Option { + let call_span = VscSpan::from_span(&call_span, source_map)?; + let contracts_spans = contracts_spans + .iter() + .filter_map(|sp| VscSpan::from_span(sp, source_map)) + .collect::>(); + Some(Self { + defpath, + call_span, + contracts_spans, + }) + } + +} + +#[derive(Serialize)] +pub struct EncodingInfo { + pub call_contract_spans: Vec, +} + +impl EncodingInfo { + pub fn to_json_string(self) -> String { + serde_json::to_string(&self).unwrap() + } +} + diff --git a/prusti-viper/src/ide/mod.rs b/prusti-viper/src/ide/mod.rs index 3791604496c..9f036d83539 100644 --- a/prusti-viper/src/ide/mod.rs +++ b/prusti-viper/src/ide/mod.rs @@ -1 +1,3 @@ pub mod verification_info; +pub mod encoding_info; +pub mod vsc_span; diff --git a/prusti-viper/src/ide/vsc_span.rs b/prusti-viper/src/ide/vsc_span.rs new file mode 100644 index 00000000000..dd7f57a195d --- /dev/null +++ b/prusti-viper/src/ide/vsc_span.rs @@ -0,0 +1,40 @@ +use prusti_rustc_interface::span::{source_map::SourceMap, Span}; +use serde::{Serialize, Deserialize}; +/// a representation of spans that is more usable with VSCode. +#[derive(Serialize, Clone)] +pub struct VscSpan { + column_end: usize, + column_start: usize, + line_end: usize, + line_start: usize, + file_name: String, + is_primary: bool, + label: Option<()>, + expansion: Option<()>, +} + +impl VscSpan { + pub fn from_span(sp: &Span, sourcemap: &SourceMap) -> Option { + let filename = sourcemap.span_to_filename(*sp); + let diag_filename = sourcemap.filename_for_diagnostics(&filename); + let fname = format!("{}", diag_filename); + + if let Ok((l1, l2)) = sourcemap.is_valid_span(*sp) { + Some(Self { + column_end: l2.col.0, + column_start: l1.col.0, + line_end: l2.line, + line_start: l2.line, + file_name: fname, + // the following 3 are not relevant here, we just want to be + // able to reuse the existing methods and the parser + // for spans in VSCode + is_primary: false, + label: None, + expansion: None, + }) + } else { + None + } + } +} diff --git a/prusti-viper/src/lib.rs b/prusti-viper/src/lib.rs index b4a2a348a09..c260d153543 100644 --- a/prusti-viper/src/lib.rs +++ b/prusti-viper/src/lib.rs @@ -26,4 +26,4 @@ pub mod encoder; mod utils; pub mod verifier; -mod ide; +pub mod ide; diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index f57ede18ee6..7e65f31f9a8 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -28,6 +28,7 @@ use std::collections::HashMap; use serde_json::json; use async_stream::stream; use futures_util::{Stream, StreamExt, pin_mut}; +use std::cell::RefCell; /// A verifier is an object for verifying a single crate, potentially /// many times. @@ -95,6 +96,12 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { }; programs.extend(self.encoder.get_core_proof_programs()); + if config::show_ide_info() { + self.emit_contract_spans(); + } + + + stopwatch.start_next("verifying Viper program"); let requests = self.programs_to_requests(programs); @@ -324,5 +331,13 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { }; return verification_stream.flatten(); } + + pub fn emit_contract_spans(&self) { + let encoding_info = ide::encoding_info::EncodingInfo { + call_contract_spans: self.encoder.spans_of_call_contracts.borrow().to_vec(), + }; + println!("EncodingInfo {}", encoding_info.to_json_string()); + + } } diff --git a/prusti/src/ide_helper/compiler_info.rs b/prusti/src/ide_helper/compiler_info.rs index 1fa279a3f79..a6cada5b982 100644 --- a/prusti/src/ide_helper/compiler_info.rs +++ b/prusti/src/ide_helper/compiler_info.rs @@ -4,6 +4,7 @@ use prusti_rustc_interface::{ hir::def_id::DefId, span::{source_map::SourceMap, Span}, }; +use prusti_viper::ide::vsc_span::VscSpan; use serde::{ser::SerializeStruct, Serialize}; // create some struct storing all the information the IDE will ever need. @@ -28,7 +29,7 @@ impl IdeInfo { .map(|(name, defid, sp)| ProcDef { name, defid, - span: VscSpan::from_span(sp, source_map).unwrap(), + span: VscSpan::from_span(&sp, source_map).unwrap(), }) .collect(); @@ -61,18 +62,6 @@ impl Serialize for ProcDef { } } -/// a representation of spans that is more usable with VSCode. -#[derive(Serialize)] -pub struct VscSpan { - column_end: usize, - column_start: usize, - line_end: usize, - line_start: usize, - file_name: String, - is_primary: bool, - label: Option<()>, - expansion: Option<()>, -} // collect information about the program that will be passed to IDE: fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec { @@ -82,7 +71,7 @@ fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec) -> Vec<(String, DefId, Span)> { return fnvisitor.called_functions; } -impl VscSpan { - pub fn from_span(sp: Span, sourcemap: &SourceMap) -> Option { - let filename = sourcemap.span_to_filename(sp); - let diag_filename = sourcemap.filename_for_diagnostics(&filename); - let fname = format!("{}", diag_filename); - - if let Ok((l1, l2)) = sourcemap.is_valid_span(sp) { - Some(Self { - column_end: l2.col.0, - column_start: l1.col.0, - line_end: l2.line, - line_start: l2.line, - file_name: fname, - // the following 3 are not relevant here, we just want to be - // able to reuse the existing methods and the parser - // for spans in VSCode - is_primary: false, - label: None, - expansion: None, - }) - } else { - None - } - } -} From c3a2c6fb2ebfca18ad26d32e73b477709ae6f2f8 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Thu, 16 Feb 2023 18:19:56 +0100 Subject: [PATCH 31/74] WIP: async processing of verification info --- prusti-server/src/process_verification.rs | 5 +++-- prusti-viper/src/ide/mod.rs | 1 - ...ion_info.rs => ide_verification_result.rs} | 20 ------------------- prusti-viper/src/lib.rs | 2 +- prusti-viper/src/verifier.rs | 10 +++++----- 5 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 prusti-viper/src/ide/mod.rs rename prusti-viper/src/{ide/verification_info.rs => ide_verification_result.rs} (57%) diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index caa7b338e60..2bafad7f85f 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -267,12 +267,13 @@ fn polling_function(viper_arc: &Arc, let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); - // also matches the "-aux" quantifiers generated + // also matches the "-aux" and "_precondition" quantifiers generated // we encoded the position id in the line and column number since this is not used by // prusti either way if q_name.starts_with("prog.l") { let no_pref = q_name.strip_prefix("prog.l").unwrap(); - let stripped = no_pref.strip_suffix("-aux").or(Some(no_pref)).unwrap(); + let stripped = no_pref.strip_suffix("-aux").or( + no_pref.strip_suffix("_precondition")).unwrap_or(no_pref); let parsed = stripped.parse::(); match parsed { Ok(pos_id) => { diff --git a/prusti-viper/src/ide/mod.rs b/prusti-viper/src/ide/mod.rs deleted file mode 100644 index 3791604496c..00000000000 --- a/prusti-viper/src/ide/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod verification_info; diff --git a/prusti-viper/src/ide/verification_info.rs b/prusti-viper/src/ide_verification_result.rs similarity index 57% rename from prusti-viper/src/ide/verification_info.rs rename to prusti-viper/src/ide_verification_result.rs index 5aefe7bcbd8..ea95948a27c 100644 --- a/prusti-viper/src/ide/verification_info.rs +++ b/prusti-viper/src/ide_verification_result.rs @@ -1,26 +1,6 @@ use viper::VerificationResult; use serde::Serialize; -#[derive(Serialize)] -pub struct VerificationInfo { - result_list: Vec -} - -impl VerificationInfo { - pub fn new() -> Self { - Self { - result_list: vec![], - } - } - pub fn add(&mut self, res: &VerificationResult) { - let ide_res = IdeVerificationResult::from_res(res); - self.result_list.push(ide_res); - } - pub fn to_json_string(self) -> String { - serde_json::to_string(&self).unwrap() - } -} - // since the existing verification result // is not as trivially passed in json #[derive(Serialize)] diff --git a/prusti-viper/src/lib.rs b/prusti-viper/src/lib.rs index b4a2a348a09..0ccc702fba0 100644 --- a/prusti-viper/src/lib.rs +++ b/prusti-viper/src/lib.rs @@ -26,4 +26,4 @@ pub mod encoder; mod utils; pub mod verifier; -mod ide; +mod ide_verification_result; diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index ede296ffe63..d869ec63245 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -12,7 +12,7 @@ use vir_crate::common::check_mode::CheckMode; use crate::encoder::Encoder; use crate::encoder::counterexamples::counterexample_translation; use crate::encoder::counterexamples::counterexample_translation_refactored; -use crate::ide; +use crate::ide_verification_result::IdeVerificationResult; use prusti_interface::data::VerificationResult; use prusti_interface::data::VerificationTask; use prusti_interface::environment::Environment; @@ -136,14 +136,16 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { // if we are not running in an ide, we want the errors to be reported sortedly let mut prusti_errors: Vec<_> = vec![]; - let mut verification_info = ide::verification_info::VerificationInfo::new(); pin_mut!(verification_messages); while let Some((program_name, server_msg)) = verification_messages.next().await { match server_msg { ServerMessage::Termination(result) => { - verification_info.add(&result); + PrustiError::message( + serde_json::to_string(&IdeVerificationResult::from_res(&result)).unwrap(), + DUMMY_SP.into() + ).emit(&self.env.diagnostic); match result.result_type { // nothing to do viper::VerificationResultType::Success => (), @@ -274,8 +276,6 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { debug!("Prusti error: {:?}", prusti_error); prusti_error.emit(&self.env.diagnostic); } - } else { - println!("VerificationInfo {}", verification_info.to_json_string()); } if encoding_errors_count != 0 { From 5ed232e0a6cb934981eda1dea64537761d7d3b3b Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sun, 19 Feb 2023 19:36:55 +0100 Subject: [PATCH 32/74] (IDE) Contract Spans are now also collected for pedges and termatinations (altough ranges are not always accurate) --- prusti-viper/src/encoder/procedure_encoder.rs | 80 ++++++++++++++----- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/prusti-viper/src/encoder/procedure_encoder.rs b/prusti-viper/src/encoder/procedure_encoder.rs index 01260166508..1cc7291bd1c 100644 --- a/prusti-viper/src/encoder/procedure_encoder.rs +++ b/prusti-viper/src/encoder/procedure_encoder.rs @@ -3388,28 +3388,11 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { substs, ).with_span(call_site_span)? }; - - let mut contracts_spans: Vec = procedure_contract - .functional_precondition(self.encoder.env(), substs) - .iter() - .map(|(ts, _)| self.encoder.env().query.get_def_span(ts)) - .collect(); - let mut postcond = procedure_contract - .functional_postcondition(self.encoder.env(), substs) - .iter() - .map(|(ts, _)| self.encoder.env().query.get_def_span(ts)) - .collect(); - contracts_spans.append(&mut postcond); - let tcx = self.encoder.env().tcx(); - let contract_spans_opt = SpanOfCallContracts::new( - tcx.def_path_str(called_def_id), + self.store_contract_spans( + called_def_id, + &procedure_contract, call_site_span, - contracts_spans, - &tcx.sess.source_map(), ); - if let Some(contract_spans) = contract_spans_opt { - self.encoder.spans_of_call_contracts.borrow_mut().push(contract_spans); - } assert_one_magic_wand(procedure_contract.borrow_infos.len()).with_span(call_site_span)?; @@ -3552,6 +3535,63 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { Ok(stmts) } + fn store_contract_spans( + &self, + called_def_id: ProcedureDefId, + contract: &ProcedureContract<'tcx>, + call_site_span: Span, + ) { + let mut precondition_spans:Vec = contract + .functional_precondition(self.encoder.env(), self.substs) + .iter() + .map(|(ts,_)| self.encoder.env().query.get_def_span(ts)) + .collect(); + let mut postcondition_spans: Vec = contract + .functional_postcondition(self.encoder.env(), self.substs) + .iter() + .map(|(ts,_)| self.encoder.env().query.get_def_span(ts)) + .collect(); + let purity: Option = contract + .specification.purity + .extract_with_selective_replacement() + .copied().unwrap_or(None) + .map(|id| self.encoder.env().query.get_def_span(id)); + let terminates: Option = contract.specification.terminates + .extract_with_selective_replacement() + .copied().unwrap_or(None) + .map(|id| self.encoder.env().query.get_def_span(id)); + + // pledges store 2 defids, let's put in both.. + // let mut pledges_lhs: Vec = contract.pledges().filter_map(|x| x.lhs) + // .map(|id| self.encoder.env().query.get_def_span(id)) + // .collect(); + // let mut pledges_rhs: Vec = contract.pledges().map(|x| x.rhs) + // .map(|id| self.encoder.env().query.get_def_span(id)) + // .collect(); + let mut result = vec![]; + result.append(&mut precondition_spans); + result.append(&mut postcondition_spans); + // result.append(&mut pledges_rhs); + // result.append(&mut pledges_lhs); + if let Some(purity) = purity { + result.push(purity); + } + if let Some(terminates) = terminates { + result.push(terminates); + } + // let mut pure_opt = contract. + let tcx = self.encoder.env().tcx(); + let contract_spans_opt = SpanOfCallContracts::new( + tcx.def_path_str(called_def_id), + call_site_span, + result, + &tcx.sess.source_map(), + ); + if let Some(contract_spans) = contract_spans_opt { + self.encoder.spans_of_call_contracts.borrow_mut().push(contract_spans); + } + } + #[allow(clippy::too_many_arguments)] fn encode_pure_function_call( &mut self, From 91b91c4b52c8406323a967476d7236d77ace6ecc Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sun, 19 Feb 2023 19:53:59 +0100 Subject: [PATCH 33/74] Cleaning up unused imports and debugging print statements --- prusti-viper/src/encoder/encoder.rs | 1 - prusti-viper/src/ide/encoding_info.rs | 1 - prusti-viper/src/ide/vsc_span.rs | 2 +- prusti-viper/src/verifier.rs | 1 - prusti/src/callbacks.rs | 5 ----- prusti/src/ide_helper/call_finder.rs | 13 ------------- prusti/src/ide_helper/compiler_info.rs | 1 - 7 files changed, 1 insertion(+), 23 deletions(-) diff --git a/prusti-viper/src/encoder/encoder.rs b/prusti-viper/src/encoder/encoder.rs index cb0d340d4de..24487801e03 100644 --- a/prusti-viper/src/encoder/encoder.rs +++ b/prusti-viper/src/encoder/encoder.rs @@ -7,7 +7,6 @@ use ::log::{info, debug, trace}; use prusti_common::utils::identifiers::encode_identifier; use vir_crate::common::check_mode::CheckMode; -use vir_crate::low::ast::statement::MethodCall; use crate::encoder::builtin_encoder::BuiltinEncoder; use crate::encoder::builtin_encoder::BuiltinMethodKind; use crate::encoder::errors::{ErrorManager, SpannedEncodingError, EncodingError}; diff --git a/prusti-viper/src/ide/encoding_info.rs b/prusti-viper/src/ide/encoding_info.rs index 4af86516da9..02c5b16188d 100644 --- a/prusti-viper/src/ide/encoding_info.rs +++ b/prusti-viper/src/ide/encoding_info.rs @@ -1,4 +1,3 @@ -use prusti_rustc_interface::hir::def_id::DefId; use prusti_rustc_interface::span::{source_map::SourceMap, Span}; use serde::Serialize; diff --git a/prusti-viper/src/ide/vsc_span.rs b/prusti-viper/src/ide/vsc_span.rs index dd7f57a195d..7b93b458396 100644 --- a/prusti-viper/src/ide/vsc_span.rs +++ b/prusti-viper/src/ide/vsc_span.rs @@ -1,5 +1,5 @@ use prusti_rustc_interface::span::{source_map::SourceMap, Span}; -use serde::{Serialize, Deserialize}; +use serde::Serialize; /// a representation of spans that is more usable with VSCode. #[derive(Serialize, Clone)] pub struct VscSpan { diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 7e65f31f9a8..6972dc6f840 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -28,7 +28,6 @@ use std::collections::HashMap; use serde_json::json; use async_stream::stream; use futures_util::{Stream, StreamExt, pin_mut}; -use std::cell::RefCell; /// A verifier is an object for verifying a single crate, potentially /// many times. diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index cf89220d69b..b0a3de10dce 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -134,7 +134,6 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { if !config::no_verify() && !config::skip_verification() { if config::selective_verify().is_none() { - println!("verifying everything\n\n\n"); let verification_task = VerificationTask { procedures: annotated_procedures, types, @@ -142,10 +141,6 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { verify(env, def_spec, verification_task); } else { let target_def_path = config::selective_verify().unwrap(); - println!( - "Selective Verification invoked for method {}\n\n\n", - target_def_path - ); let procedures = annotated_procedures .into_iter() .filter(|x| env.name.get_unique_item_name(*x) == target_def_path) diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index ef6761b43e1..36d081f9387 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -54,20 +54,13 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { intravisit::walk_expr(self, expr); match expr.kind { ExprKind::Call(e1, _e2) => { - println!("found a call: resolving!"); - if let ExprKind::Path(ref qself) = e1.kind { let tyck_res = self.tcx.typeck(e1.hir_id.owner.def_id); let res = tyck_res.qpath_res(qself, e1.hir_id); if let prusti_rustc_interface::hir::def::Res::Def(_, def_id) = res { let defpath = self.tcx.def_path_str(def_id); - println!("Call DefPath: {}", defpath); self.called_functions.push((defpath, def_id, expr.span)); - } else { - println!("Resolving a call failed!\n\n\n"); } - } else { - println!("Resolving a Call failed!\n\n\n"); } } ExprKind::MethodCall(_path, _e1, _e2, sp) => { @@ -106,16 +99,10 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { // TODO: replace with is_local once we are not debugging anymore // no need to create external specs for local methods if defpath_unresolved == defpath_resolved { - println!("Defpaths for binary operation were equal"); self.called_functions.push((defpath_resolved, resolved_def_id, expr.span)); } else { // For binary operations this will be the operation // from the standard libary and the "overriding" method - println!( - "\n\n\n\nFound two differing defpaths for binary operation" - ); - println!("1. {}", defpath_resolved); - println!("2. {}", defpath_unresolved); self.called_functions.push((defpath_resolved, resolved_def_id,expr.span)); self.called_functions.push((defpath_unresolved, method_def_id, expr.span)); diff --git a/prusti/src/ide_helper/compiler_info.rs b/prusti/src/ide_helper/compiler_info.rs index a6cada5b982..b3a8edb62c6 100644 --- a/prusti/src/ide_helper/compiler_info.rs +++ b/prusti/src/ide_helper/compiler_info.rs @@ -70,7 +70,6 @@ fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec Date: Mon, 20 Feb 2023 15:18:11 +0100 Subject: [PATCH 34/74] Some minor output changes --- prusti-viper/src/verifier.rs | 7 +++---- prusti/src/callbacks.rs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index d869ec63245..c69290f1efe 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -142,10 +142,9 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { while let Some((program_name, server_msg)) = verification_messages.next().await { match server_msg { ServerMessage::Termination(result) => { - PrustiError::message( - serde_json::to_string(&IdeVerificationResult::from_res(&result)).unwrap(), - DUMMY_SP.into() - ).emit(&self.env.diagnostic); + if config::show_ide_info() { + eprintln!("ide_verification_result{}", serde_json::to_string(&IdeVerificationResult::from_res(&result)).unwrap()); + } match result.result_type { // nothing to do viper::VerificationResultType::Success => (), diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 6b55c7f0084..9a907f61f4e 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -128,7 +128,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { let out = serde_json::to_string(&compiler_info).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... - println!("CompilerInfo {}", out); + eprintln!("CompilerInfo{out}"); } // collect and output Information used by IDE: From f886f8b3627b3282ecb3d8582c6614191be47aca Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Mon, 20 Feb 2023 15:54:54 +0100 Subject: [PATCH 35/74] Minor fixes --- prusti-viper/src/verifier.rs | 4 ++-- prusti/src/callbacks.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 7afcdd31af0..643b3a33bff 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -12,7 +12,7 @@ use vir_crate::common::check_mode::CheckMode; use crate::encoder::Encoder; use crate::encoder::counterexamples::counterexample_translation; use crate::encoder::counterexamples::counterexample_translation_refactored; -use crate::ide_verification_result::IdeVerificationResult; +use crate::ide::{self, ide_verification_result::IdeVerificationResult}; use prusti_interface::data::VerificationResult; use prusti_interface::data::VerificationTask; use prusti_interface::environment::Environment; @@ -349,7 +349,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { let encoding_info = ide::encoding_info::EncodingInfo { call_contract_spans: self.encoder.spans_of_call_contracts.borrow().to_vec(), }; - println!("EncodingInfo {}", encoding_info.to_json_string()); + eprintln!("EncodingInfo{}", encoding_info.to_json_string()); } } diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 197b61b587c..d434ad44603 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -19,7 +19,6 @@ use prusti_rustc_interface::{ }, session::Session, }; -use ::log::info; #[derive(Default)] pub struct PrustiCompilerCalls; From 9ef451a83c956eaba354816f5df6c4183480b2fc Mon Sep 17 00:00:00 2001 From: hegglinc Date: Tue, 21 Feb 2023 08:13:19 +0100 Subject: [PATCH 36/74] give spans of pledges to IDE and filter out trusted methods and predicates from items offered for selective verification --- prusti-viper/src/encoder/procedure_encoder.rs | 23 ++++++---- prusti/src/callbacks.rs | 5 +-- prusti/src/ide_helper/compiler_info.rs | 44 ++++++++++++++----- 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/prusti-viper/src/encoder/procedure_encoder.rs b/prusti-viper/src/encoder/procedure_encoder.rs index 1cc7291bd1c..287f58ae29f 100644 --- a/prusti-viper/src/encoder/procedure_encoder.rs +++ b/prusti-viper/src/encoder/procedure_encoder.rs @@ -3561,18 +3561,23 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { .copied().unwrap_or(None) .map(|id| self.encoder.env().query.get_def_span(id)); - // pledges store 2 defids, let's put in both.. - // let mut pledges_lhs: Vec = contract.pledges().filter_map(|x| x.lhs) - // .map(|id| self.encoder.env().query.get_def_span(id)) - // .collect(); - // let mut pledges_rhs: Vec = contract.pledges().map(|x| x.rhs) - // .map(|id| self.encoder.env().query.get_def_span(id)) - // .collect(); + // for each pledge, if it has a lefthandside, join the 2, otherwise + // just return span of the righthandside + let mut pledges: Vec = contract.pledges() + .map(|pledge| { + if let Some(lhs_defid) = pledge.lhs { + let lhs_span = self.encoder.env().query.get_def_span(lhs_defid); + let rhs_span = self.encoder.env().query.get_def_span(pledge.rhs); + lhs_span.to(rhs_span) + } else { + self.encoder.env().query.get_def_span(pledge.rhs) + } + }) + .collect(); let mut result = vec![]; result.append(&mut precondition_spans); result.append(&mut postcondition_spans); - // result.append(&mut pledges_rhs); - // result.append(&mut pledges_lhs); + result.append(&mut pledges); if let Some(purity) = purity { result.push(purity); } diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index b0a3de10dce..0be0ab2a9ef 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -119,11 +119,8 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // determine procedures that have to be verified. let (annotated_procedures, types) = env.get_annotated_procedures_and_types(); - // todo: selective verification, filter procedures that are - // part of verification_task - if config::show_ide_info() && !config::no_verify() { - let compiler_info = compiler_info::IdeInfo::collect(&env, &annotated_procedures); + let compiler_info = compiler_info::IdeInfo::collect(&env, &annotated_procedures, &def_spec); let out = serde_json::to_string(&compiler_info).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... diff --git a/prusti/src/ide_helper/compiler_info.rs b/prusti/src/ide_helper/compiler_info.rs index b3a8edb62c6..985f66e30d0 100644 --- a/prusti/src/ide_helper/compiler_info.rs +++ b/prusti/src/ide_helper/compiler_info.rs @@ -1,5 +1,5 @@ use super::{call_finder, query_signature}; -use prusti_interface::environment::Environment; +use prusti_interface::{environment::Environment, specs::typed}; use prusti_rustc_interface::{ hir::def_id::DefId, span::{source_map::SourceMap, Span}, @@ -21,8 +21,8 @@ pub struct IdeInfo { } impl IdeInfo { - pub fn collect(env: &Environment<'_>, procedures: &Vec) -> Self { - let procs = collect_procedures(env, procedures); + pub fn collect(env: &Environment<'_>, procedures: &Vec, def_spec: &typed::DefSpecificationMap) -> Self { + let procs = collect_procedures(env, procedures, def_spec); let source_map = env.tcx().sess.source_map(); let fncalls: Vec = collect_fncalls(env) .into_iter() @@ -64,19 +64,39 @@ impl Serialize for ProcDef { // collect information about the program that will be passed to IDE: -fn collect_procedures(env: &Environment<'_>, procedures: &Vec) -> Vec { +fn collect_procedures(env: &Environment<'_>, procedures: &Vec, def_spec: &typed::DefSpecificationMap) -> Vec { let sourcemap: &SourceMap = env.tcx().sess.source_map(); let mut procs = Vec::new(); - for procedure in procedures { - let defpath = env.name.get_unique_item_name(*procedure); - let span = env.query.get_def_span(procedure); + for defid in procedures { + let defpath = env.name.get_unique_item_name(*defid); + let span = env.query.get_def_span(defid); let vscspan = VscSpan::from_span(&span, sourcemap).unwrap(); + + // Filter out the predicates and trusted methods, + // since we don't want to allow selective verification + // for them + let mut is_predicate = false; + let mut is_trusted = false; - procs.push(ProcDef { - name: defpath, - defid: *procedure, - span: vscspan, - }); + let proc_spec_opt = def_spec.get_proc_spec(defid); + if let Some(proc_spec) = proc_spec_opt { + let kind_spec = proc_spec.base_spec.kind.extract_with_selective_replacement(); + let trusted_spec = proc_spec.base_spec.trusted.extract_with_selective_replacement(); + if let Some(typed::ProcedureSpecificationKind::Predicate(..)) = kind_spec { + is_predicate = true; + } + if let Some(true) = trusted_spec { + is_trusted = true; + } + } + + if !is_trusted && !is_predicate { + procs.push(ProcDef { + name: defpath, + defid: *defid, + span: vscspan, + }); + } } procs } From b6ba6f6c57101c13f7a5d8c23788958bb3f88580 Mon Sep 17 00:00:00 2001 From: cedric Date: Tue, 21 Feb 2023 09:25:56 +0100 Subject: [PATCH 37/74] updated ide-parts to new compiler version --- prusti/src/ide_helper/query_signature.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index bbb52ede5a7..8b769da25ba 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -245,13 +245,13 @@ impl FunctionSignature { let sig = tcx.fn_sig(defid).skip_binder(); let arg_types = sig.inputs().iter(); let arg_names = tcx.fn_arg_names(defid); - let output = sig.output(); + let output = sig.output().skip_binder(); let return_type = if output.is_unit() { None } else { Some(output.to_string()) }; let arguments: Vec<(String, String)> = arg_names .iter() .zip(arg_types) - .map(|(name, ty)| (name.to_string(), ty.to_string())) + .map(|(name, ty)| (name.to_string(), ty.skip_binder().to_string())) .collect(); let generics = generic_params(tcx, defid); From f27499ce17609bbac6db2a4c0f66b46b9f8116dd Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Tue, 21 Feb 2023 10:57:32 +0100 Subject: [PATCH 38/74] Emit everything in a compiler note/message --- prusti-viper/src/verifier.rs | 10 ++++++++-- prusti/src/callbacks.rs | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 643b3a33bff..24bd87e1e43 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -149,7 +149,11 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { match server_msg { ServerMessage::Termination(result) => { if config::show_ide_info() { - eprintln!("ide_verification_result{}", serde_json::to_string(&IdeVerificationResult::from_res(&result)).unwrap()); + PrustiError::message( + format!("ide_verification_result{}", + serde_json::to_string(&IdeVerificationResult::from_res(&result)).unwrap() + ), DUMMY_SP.into() + ).emit(&self.env.diagnostic); } match result.result_type { // nothing to do @@ -349,7 +353,9 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { let encoding_info = ide::encoding_info::EncodingInfo { call_contract_spans: self.encoder.spans_of_call_contracts.borrow().to_vec(), }; - eprintln!("EncodingInfo{}", encoding_info.to_json_string()); + PrustiError::message( + format!("EncodingInfo{}", encoding_info.to_json_string()), DUMMY_SP.into() + ).emit(&self.env.diagnostic); } } diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 1588bd97308..a67848f69c1 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -7,6 +7,7 @@ use prusti_interface::{ data::VerificationTask, environment::{mir_storage, Environment}, specs::{self, cross_crate::CrossCrateSpecs, is_spec_fn}, + PrustiError, }; use prusti_rustc_interface::{ driver::Compilation, @@ -18,6 +19,7 @@ use prusti_rustc_interface::{ TyCtxt, }, session::Session, + span::DUMMY_SP, }; #[derive(Default)] @@ -140,7 +142,9 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { let out = serde_json::to_string(&compiler_info).unwrap(); // probably should make this part of compilers output.. // actually not sure which way is better... - eprintln!("CompilerInfo{out}"); + eprintln!(""); + PrustiError::message(format!("CompilerInfo{out}"), DUMMY_SP.into()) + .emit(&env.diagnostic); } // collect and output Information used by IDE: From cf53b8e259a95954887b8299ffaf239910bebe06 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Tue, 21 Feb 2023 11:18:23 +0100 Subject: [PATCH 39/74] Make every token CamelCase --- prusti-viper/src/verifier.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 24bd87e1e43..9dbb6eaed3e 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -150,7 +150,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ServerMessage::Termination(result) => { if config::show_ide_info() { PrustiError::message( - format!("ide_verification_result{}", + format!("IdeVerificationResult{}", serde_json::to_string(&IdeVerificationResult::from_res(&result)).unwrap() ), DUMMY_SP.into() ).emit(&self.env.diagnostic); @@ -248,7 +248,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { n += *insts; } PrustiError::message( - format!("quantifier_instantiations_message{}", + format!("QuantifierInstantiationsMessage{}", json!({"instantiations": n, "method": program_name}), ), span.clone() ).emit(&self.env.diagnostic); @@ -263,7 +263,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { match error_manager.position_manager().get_span_from_id(pos_id) { Some(span) => { PrustiError::message( - format!("quantifier_chosen_triggers_message{}", + format!("QuantifierChosenTriggersMessage{}", json!({"viper_quant": viper_quant, "triggers": triggers}), ), span.clone() ).emit(&self.env.diagnostic); From 158b9ad7c0c5eccb40a8b02c818db60f94a88363 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Tue, 21 Feb 2023 12:40:12 +0100 Subject: [PATCH 40/74] Set config variable for viper message reporting to default false. --- prusti-utils/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index e47478b84a0..efaf845bc9c 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -104,7 +104,7 @@ lazy_static::lazy_static! { settings.set_default("smt_qi_eager_threshold", 1000).unwrap(); settings.set_default("smt_qi_profile", true).unwrap(); settings.set_default("smt_qi_profile_freq", 10000).unwrap(); - settings.set_default("report_viper_messages", true).unwrap(); + settings.set_default("report_viper_messages", false).unwrap(); settings.set_default("use_more_complete_exhale", true).unwrap(); settings.set_default("skip_unsupported_features", false).unwrap(); settings.set_default("internal_errors_as_warnings", false).unwrap(); From 195af4d7b0491b71fe3f1f299081e79c7f0e5807 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Wed, 22 Feb 2023 10:05:11 +0100 Subject: [PATCH 41/74] Proper camelCase messages --- prusti-viper/src/verifier.rs | 8 ++++---- prusti/src/callbacks.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 9dbb6eaed3e..e41f946421c 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -150,7 +150,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ServerMessage::Termination(result) => { if config::show_ide_info() { PrustiError::message( - format!("IdeVerificationResult{}", + format!("ideVerificationResult{}", serde_json::to_string(&IdeVerificationResult::from_res(&result)).unwrap() ), DUMMY_SP.into() ).emit(&self.env.diagnostic); @@ -248,7 +248,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { n += *insts; } PrustiError::message( - format!("QuantifierInstantiationsMessage{}", + format!("quantifierInstantiationsMessage{}", json!({"instantiations": n, "method": program_name}), ), span.clone() ).emit(&self.env.diagnostic); @@ -263,7 +263,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { match error_manager.position_manager().get_span_from_id(pos_id) { Some(span) => { PrustiError::message( - format!("QuantifierChosenTriggersMessage{}", + format!("quantifierChosenTriggersMessage{}", json!({"viper_quant": viper_quant, "triggers": triggers}), ), span.clone() ).emit(&self.env.diagnostic); @@ -354,7 +354,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { call_contract_spans: self.encoder.spans_of_call_contracts.borrow().to_vec(), }; PrustiError::message( - format!("EncodingInfo{}", encoding_info.to_json_string()), DUMMY_SP.into() + format!("encodingInfo{}", encoding_info.to_json_string()), DUMMY_SP.into() ).emit(&self.env.diagnostic); } diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index a67848f69c1..d753017dcbc 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -143,7 +143,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // probably should make this part of compilers output.. // actually not sure which way is better... eprintln!(""); - PrustiError::message(format!("CompilerInfo{out}"), DUMMY_SP.into()) + PrustiError::message(format!("compilerInfo{out}"), DUMMY_SP.into()) .emit(&env.diagnostic); } From 91f4ff9902f1bf46cc3e1985ed52d8ee01089143 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Wed, 22 Feb 2023 13:18:35 +0100 Subject: [PATCH 42/74] Can not automatically generate extern_specs for local methods anymore --- prusti/src/callbacks.rs | 3 --- prusti/src/ide_helper/call_finder.rs | 18 ++++++++---------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index d753017dcbc..68646bb1901 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -140,9 +140,6 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { if config::show_ide_info() && !config::no_verify() { let compiler_info = compiler_info::IdeInfo::collect(&env, &annotated_procedures, &def_spec); let out = serde_json::to_string(&compiler_info).unwrap(); - // probably should make this part of compilers output.. - // actually not sure which way is better... - eprintln!(""); PrustiError::message(format!("compilerInfo{out}"), DUMMY_SP.into()) .emit(&env.diagnostic); } diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 36d081f9387..07d3d4ca299 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -58,8 +58,10 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { let tyck_res = self.tcx.typeck(e1.hir_id.owner.def_id); let res = tyck_res.qpath_res(qself, e1.hir_id); if let prusti_rustc_interface::hir::def::Res::Def(_, def_id) = res { - let defpath = self.tcx.def_path_str(def_id); - self.called_functions.push((defpath, def_id, expr.span)); + if !def_id.as_local().is_some() { + let defpath = self.tcx.def_path_str(def_id); + self.called_functions.push((defpath, def_id, expr.span)); + } } } } @@ -67,13 +69,11 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { let resolve_res = self.resolve_expression(expr); match resolve_res { Ok((method_def_id, resolved_def_id)) => { - let _is_local = method_def_id.as_local().is_some(); + let is_local = method_def_id.as_local().is_some(); let defpath_unresolved = self.tcx.def_path_str(method_def_id); let defpath_resolved = self.tcx.def_path_str(resolved_def_id); - if true { - // TODO: replace with is_local once we are not debugging anymore - // no need to create external specs for local methods + if !is_local { if defpath_unresolved == defpath_resolved { self.called_functions.push((defpath_resolved, resolved_def_id, sp)); } else { @@ -91,13 +91,11 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { // this will already fail for standard addition match resolve_res { Ok((method_def_id, resolved_def_id)) => { - let _is_local = method_def_id.as_local().is_some(); + let is_local = method_def_id.as_local().is_some(); let defpath_unresolved = self.tcx.def_path_str(method_def_id); let defpath_resolved = self.tcx.def_path_str(resolved_def_id); - if true { - // TODO: replace with is_local once we are not debugging anymore - // no need to create external specs for local methods + if !is_local { if defpath_unresolved == defpath_resolved { self.called_functions.push((defpath_resolved, resolved_def_id, expr.span)); } else { From 82e606333a58edae470fa9ce2f604a9f3494870f Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Wed, 22 Feb 2023 16:26:33 +0100 Subject: [PATCH 43/74] Fix GlobalRef detaching warning. Add a few comments. Remove some debugging leftovers. --- prusti-server/src/process_verification.rs | 31 +++++++++++++---------- prusti-smt-solver/src/context.rs | 3 --- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index 2bafad7f85f..f98789b0096 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -31,10 +31,10 @@ pub struct VerificationRequestProcessing { mtx_tx_verreq: sync::Mutex>, } -// one structure that lives for all the requests and has a single thread working on all the -// requests sequentially -// on reception of a verification request, we send it through a channel to the already running -// thread +/// A structure that lives for all the requests and has a single thread working on all the +/// requests sequentially. +/// On reception of a verification request, we send it through a channel to the already running +/// thread. impl VerificationRequestProcessing { pub fn new() -> Self { let (tx_servermsg, rx_servermsg) = mpsc::channel(); @@ -76,6 +76,7 @@ impl VerificationRequestProcessing { .unwrap() .send(ServerRequest::Verification(request)) .unwrap(); + // return a stream that has as last non-None message the ServerMessage::Termination futures::stream::unfold(false, move |done: bool| async move { if done { return None; @@ -233,25 +234,27 @@ fn verify_and_poll_msgs(verification_context: &viper::VerificationContext, sender: mpsc::Sender) -> VerificationResultType { let mut result_type = VerificationResultType::Success; + + // get the reporter global reference outside of the thread scope because it needs to + // be dropped by thread attached to the jvm. This is also why we pass it as reference + // and not per value + let env = &verification_context.env(); + let jni = JniUtils::new(env); + let verifier_wrapper = silver::verifier::Verifier::with(env); + let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); + let rep_glob_ref = env.new_global_ref(reporter).unwrap(); + // start thread for polling messages thread::scope(|scope| { - // get the reporter - let env = &verification_context.env(); - let jni = JniUtils::new(env); - let verifier_wrapper = silver::verifier::Verifier::with(env); - let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); - let rep_glob_ref = env.new_global_ref(reporter).unwrap(); - - let polling_thread = scope.spawn(|| polling_function(viper_arc, rep_glob_ref, sender)); + let polling_thread = scope.spawn(|| polling_function(viper_arc, &rep_glob_ref, sender)); result_type = verifier.verify(viper_program); - // FIXME: here the global ref is dropped from a detached thread polling_thread.join().unwrap(); }); result_type } fn polling_function(viper_arc: &Arc, - rep_glob_ref: jni::objects::GlobalRef, + rep_glob_ref: &jni::objects::GlobalRef, sender: mpsc::Sender) { let verification_context = viper_arc.attach_current_thread(); let env = verification_context.env(); diff --git a/prusti-smt-solver/src/context.rs b/prusti-smt-solver/src/context.rs index 7ddb76cb560..6fe92557a18 100644 --- a/prusti-smt-solver/src/context.rs +++ b/prusti-smt-solver/src/context.rs @@ -5,7 +5,6 @@ use async_std::{ sync::Mutex, }; use std::{env::VarError, str::FromStr}; -use log::trace; #[derive(Debug)] pub(super) struct Context { @@ -16,7 +15,6 @@ pub(super) struct Context { impl Context { pub(crate) async fn new() -> Self { - trace!("BBBB"); let (log_file_path, z3_trace_path) = if let Some(port) = read_integer("PRUSTI_SMT_SOLVER_MANAGER_PORT") { let stream = TcpStream::connect(("127.0.0.1", port)).await.unwrap(); @@ -31,7 +29,6 @@ impl Context { } else { (None, None) }; - trace!("{:?} BBBB {:?}", log_file_path, z3_trace_path); let quantifier_instantiations_bound_global = read_integer("PRUSTI_SMT_QI_BOUND_GLOBAL"); let log_file = { if let Ok(value) = std::env::var("PRUSTI_LOG_SMT_INTERACTION") { From 849b18d66fa9269ee232214c443180125a3feec2 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Wed, 22 Feb 2023 16:39:32 +0100 Subject: [PATCH 44/74] Better comment for the VerificationRequestProcessing global variable. --- prusti-server/src/server.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index 866ce157385..bd587f3d8c9 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -41,7 +41,10 @@ pub fn spawn_server_thread() -> SocketAddr { receiver.recv().unwrap() } -// initialize on first access +// This VerificationRequestProcessing object is starting the verification thread (for more details +// see the file process_verification.rs). +// It has to have a static lifetime because warp websockets need their closures to have a static +// lifetime and we need to access this object in them. static VERIFICATION_REQUEST_PROCESSING: Lazy = Lazy::new(|| { VerificationRequestProcessing::new() }); fn listen_on_port_with_address_callback(port: u16, address_callback: F) -> ! From 33a2e6384b22d5ad7cda05624a82e88bcfd74928 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Wed, 22 Feb 2023 17:45:06 +0100 Subject: [PATCH 45/74] Remove leftover comment. --- viper/src/verification_result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viper/src/verification_result.rs b/viper/src/verification_result.rs index 6e0107bbafc..de222838b26 100644 --- a/viper/src/verification_result.rs +++ b/viper/src/verification_result.rs @@ -32,7 +32,7 @@ impl VerificationResult { pub enum VerificationResultType{ /// The program verified. Success, - /// The program did not verify. Again with quantifier statistics. + /// The program did not verify. Failure(Vec), /// The program has consistency errors. ConsistencyErrors(Vec), From 1b1242d817ff114499da0447854d9bded15a9357 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Wed, 22 Feb 2023 22:35:59 +0100 Subject: [PATCH 46/74] Correct formatting and removed all clippy warnings except for 2 --- prusti-common/src/vir/to_viper.rs | 6 +- .../src/environment/diagnostic.rs | 6 +- prusti-interface/src/prusti_error.rs | 5 +- prusti-server/src/client.rs | 39 +++- prusti-server/src/process_verification.rs | 185 +++++++++------ prusti-server/src/server.rs | 28 ++- prusti-server/src/server_message.rs | 12 +- prusti-utils/src/config.rs | 2 +- .../mir/pure/specifications/encoder_poly.rs | 6 +- prusti-viper/src/encoder/procedure_encoder.rs | 2 +- .../src/ide/ide_verification_result.rs | 18 +- prusti-viper/src/ide/vsc_span.rs | 2 +- prusti-viper/src/verifier.rs | 211 +++++++++++------- prusti/src/callbacks.rs | 5 +- prusti/src/ide_helper/call_finder.rs | 71 +++--- prusti/src/ide_helper/compiler_info.rs | 30 ++- prusti/src/ide_helper/fake_error.rs | 13 +- prusti/src/ide_helper/query_signature.rs | 87 ++++---- prusti/src/verifier.rs | 2 - viper/src/verification_result.rs | 3 +- viper/src/verifier.rs | 7 +- vir/defs/polymorphic/ast/expr.rs | 8 +- 22 files changed, 445 insertions(+), 303 deletions(-) diff --git a/prusti-common/src/vir/to_viper.rs b/prusti-common/src/vir/to_viper.rs index 35e109f112b..e62a95a612c 100644 --- a/prusti-common/src/vir/to_viper.rs +++ b/prusti-common/src/vir/to_viper.rs @@ -661,14 +661,12 @@ impl<'v> ToViper<'v, viper::Expr<'v>> for Expr { right.to_viper(context, ast), pos.to_viper(context, ast), ), - Expr::ForAll(ref vars, ref triggers, ref body, ref pos) => { - ast.forall_with_pos( + Expr::ForAll(ref vars, ref triggers, ref body, ref pos) => ast.forall_with_pos( &vars.to_viper_decl(context, ast)[..], &(triggers, pos).to_viper(context, ast), body.to_viper(context, ast), pos.to_viper(context, ast), - ) - }, + ), Expr::Exists(ref vars, ref triggers, ref body, ref pos) => ast.exists_with_pos( &vars.to_viper_decl(context, ast)[..], &(triggers, pos).to_viper(context, ast), diff --git a/prusti-interface/src/environment/diagnostic.rs b/prusti-interface/src/environment/diagnostic.rs index f9aab4f3ca2..2dae0d01a75 100644 --- a/prusti-interface/src/environment/diagnostic.rs +++ b/prusti-interface/src/environment/diagnostic.rs @@ -79,11 +79,7 @@ impl<'tcx> EnvDiagnostic<'tcx> { } /// Emits a note - pub fn span_note + Clone>( - &self, - sp: S, - msg: &str, - ) { + pub fn span_note + Clone>(&self, sp: S, msg: &str) { self.tcx.sess.span_note_without_error(sp, msg); } diff --git a/prusti-interface/src/prusti_error.rs b/prusti-interface/src/prusti_error.rs index d84fbd7a475..4596a11e137 100644 --- a/prusti-interface/src/prusti_error.rs +++ b/prusti-interface/src/prusti_error.rs @@ -212,10 +212,7 @@ impl PrustiError { &self.help, &self.notes, ), - PrustiErrorKind::Message => env_diagnostic.span_note( - *self.span, - &self.message, - ), + PrustiErrorKind::Message => env_diagnostic.span_note(*self.span, &self.message), }; } diff --git a/prusti-server/src/client.rs b/prusti-server/src/client.rs index 58d8aa2baa8..33ec57aa105 100644 --- a/prusti-server/src/client.rs +++ b/prusti-server/src/client.rs @@ -4,12 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::{VerificationRequest, ServerMessage}; +use crate::{ServerMessage, VerificationRequest}; +use futures_util::{ + sink::SinkExt, + stream::{Stream, StreamExt}, +}; use prusti_common::config; +use tokio_tungstenite::{ + connect_async, + tungstenite::{error::Error, Message}, +}; use url::Url; -use tokio_tungstenite::{connect_async, - tungstenite::{Message, error::Error}}; -use futures_util::{stream::{Stream, StreamExt}, sink::SinkExt}; pub struct PrustiClient; @@ -29,13 +34,22 @@ impl PrustiClient { let use_json = config::json_communication(); let uri = server_url - .join(if use_json { "json/" } else { "bincode/" }) - .unwrap() - .join("verify/") - .unwrap(); + .join(if use_json { "json/" } else { "bincode/" }) + .unwrap() + .join("verify/") + .unwrap(); let (mut socket, _) = connect_async(uri).await.unwrap(); - let msg = if use_json { Message::text(serde_json::to_string(&request).expect("error encoding verification request in json")) } - else { Message::binary(bincode::serialize(&request).expect("error encoding verification request as binary")) }; + let msg = if use_json { + Message::text( + serde_json::to_string(&request) + .expect("error encoding verification request in json"), + ) + } else { + Message::binary( + bincode::serialize(&request) + .expect("error encoding verification request as binary"), + ) + }; socket.send(msg).await.unwrap(); let json_map = |ws_msg| { if let Message::Text(json) = ws_msg { @@ -58,7 +72,8 @@ impl PrustiClient { _ => Some(msg), } }; - let s = socket.filter_map(filter_close).map(if use_json { json_map } else { bin_map }); - s + socket + .filter_map(filter_close) + .map(if use_json { json_map } else { bin_map }) } } diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index f98789b0096..561800984c1 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -4,7 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::{VerificationRequest, ViperBackendConfig, ServerMessage}; +use crate::{ServerMessage, VerificationRequest, ViperBackendConfig}; +use futures::{lock, stream::Stream}; use log::info; use prusti_common::{ config, @@ -12,14 +13,17 @@ use prusti_common::{ vir::{program_normalization::NormalizationInfo, ToViper}, Stopwatch, }; -use std::{fs::create_dir_all, path::PathBuf, thread, sync::{mpsc, Arc, self}}; +use std::{ + fs::create_dir_all, + path::PathBuf, + sync::{self, mpsc, Arc}, + thread, time, +}; use viper::{ - smt_manager::SmtManager, PersistentCache, Cache, VerificationBackend, VerificationResult, VerificationResultType, Viper, VerificationContext, jni_utils::JniUtils + jni_utils::JniUtils, smt_manager::SmtManager, Cache, PersistentCache, VerificationBackend, + VerificationContext, VerificationResult, VerificationResultType, Viper, }; -use viper_sys::wrappers::viper::*; -use viper_sys::wrappers::java; -use std::time; -use futures::{stream::Stream, lock}; +use viper_sys::wrappers::{java, viper::*}; enum ServerRequest { Verification(VerificationRequest), @@ -31,6 +35,12 @@ pub struct VerificationRequestProcessing { mtx_tx_verreq: sync::Mutex>, } +impl Default for VerificationRequestProcessing { + fn default() -> Self { + Self::new() + } +} + /// A structure that lives for all the requests and has a single thread working on all the /// requests sequentially. /// On reception of a verification request, we send it through a channel to the already running @@ -42,35 +52,43 @@ impl VerificationRequestProcessing { let mtx_rx_servermsg = lock::Mutex::new(rx_servermsg); let mtx_tx_verreq = sync::Mutex::new(tx_verreq); - let ret = Self {mtx_rx_servermsg: mtx_rx_servermsg, - mtx_tx_verreq: mtx_tx_verreq, - }; - thread::spawn(|| { Self::verification_thread(rx_verreq, tx_servermsg) }); + let ret = Self { + mtx_rx_servermsg, + mtx_tx_verreq, + }; + thread::spawn(|| Self::verification_thread(rx_verreq, tx_servermsg)); ret } - fn verification_thread(rx_verreq: mpsc::Receiver, tx_servermsg: mpsc::Sender) { + fn verification_thread( + rx_verreq: mpsc::Receiver, + tx_servermsg: mpsc::Sender, + ) { let mut stopwatch = Stopwatch::start("verification_request_processing", "JVM startup"); - let viper = Arc::new(Viper::new_with_args(&config::viper_home(), config::extra_jvm_args())); + let viper = Arc::new(Viper::new_with_args( + &config::viper_home(), + config::extra_jvm_args(), + )); let mut cache = PersistentCache::load_cache(config::cache_path()); stopwatch.start_next("attach thread to JVM"); let verification_context = viper.attach_current_thread(); stopwatch.finish(); - loop { - match rx_verreq.recv() { - Ok(request) => { - match request { - ServerRequest::Verification(verification_request) => - process_verification_request(&viper, &mut cache, &verification_context, &tx_servermsg, verification_request), - ServerRequest::SaveCache => cache.save(), - } - } - Err(_) => break, + // loop { + while let Ok(request) = rx_verreq.recv() { + match request { + ServerRequest::Verification(verification_request) => process_verification_request( + &viper, + &mut cache, + &verification_context, + &tx_servermsg, + verification_request, + ), + ServerRequest::SaveCache => cache.save(), } } } - pub fn verify<'a>(&'a self, request: VerificationRequest) -> impl Stream + 'a { + pub fn verify(&self, request: VerificationRequest) -> impl Stream + '_ { self.mtx_tx_verreq .lock() .unwrap() @@ -81,11 +99,7 @@ impl VerificationRequestProcessing { if done { return None; } - let msg = self.mtx_rx_servermsg - .lock() - .await - .recv() - .unwrap(); + let msg = self.mtx_rx_servermsg.lock().await.recv().unwrap(); let mut done = false; if let ServerMessage::Termination(_) = msg { done = true; @@ -163,15 +177,19 @@ pub fn process_verification_request( let _ = build_or_dump_viper_program(); }); } - sender.send(ServerMessage::Termination(VerificationResult::dummy_success())).unwrap(); + sender + .send(ServerMessage::Termination( + VerificationResult::dummy_success(), + )) + .unwrap(); return; } - let mut result = VerificationResult{ - item_name : request.program.get_name().to_string(), + let mut result = VerificationResult { + item_name: request.program.get_name().to_string(), result_type: VerificationResultType::Success, cached: false, - time_ms: 0 + time_ms: 0, }; // Early return in case of cache hit @@ -202,18 +220,26 @@ pub fn process_verification_request( // Workaround for https://github.com/viperproject/prusti-dev/issues/744 let mut stopwatch = Stopwatch::start("prusti-server", "verifier startup"); let mut verifier = - new_viper_verifier(program_name, &verification_context, request.backend_config); + new_viper_verifier(program_name, verification_context, request.backend_config); stopwatch.start_next("verification"); result.result_type = if config::report_viper_messages() { - verify_and_poll_msgs(verification_context, verifier, viper_program, viper_arc, sender.clone()) + verify_and_poll_msgs( + verification_context, + verifier, + viper_program, + viper_arc, + sender.clone(), + ) } else { verifier.verify(viper_program) }; result.time_ms = stopwatch.finish().as_millis(); // Don't cache Java exceptions, which might be due to misconfigured paths. - if config::enable_cache() && !matches!(result.result_type, VerificationResultType::JavaException(_)) { + if config::enable_cache() + && !matches!(result.result_type, VerificationResultType::JavaException(_)) + { info!( "Storing new cached result {:?} for program {}", &result, @@ -227,12 +253,13 @@ pub fn process_verification_request( }) } -fn verify_and_poll_msgs(verification_context: &viper::VerificationContext, - mut verifier: viper::Verifier, - viper_program: viper::Program, - viper_arc: &Arc, - sender: mpsc::Sender) - -> VerificationResultType { +fn verify_and_poll_msgs( + verification_context: &viper::VerificationContext, + mut verifier: viper::Verifier, + viper_program: viper::Program, + viper_arc: &Arc, + sender: mpsc::Sender, +) -> VerificationResultType { let mut result_type = VerificationResultType::Success; // get the reporter global reference outside of the thread scope because it needs to @@ -241,7 +268,7 @@ fn verify_and_poll_msgs(verification_context: &viper::VerificationContext, let env = &verification_context.env(); let jni = JniUtils::new(env); let verifier_wrapper = silver::verifier::Verifier::with(env); - let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(verifier.verifier_instance().clone())); + let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(*verifier.verifier_instance())); let rep_glob_ref = env.new_global_ref(reporter).unwrap(); // start thread for polling messages @@ -253,21 +280,29 @@ fn verify_and_poll_msgs(verification_context: &viper::VerificationContext, result_type } -fn polling_function(viper_arc: &Arc, - rep_glob_ref: &jni::objects::GlobalRef, - sender: mpsc::Sender) { +fn polling_function( + viper_arc: &Arc, + rep_glob_ref: &jni::objects::GlobalRef, + sender: mpsc::Sender, +) { let verification_context = viper_arc.attach_current_thread(); let env = verification_context.env(); let jni = JniUtils::new(env); let reporter_instance = rep_glob_ref.as_obj(); let reporter_wrapper = silver::reporter::PollingReporter::with(env); loop { - while reporter_wrapper.call_hasNewMessage(reporter_instance).unwrap() { - let msg = reporter_wrapper.call_getNewMessage(reporter_instance).unwrap(); + while reporter_wrapper + .call_hasNewMessage(reporter_instance) + .unwrap() + { + let msg = reporter_wrapper + .call_getNewMessage(reporter_instance) + .unwrap(); match jni.class_name(msg).as_str() { "viper.silver.reporter.QuantifierInstantiationsMessage" => { let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); - let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); + let q_name = + jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); // also matches the "-aux" and "_precondition" quantifiers generated @@ -275,17 +310,22 @@ fn polling_function(viper_arc: &Arc, // prusti either way if q_name.starts_with("prog.l") { let no_pref = q_name.strip_prefix("prog.l").unwrap(); - let stripped = no_pref.strip_suffix("-aux").or( - no_pref.strip_suffix("_precondition")).unwrap_or(no_pref); + let stripped = no_pref + .strip_suffix("-aux") + .or(no_pref.strip_suffix("_precondition")) + .unwrap_or(no_pref); let parsed = stripped.parse::(); match parsed { Ok(pos_id) => { - sender.send(ServerMessage::QuantifierInstantiation{q_name: q_name, - insts: u64::try_from(q_inst).unwrap(), - pos_id: pos_id} - ).unwrap(); + sender + .send(ServerMessage::QuantifierInstantiation { + q_name, + insts: u64::try_from(q_inst).unwrap(), + pos_id, + }) + .unwrap(); } - _ => info!("Unexpected quantifier name {}", q_name) + _ => info!("Unexpected quantifier name {}", q_name), } } } @@ -295,23 +335,32 @@ fn polling_function(viper_arc: &Arc, let msg_wrapper = silver::reporter::QuantifierChosenTriggersMessage::with(env); let viper_quant = jni.unwrap_result(msg_wrapper.call_quantifier(msg)); - let viper_quant_str = jni.get_string(jni.unwrap_result(obj_wrapper.call_toString(viper_quant))); + let viper_quant_str = + jni.get_string(jni.unwrap_result(obj_wrapper.call_toString(viper_quant))); // we encoded the position id in the line and column number since this is not used by // prusti either way let pos = jni.unwrap_result(positioned_wrapper.call_pos(viper_quant)); - let pos_string = jni.get_string(jni.unwrap_result(obj_wrapper.call_toString(pos))); - let pos_id_index = pos_string.rfind(".").unwrap(); - let pos_id = pos_string[pos_id_index+1..].parse::().unwrap(); - - let viper_triggers = jni.get_string(jni.unwrap_result(msg_wrapper.call_triggers__string(msg))); - info!("QuantifierChosenTriggersMessage: {} {} {}", viper_quant_str, viper_triggers, pos_id); - sender.send(ServerMessage::QuantifierChosenTriggers{viper_quant: viper_quant_str, - triggers: viper_triggers, - pos_id: pos_id} - ).unwrap(); + let pos_string = + jni.get_string(jni.unwrap_result(obj_wrapper.call_toString(pos))); + let pos_id_index = pos_string.rfind('.').unwrap(); + let pos_id = pos_string[pos_id_index + 1..].parse::().unwrap(); + + let viper_triggers = + jni.get_string(jni.unwrap_result(msg_wrapper.call_triggers__string(msg))); + info!( + "QuantifierChosenTriggersMessage: {} {} {}", + viper_quant_str, viper_triggers, pos_id + ); + sender + .send(ServerMessage::QuantifierChosenTriggers { + viper_quant: viper_quant_str, + triggers: viper_triggers, + pos_id, + }) + .unwrap(); } "viper.silver.reporter.VerificationTerminationMessage" => return, - _ => () + _ => (), } } thread::sleep(time::Duration::from_millis(10)); diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index bd587f3d8c9..bc5fe7e1739 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -4,17 +4,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::{VerificationRequestProcessing}; +use crate::VerificationRequestProcessing; +use futures_util::{pin_mut, SinkExt, StreamExt}; use log::info; +use once_cell::sync::Lazy; use std::{ net::{Ipv4Addr, SocketAddr}, sync::mpsc, thread, }; use tokio::runtime::Builder; -use futures_util::{pin_mut, StreamExt, SinkExt}; use warp::Filter; -use once_cell::sync::Lazy; #[derive(Debug)] struct BincodeReject(bincode::Error); @@ -45,7 +45,8 @@ pub fn spawn_server_thread() -> SocketAddr { // see the file process_verification.rs). // It has to have a static lifetime because warp websockets need their closures to have a static // lifetime and we need to access this object in them. -static VERIFICATION_REQUEST_PROCESSING: Lazy = Lazy::new(|| { VerificationRequestProcessing::new() }); +static VERIFICATION_REQUEST_PROCESSING: Lazy = + Lazy::new(VerificationRequestProcessing::new); fn listen_on_port_with_address_callback(port: u16, address_callback: F) -> ! where @@ -57,11 +58,19 @@ where ws.on_upgrade(|websocket| async { let (mut ws_send, mut ws_recv) = websocket.split(); let req_msg = ws_recv.next().await.unwrap().unwrap(); - let verification_request = req_msg.to_str().and_then(|s: &str| serde_json::from_str(s).unwrap()).unwrap(); + let verification_request = req_msg + .to_str() + .and_then(|s: &str| serde_json::from_str(s).unwrap()) + .unwrap(); let stream = VERIFICATION_REQUEST_PROCESSING.verify(verification_request); pin_mut!(stream); while let Some(server_msg) = stream.next().await { - ws_send.send(warp::filters::ws::Message::text(serde_json::to_string(&server_msg).unwrap())).await.unwrap(); + ws_send + .send(warp::filters::ws::Message::text( + serde_json::to_string(&server_msg).unwrap(), + )) + .await + .unwrap(); } ws_send.close().await.unwrap(); }) @@ -77,7 +86,12 @@ where let stream = VERIFICATION_REQUEST_PROCESSING.verify(verification_request); pin_mut!(stream); while let Some(server_msg) = stream.next().await { - ws_send.send(warp::filters::ws::Message::binary(bincode::serialize(&server_msg).unwrap())).await.unwrap(); + ws_send + .send(warp::filters::ws::Message::binary( + bincode::serialize(&server_msg).unwrap(), + )) + .await + .unwrap(); } ws_send.close().await.unwrap(); }) diff --git a/prusti-server/src/server_message.rs b/prusti-server/src/server_message.rs index bcc973ec0bc..fcf542d9d0d 100644 --- a/prusti-server/src/server_message.rs +++ b/prusti-server/src/server_message.rs @@ -9,6 +9,14 @@ use viper::VerificationResult; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum ServerMessage { Termination(VerificationResult), - QuantifierInstantiation { q_name: String, insts: u64, pos_id: u64 }, - QuantifierChosenTriggers { viper_quant: String, triggers: String, pos_id: u64 }, + QuantifierInstantiation { + q_name: String, + insts: u64, + pos_id: u64, + }, + QuantifierChosenTriggers { + viper_quant: String, + triggers: String, + pos_id: u64, + }, } diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index efaf845bc9c..e9e286fa3d3 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -1045,7 +1045,7 @@ pub fn enable_type_invariants() -> bool { } /// When enabled, prusti should return various Data structures that are -/// used by prusti-assistant, such as a list of method calls, +/// used by prusti-assistant, such as a list of method calls, /// a list of all procedures to be verified, etc. pub fn show_ide_info() -> bool { read_setting("show_ide_info") diff --git a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs index bcbf94be1c1..4694cb3a318 100644 --- a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs +++ b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs @@ -290,7 +290,11 @@ pub(super) fn encode_quantifier<'tcx>( final_body, // we encode the position ID in the line number because that is used to name the // quantifiers in z3 - vir_crate::polymorphic::Position::new(i32::try_from(pos.id()).unwrap(), i32::try_from(pos.id()).unwrap(), pos.id()), + vir_crate::polymorphic::Position::new( + i32::try_from(pos.id()).unwrap(), + i32::try_from(pos.id()).unwrap(), + pos.id(), + ), )) } } diff --git a/prusti-viper/src/encoder/procedure_encoder.rs b/prusti-viper/src/encoder/procedure_encoder.rs index cf58f083812..b4d1f58a8f3 100644 --- a/prusti-viper/src/encoder/procedure_encoder.rs +++ b/prusti-viper/src/encoder/procedure_encoder.rs @@ -3590,7 +3590,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { tcx.def_path_str(called_def_id), call_site_span, result, - &tcx.sess.source_map(), + tcx.sess.source_map(), ); if let Some(contract_spans) = contract_spans_opt { self.encoder.spans_of_call_contracts.borrow_mut().push(contract_spans); diff --git a/prusti-viper/src/ide/ide_verification_result.rs b/prusti-viper/src/ide/ide_verification_result.rs index ea95948a27c..1c315156bf7 100644 --- a/prusti-viper/src/ide/ide_verification_result.rs +++ b/prusti-viper/src/ide/ide_verification_result.rs @@ -1,5 +1,5 @@ -use viper::VerificationResult; use serde::Serialize; +use viper::VerificationResult; // since the existing verification result // is not as trivially passed in json @@ -8,20 +8,16 @@ pub struct IdeVerificationResult { item_name: String, success: bool, time_ms: u128, - cached: bool + cached: bool, } impl IdeVerificationResult { pub fn from_res(res: &VerificationResult) -> Self { - match res { - _ => { - Self { - item_name: res.item_name.clone(), - success: res.is_success(), - time_ms: res.time_ms, - cached: res.cached, - } - } + Self { + item_name: res.item_name.clone(), + success: res.is_success(), + time_ms: res.time_ms, + cached: res.cached, } } } diff --git a/prusti-viper/src/ide/vsc_span.rs b/prusti-viper/src/ide/vsc_span.rs index 7b93b458396..dd6481e27b2 100644 --- a/prusti-viper/src/ide/vsc_span.rs +++ b/prusti-viper/src/ide/vsc_span.rs @@ -17,7 +17,7 @@ impl VscSpan { pub fn from_span(sp: &Span, sourcemap: &SourceMap) -> Option { let filename = sourcemap.span_to_filename(*sp); let diag_filename = sourcemap.filename_for_diagnostics(&filename); - let fname = format!("{}", diag_filename); + let fname = format!("{diag_filename}"); if let Ok((l1, l2)) = sourcemap.is_valid_span(*sp) { Some(Self { diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index e41f946421c..51dadc38505 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -4,30 +4,38 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use prusti_common::vir::{optimizations::optimize_program}; +use crate::{ + encoder::{ + counterexamples::{counterexample_translation, counterexample_translation_refactored}, + Encoder, + }, + ide::{self, ide_verification_result::IdeVerificationResult}, +}; use prusti_common::{ - config, report::log, Stopwatch, vir::program::Program, + config, + report::log, + vir::{optimizations::optimize_program, program::Program}, + Stopwatch, +}; +use prusti_interface::{ + data::{VerificationResult, VerificationTask}, + environment::Environment, + PrustiError, }; -use vir_crate::common::check_mode::CheckMode; -use crate::encoder::Encoder; -use crate::encoder::counterexamples::counterexample_translation; -use crate::encoder::counterexamples::counterexample_translation_refactored; -use crate::ide::{self, ide_verification_result::IdeVerificationResult}; -use prusti_interface::data::VerificationResult; -use prusti_interface::data::VerificationTask; -use prusti_interface::environment::Environment; -use prusti_interface::PrustiError; use viper; +use vir_crate::common::check_mode::CheckMode; +use ::log::{debug, error, info}; +use async_stream::stream; +use futures_util::{pin_mut, Stream, StreamExt}; use prusti_interface::specs::typed; -use ::log::{info, debug, error}; -use prusti_server::{VerificationRequest, PrustiClient, VerificationRequestProcessing, spawn_server_thread, ViperBackendConfig, ServerMessage}; use prusti_rustc_interface::span::DUMMY_SP; -use prusti_server::tokio::runtime::Builder; -use std::collections::HashMap; +use prusti_server::{ + spawn_server_thread, tokio::runtime::Builder, PrustiClient, ServerMessage, VerificationRequest, + VerificationRequestProcessing, ViperBackendConfig, +}; use serde_json::json; -use async_stream::stream; -use futures_util::{Stream, StreamExt, pin_mut}; +use std::collections::HashMap; /// A verifier is an object for verifying a single crate, potentially /// many times. @@ -40,10 +48,7 @@ where } impl<'v, 'tcx> Verifier<'v, 'tcx> { - pub fn new( - env: &'v Environment<'tcx>, - def_spec: typed::DefSpecificationMap, - ) -> Self { + pub fn new(env: &'v Environment<'tcx>, def_spec: typed::DefSpecificationMap) -> Self { Verifier { env, encoder: Encoder::new(env, def_spec), @@ -85,13 +90,15 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { let mut programs: Vec = if config::simplify_encoding() { stopwatch.start_next("optimizing Viper program"); let source_file_name = self.encoder.env().name.source_file_name(); - polymorphic_programs.into_iter().map( - |program| Program::Legacy(optimize_program(program, &source_file_name).into()) - ).collect() + polymorphic_programs + .into_iter() + .map(|program| Program::Legacy(optimize_program(program, &source_file_name).into())) + .collect() } else { - polymorphic_programs.into_iter().map( - |program| Program::Legacy(program.into()) - ).collect() + polymorphic_programs + .into_iter() + .map(|program| Program::Legacy(program.into())) + .collect() }; programs.extend(self.encoder.get_core_proof_programs()); @@ -99,8 +106,6 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { self.emit_contract_spans(); } - - stopwatch.start_next("verifying Viper program"); let requests = self.programs_to_requests(programs); @@ -131,14 +136,18 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { overall_result } - async fn handle_stream(&self, verification_messages: impl Stream) -> VerificationResult { + async fn handle_stream( + &self, + verification_messages: impl Stream, + ) -> VerificationResult { let mut overall_result = VerificationResult::Success; let encoding_errors_count = self.encoder.count_encoding_errors(); let error_manager = self.encoder.error_manager(); // we want quantifier_pos_ID + program_name + q_name as identifier because there are // different q_names for the same ID and each program reports independent results // key: (pos_id, program_name), key to result: q_name result: num_instantiations - let mut quantifier_instantiations: HashMap::<(u64, String), HashMap::> = HashMap::new(); + let mut quantifier_instantiations: HashMap<(u64, String), HashMap> = + HashMap::new(); // if we are not running in an ide, we want the errors to be reported sortedly let mut prusti_errors: Vec<_> = vec![]; @@ -150,10 +159,14 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ServerMessage::Termination(result) => { if config::show_ide_info() { PrustiError::message( - format!("ideVerificationResult{}", - serde_json::to_string(&IdeVerificationResult::from_res(&result)).unwrap() - ), DUMMY_SP.into() - ).emit(&self.env.diagnostic); + format!( + "ideVerificationResult{}", + serde_json::to_string(&IdeVerificationResult::from_res(&result)) + .unwrap() + ), + DUMMY_SP.into(), + ) + .emit(&self.env.diagnostic); } match result.result_type { // nothing to do @@ -161,28 +174,40 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { viper::VerificationResultType::ConsistencyErrors(errors) => { for error in errors { PrustiError::internal( - format!("consistency error in {program_name}: {error}"), DUMMY_SP.into() - ).emit(&self.env.diagnostic); + format!("consistency error in {program_name}: {error}"), + DUMMY_SP.into(), + ) + .emit(&self.env.diagnostic); } overall_result = VerificationResult::Failure; } viper::VerificationResultType::Failure(errors) => { for verification_error in errors { - debug!("Verification error in {}: {:?}", program_name.clone(), verification_error); - let mut prusti_error = error_manager.translate_verification_error(&verification_error); + debug!( + "Verification error in {}: {:?}", + program_name.clone(), + verification_error + ); + let mut prusti_error = + error_manager.translate_verification_error(&verification_error); // annotate with counterexample, if requested if config::counterexample() { - if config::unsafe_core_proof(){ - if let Some(silicon_counterexample) = &verification_error.counterexample { - if let Some(def_id) = error_manager.get_def_id(&verification_error) { + if config::unsafe_core_proof() { + if let Some(silicon_counterexample) = + &verification_error.counterexample + { + if let Some(def_id) = + error_manager.get_def_id(&verification_error) + { let counterexample = counterexample_translation_refactored::backtranslate( &self.encoder, error_manager.position_manager(), def_id, silicon_counterexample, ); - prusti_error = counterexample.annotate_error(prusti_error); + prusti_error = + counterexample.annotate_error(prusti_error); } else { prusti_error = prusti_error.add_note( format!( @@ -192,14 +217,20 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ); } } - } else if let Some(silicon_counterexample) = &verification_error.counterexample { - if let Some(def_id) = error_manager.get_def_id(&verification_error) { - let counterexample = counterexample_translation::backtranslate( - &self.encoder, - def_id, - silicon_counterexample, - ); - prusti_error = counterexample.annotate_error(prusti_error); + } else if let Some(silicon_counterexample) = + &verification_error.counterexample + { + if let Some(def_id) = + error_manager.get_def_id(&verification_error) + { + let counterexample = + counterexample_translation::backtranslate( + &self.encoder, + def_id, + silicon_counterexample, + ); + prusti_error = + counterexample.annotate_error(prusti_error); } else { prusti_error = prusti_error.add_note( format!( @@ -213,12 +244,10 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { debug!("Prusti error: {:?}", prusti_error); if prusti_error.is_disabled() { prusti_error.cancel(); + } else if config::show_ide_info() { + prusti_error.emit(&self.env.diagnostic); } else { - if config::show_ide_info() { - prusti_error.emit(&self.env.diagnostic); - } else { - prusti_errors.push(prusti_error); - } + prusti_errors.push(prusti_error); } } overall_result = VerificationResult::Failure; @@ -226,13 +255,19 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { viper::VerificationResultType::JavaException(exception) => { error!("Java exception: {}", exception.get_stack_trace()); PrustiError::internal( - format!("in {program_name}: {exception}"), DUMMY_SP.into() - ).emit(&self.env.diagnostic); + format!("in {program_name}: {exception}"), + DUMMY_SP.into(), + ) + .emit(&self.env.diagnostic); overall_result = VerificationResult::Failure; } } } - ServerMessage::QuantifierInstantiation{q_name, insts, pos_id} => { + ServerMessage::QuantifierInstantiation { + q_name, + insts, + pos_id, + } => { if config::report_viper_messages() { match error_manager.position_manager().get_span_from_id(pos_id) { Some(span) => { @@ -244,7 +279,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { // this replaces the old entry which is exactly what we want map.insert(q_name, insts); let mut n: u64 = 0; - for (_, insts) in map { + for insts in map.values() { n += *insts; } PrustiError::message( @@ -257,19 +292,21 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } } } - ServerMessage::QuantifierChosenTriggers{viper_quant, triggers, pos_id} => { - if config::report_viper_messages() { - if pos_id != 0 { - match error_manager.position_manager().get_span_from_id(pos_id) { - Some(span) => { - PrustiError::message( - format!("quantifierChosenTriggersMessage{}", - json!({"viper_quant": viper_quant, "triggers": triggers}), - ), span.clone() - ).emit(&self.env.diagnostic); - }, - None => error!("Invalid position id {pos_id} for viper quantifier {viper_quant} in verification of {program_name}"), - } + ServerMessage::QuantifierChosenTriggers { + viper_quant, + triggers, + pos_id, + } => { + if config::report_viper_messages() && pos_id != 0 { + match error_manager.position_manager().get_span_from_id(pos_id) { + Some(span) => { + PrustiError::message( + format!("quantifierChosenTriggersMessage{}", + json!({"viper_quant": viper_quant, "triggers": triggers}), + ), span.clone() + ).emit(&self.env.diagnostic); + }, + None => error!("Invalid position id {pos_id} for viper quantifier {viper_quant} in verification of {program_name}"), } } } @@ -310,7 +347,9 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { config::verify_specifications_backend() } else { config::viper_backend() - }.parse().unwrap(); + } + .parse() + .unwrap(); let request = VerificationRequest { program, backend_config: ViperBackendConfig::new(backend), @@ -321,9 +360,10 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } /// Returns a list of (program_name, verification_requests) tuples. - fn verify_requests_server(verification_requests: Vec<(String, VerificationRequest)>, server_address: String) - -> impl Stream - { + fn verify_requests_server( + verification_requests: Vec<(String, VerificationRequest)>, + server_address: String, + ) -> impl Stream { let server_address = if server_address == "MOCK" { spawn_server_thread().to_string() } else { @@ -338,15 +378,16 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { verification_stream.flatten() } - fn verify_requests_local<'a>(verification_requests: Vec<(String, VerificationRequest)>, vrp: &'a VerificationRequestProcessing) - -> impl Stream + 'a - { + fn verify_requests_local<'a>( + verification_requests: Vec<(String, VerificationRequest)>, + vrp: &'a VerificationRequestProcessing, + ) -> impl Stream + 'a { let verification_stream = stream! { for (program_name, request) in verification_requests { yield vrp.verify(request).map(move |msg| (program_name.clone(), msg)); } }; - return verification_stream.flatten(); + verification_stream.flatten() } pub fn emit_contract_spans(&self) { @@ -354,9 +395,9 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { call_contract_spans: self.encoder.spans_of_call_contracts.borrow().to_vec(), }; PrustiError::message( - format!("encodingInfo{}", encoding_info.to_json_string()), DUMMY_SP.into() - ).emit(&self.env.diagnostic); - + format!("encodingInfo{}", encoding_info.to_json_string()), + DUMMY_SP.into(), + ) + .emit(&self.env.diagnostic); } } - diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 68646bb1901..9cfd2b37795 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -1,5 +1,5 @@ use crate::{ - ide_helper::{fake_error::fake_error, compiler_info}, + ide_helper::{compiler_info, fake_error::fake_error}, verifier::verify, }; use prusti_common::config; @@ -138,7 +138,8 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { let (annotated_procedures, types) = env.get_annotated_procedures_and_types(); if config::show_ide_info() && !config::no_verify() { - let compiler_info = compiler_info::IdeInfo::collect(&env, &annotated_procedures, &def_spec); + let compiler_info = + compiler_info::IdeInfo::collect(&env, &annotated_procedures, &def_spec); let out = serde_json::to_string(&compiler_info).unwrap(); PrustiError::message(format!("compilerInfo{out}"), DUMMY_SP.into()) .emit(&env.diagnostic); diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 07d3d4ca299..68bdd45e476 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -36,9 +36,9 @@ impl<'tcx> CallSpanFinder<'tcx> { let (resolved_def_id, _subst) = self.env_query .resolve_method_call(owner_def_id, method_def_id, substs); - return Ok((method_def_id, resolved_def_id)); + Ok((method_def_id, resolved_def_id)) } else { - return Err(()); + Err(()) } } } @@ -58,7 +58,7 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { let tyck_res = self.tcx.typeck(e1.hir_id.owner.def_id); let res = tyck_res.qpath_res(qself, e1.hir_id); if let prusti_rustc_interface::hir::def::Res::Def(_, def_id) = res { - if !def_id.as_local().is_some() { + if def_id.as_local().is_none() { let defpath = self.tcx.def_path_str(def_id); self.called_functions.push((defpath, def_id, expr.span)); } @@ -67,47 +67,54 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { } ExprKind::MethodCall(_path, _e1, _e2, sp) => { let resolve_res = self.resolve_expression(expr); - match resolve_res { - Ok((method_def_id, resolved_def_id)) => { - let is_local = method_def_id.as_local().is_some(); - let defpath_unresolved = self.tcx.def_path_str(method_def_id); - let defpath_resolved = self.tcx.def_path_str(resolved_def_id); + if let Ok((method_def_id, resolved_def_id)) = resolve_res { + let defpath_unresolved = self.tcx.def_path_str(method_def_id); + let defpath_resolved = self.tcx.def_path_str(resolved_def_id); - if !is_local { - if defpath_unresolved == defpath_resolved { - self.called_functions.push((defpath_resolved, resolved_def_id, sp)); - } else { - // in this case we want both - self.called_functions.push((defpath_resolved, resolved_def_id, sp)); - self.called_functions.push((defpath_unresolved, method_def_id, sp)); - } + if method_def_id.as_local().is_none() { + if defpath_unresolved == defpath_resolved { + self.called_functions + .push((defpath_resolved, resolved_def_id, sp)); + } else { + // in this case we want both + self.called_functions + .push((defpath_resolved, resolved_def_id, sp)); + self.called_functions + .push((defpath_unresolved, method_def_id, sp)); } } - Err(()) => {} } } ExprKind::Binary(..) | ExprKind::AssignOp(..) | ExprKind::Unary(..) => { let resolve_res = self.resolve_expression(expr); // this will already fail for standard addition - match resolve_res { - Ok((method_def_id, resolved_def_id)) => { - let is_local = method_def_id.as_local().is_some(); - let defpath_unresolved = self.tcx.def_path_str(method_def_id); - let defpath_resolved = self.tcx.def_path_str(resolved_def_id); + if let Ok((method_def_id, resolved_def_id)) = resolve_res { + let defpath_unresolved = self.tcx.def_path_str(method_def_id); + let defpath_resolved = self.tcx.def_path_str(resolved_def_id); - if !is_local { - if defpath_unresolved == defpath_resolved { - self.called_functions.push((defpath_resolved, resolved_def_id, expr.span)); - } else { - // For binary operations this will be the operation - // from the standard libary and the "overriding" method + if method_def_id.as_local().is_none() { + if defpath_unresolved == defpath_resolved { + self.called_functions.push(( + defpath_resolved, + resolved_def_id, + expr.span, + )); + } else { + // For binary operations this will be the operation + // from the standard libary and the "overriding" method - self.called_functions.push((defpath_resolved, resolved_def_id,expr.span)); - self.called_functions.push((defpath_unresolved, method_def_id, expr.span)); - } + self.called_functions.push(( + defpath_resolved, + resolved_def_id, + expr.span, + )); + self.called_functions.push(( + defpath_unresolved, + method_def_id, + expr.span, + )); } } - Err(()) => {} // standard addition etc should be caught here } } _ => {} diff --git a/prusti/src/ide_helper/compiler_info.rs b/prusti/src/ide_helper/compiler_info.rs index 985f66e30d0..b22e6089e40 100644 --- a/prusti/src/ide_helper/compiler_info.rs +++ b/prusti/src/ide_helper/compiler_info.rs @@ -21,7 +21,11 @@ pub struct IdeInfo { } impl IdeInfo { - pub fn collect(env: &Environment<'_>, procedures: &Vec, def_spec: &typed::DefSpecificationMap) -> Self { + pub fn collect( + env: &Environment<'_>, + procedures: &Vec, + def_spec: &typed::DefSpecificationMap, + ) -> Self { let procs = collect_procedures(env, procedures, def_spec); let source_map = env.tcx().sess.source_map(); let fncalls: Vec = collect_fncalls(env) @@ -62,16 +66,19 @@ impl Serialize for ProcDef { } } - // collect information about the program that will be passed to IDE: -fn collect_procedures(env: &Environment<'_>, procedures: &Vec, def_spec: &typed::DefSpecificationMap) -> Vec { +fn collect_procedures( + env: &Environment<'_>, + procedures: &Vec, + def_spec: &typed::DefSpecificationMap, +) -> Vec { let sourcemap: &SourceMap = env.tcx().sess.source_map(); let mut procs = Vec::new(); for defid in procedures { let defpath = env.name.get_unique_item_name(*defid); let span = env.query.get_def_span(defid); let vscspan = VscSpan::from_span(&span, sourcemap).unwrap(); - + // Filter out the predicates and trusted methods, // since we don't want to allow selective verification // for them @@ -80,11 +87,17 @@ fn collect_procedures(env: &Environment<'_>, procedures: &Vec, def_spec: let proc_spec_opt = def_spec.get_proc_spec(defid); if let Some(proc_spec) = proc_spec_opt { - let kind_spec = proc_spec.base_spec.kind.extract_with_selective_replacement(); - let trusted_spec = proc_spec.base_spec.trusted.extract_with_selective_replacement(); + let kind_spec = proc_spec + .base_spec + .kind + .extract_with_selective_replacement(); + let trusted_spec = proc_spec + .base_spec + .trusted + .extract_with_selective_replacement(); if let Some(typed::ProcedureSpecificationKind::Predicate(..)) = kind_spec { is_predicate = true; - } + } if let Some(true) = trusted_spec { is_trusted = true; } @@ -113,6 +126,5 @@ fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, DefId, Span)> { .hir() .visit_all_item_likes_in_crate(&mut fnvisitor); - return fnvisitor.called_functions; + fnvisitor.called_functions } - diff --git a/prusti/src/ide_helper/fake_error.rs b/prusti/src/ide_helper/fake_error.rs index 9a85e0bef7c..91157ba9c89 100644 --- a/prusti/src/ide_helper/fake_error.rs +++ b/prusti/src/ide_helper/fake_error.rs @@ -1,15 +1,12 @@ use prusti_interface::environment::Environment; -use prusti_rustc_interface::{ - span::DUMMY_SP, - errors::MultiSpan, -}; +use prusti_rustc_interface::{errors::MultiSpan, span::DUMMY_SP}; - -pub fn fake_error<'tcx>(env: Environment<'tcx>) { +pub fn fake_error(env: Environment) { let sp = MultiSpan::from_span(DUMMY_SP); let message = String::from("[Prusti: FakeError]"); let help = None; let notes = []; - - env.diagnostic.span_err_with_help_and_notes(sp, &message, &help, ¬es); + + env.diagnostic + .span_err_with_help_and_notes(sp, &message, &help, ¬es); } diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 8b769da25ba..bec314215b6 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -107,16 +107,16 @@ impl fmt::Display for ExternSpecBlock { generics, function_sig, } => { - let generics_str = generic_args_str(&generics, false); - let where_block = bounds_where_block(&generics); + let generics_str = generic_args_str(generics, false); + let where_block = bounds_where_block(generics); - write!(f, "#[extern_spec]\nimpl{} ", generics_str)?; + write!(f, "#[extern_spec]\nimpl{generics_str} ")?; if let Some(trait_name) = trait_name { - write!(f, "{} for ", trait_name)?; + write!(f, "{trait_name} for ")?; } - write!(f, "{} {}{{\n", name, where_block)?; + writeln!(f, "{name} {where_block}{{")?; let fn_sig = indent(&function_sig.to_string()); - write!(f, "{}\n}}", fn_sig) + write!(f, "{fn_sig}\n}}") } ExternSpecBlock::Trait { name, @@ -125,19 +125,19 @@ impl fmt::Display for ExternSpecBlock { function_sig, } => { let fn_sig = indent(&function_sig.to_string()); - let generics_str = generic_args_str(&generics, false); - let where_block = bounds_where_block(&generics); + let generics_str = generic_args_str(generics, false); + let where_block = bounds_where_block(generics); // do traits specify traitbounds too? - write!(f, "#[extern_spec({})]\n", path)?; - write!(f, "trait {}{} {}{{\n", name, generics_str, where_block)?; - write!(f, "{}\n}}", fn_sig) + writeln!(f, "#[extern_spec({path})]")?; + writeln!(f, "trait {name}{generics_str} {where_block}{{")?; + writeln!(f, "{fn_sig}}}") } ExternSpecBlock::StandaloneFn { parent_chain, function_sig, } => { - write!(f, "#[extern_spec({})]\n", parent_chain)?; - write!(f, "{}", function_sig) + writeln!(f, "#[extern_spec({parent_chain})]")?; + write!(f, "{function_sig}") } } } @@ -156,7 +156,7 @@ impl fmt::Display for GenericArg { // otherwise just take .name field.. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let bounds_str = traitbounds_string(&self.bounds); - write!(f, "{}: {}", self.name, bounds_str) + write!(f, "{}: {bounds_str}", self.name) // for now we ignore defaults since prusti doesnt accept them in // some cases.. // if self.default_value.is_some() { @@ -166,13 +166,13 @@ impl fmt::Display for GenericArg { } // the string for the where clause -fn bounds_where_block(arglist: &Vec) -> String { +fn bounds_where_block(arglist: &[GenericArg]) -> String { let bounds_vec = arglist .iter() - .filter(|arg| arg.bounds.len() > 0) + .filter(|arg| !arg.bounds.is_empty()) .map(|arg| format!("\t{}: {}", arg.name, traitbounds_string(&arg.bounds))) .collect::>(); - if bounds_vec.len() > 0 { + if !bounds_vec.is_empty() { format!("where\n{}\n", bounds_vec.join(",\n")) } else { "".to_string() @@ -180,7 +180,7 @@ fn bounds_where_block(arglist: &Vec) -> String { } fn generic_args_str(arglist: &Vec, include_bounds: bool) -> String { - if arglist.len() > 0 { + if !arglist.is_empty() { let res = arglist .iter() .map(|genarg| { @@ -193,7 +193,7 @@ fn generic_args_str(arglist: &Vec, include_bounds: bool) -> String { // probably happen here .collect::>() .join(", "); - format!("<{}>", res) + format!("<{res}>") } else { "".to_string() } @@ -201,7 +201,7 @@ fn generic_args_str(arglist: &Vec, include_bounds: bool) -> String { // example result: Sized + PartialEq + Eq fn traitbounds_string(boundlist: &Vec) -> String { - if boundlist.len() > 0 { + if !boundlist.is_empty() { let res = boundlist .iter() .map(|bound| bound.to_string()) @@ -221,16 +221,15 @@ struct TraitBound { impl fmt::Display for TraitBound { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name)?; // todo - if self.args.len() >= 1 { + write!(f, "{}", self.name)?; + if !self.args.is_empty() { let args_str = self.args.join(", "); - write!(f, "<{}>", args_str)?; + write!(f, "<{args_str}>")?; } Ok(()) } } - #[derive(Debug)] struct FunctionSignature { name: String, @@ -246,7 +245,11 @@ impl FunctionSignature { let arg_types = sig.inputs().iter(); let arg_names = tcx.fn_arg_names(defid); let output = sig.output().skip_binder(); - let return_type = if output.is_unit() { None } else { Some(output.to_string()) }; + let return_type = if output.is_unit() { + None + } else { + Some(output.to_string()) + }; let arguments: Vec<(String, String)> = arg_names .iter() @@ -264,12 +267,13 @@ impl FunctionSignature { } fn arguments_string(&self) -> String { - let args = self.arguments + let args = self + .arguments .iter() - .map(|(name, ty)| format!("{}: {}", name.to_string(), ty)) + .map(|(name, ty)| format!("{name}: {ty}")) .collect::>() .join(", "); - format!("({})", args) + format!("({args})") } } @@ -283,11 +287,10 @@ impl fmt::Display for FunctionSignature { write!(f, "fn {}{}{}", self.name, generics_str, args_str)?; if let Some(ret_ty) = self.return_type.clone() { - write!(f, " -> {}", ret_ty)?; + write!(f, " -> {ret_ty}")?; } write!(f, ";") } - } fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { @@ -298,7 +301,9 @@ fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { // first we just collect all generic parameters for param in generics.params.iter() { let ident = param.name.to_string(); - if ident == "Self" { continue } + if ident == "Self" { + continue; + } let mut generic_arg = GenericArg { name: ident.clone(), default_value: None, @@ -323,13 +328,15 @@ fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { let bound_traitref = t.trait_ref; let trait_name = tcx.def_path_str(bound_traitref.def_id); let self_ty = format!("{}", bound_traitref.self_ty()); - if self_ty == "Self" { continue } + if self_ty == "Self" { + continue; + } let trait_args_opt = bound_traitref.substs.try_as_type_list(); let trait_args = if let Some(typelist) = trait_args_opt { typelist .iter() .skip(1) // the first one is the self type - .map(|ty| format!("{}", ty)) + .map(|ty| format!("{ty}")) .collect::>() } else { vec![] @@ -353,11 +360,9 @@ fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { let trait_defpath = tcx.def_path_str(trait_defid); let item_name = tcx.item_name(item_id); - + let projection_term = format!("{}={}", item_name, p.term); - let genarg = generic_params - .get_mut(&self_ty) - .unwrap(); + let genarg = generic_params.get_mut(&self_ty).unwrap(); // not sure if this is actually completely correct. // can a single generic type have the same traitbound // more than once with different arguments? @@ -367,7 +372,7 @@ fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { _ => {} } } - + // handling projections here is simpler than when printing: // could not do this directly since projections for el in generic_params.values_mut() { @@ -381,7 +386,7 @@ fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { generic_params.values().cloned().collect() } -pub fn collect_queried_signature(tcx: TyCtxt<'_>, fncalls: &Vec) -> Option { +pub fn collect_queried_signature(tcx: TyCtxt<'_>, fncalls: &[ProcDef]) -> Option { let def_path_str: String = config::query_method_signature()?; let existing = fncalls .iter() @@ -390,9 +395,9 @@ pub fn collect_queried_signature(tcx: TyCtxt<'_>, fncalls: &Vec) -> Opt // helpers: let defid: DefId = existing.defid; let extern_spec_block = ExternSpecBlock::from_defid(tcx, defid); - return extern_spec_block.map(|x| x.to_string()); + extern_spec_block.map(|x| x.to_string()) } - + fn get_parent_chain(defid: DefId, tcx: TyCtxt<'_>) -> String { // let mut parent_opt = tcx.opt_parent(defid); // this (above) apparently doesn't work. E.g. for std::ops::Add diff --git a/prusti/src/verifier.rs b/prusti/src/verifier.rs index b94c2a87614..72adf5bc0fd 100644 --- a/prusti/src/verifier.rs +++ b/prusti/src/verifier.rs @@ -16,7 +16,6 @@ pub fn verify<'tcx>( ) { trace!("[verify] enter"); - if env.diagnostic.has_errors() { warn!("The compiler reported an error, so the program will not be verified."); } else { @@ -44,7 +43,6 @@ pub fn verify<'tcx>( } } - let verification_result = if verification_task.procedures.is_empty() && verification_task.types.is_empty() { VerificationResult::Success diff --git a/viper/src/verification_result.rs b/viper/src/verification_result.rs index de222838b26..d3b1188f5be 100644 --- a/viper/src/verification_result.rs +++ b/viper/src/verification_result.rs @@ -29,7 +29,7 @@ impl VerificationResult { } /// The result of a verification request on a Viper program. #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub enum VerificationResultType{ +pub enum VerificationResultType { /// The program verified. Success, /// The program did not verify. @@ -40,7 +40,6 @@ pub enum VerificationResultType{ JavaException(JavaException), } - impl VerificationResultType { pub fn is_success(&self) -> bool { matches!(self, Self::Success) diff --git a/viper/src/verifier.rs b/viper/src/verifier.rs index 1864b9f2469..08f9c0ea5ec 100644 --- a/viper/src/verifier.rs +++ b/viper/src/verifier.rs @@ -47,9 +47,10 @@ impl<'a> Verifier<'a> { jni.unwrap_result(silver::reporter::NoopReporter_object::with(env).singleton()) }; - let reporter = jni.unwrap_result(silver::reporter::PollingReporter::with(env).new( - jni.new_string("polling_reporter"), - pass_through_reporter)); + let reporter = jni.unwrap_result( + silver::reporter::PollingReporter::with(env) + .new(jni.new_string("polling_reporter"), pass_through_reporter), + ); let debug_info = jni.new_seq(&[]); match backend { diff --git a/vir/defs/polymorphic/ast/expr.rs b/vir/defs/polymorphic/ast/expr.rs index c0e7214690b..0981c0a8199 100644 --- a/vir/defs/polymorphic/ast/expr.rs +++ b/vir/defs/polymorphic/ast/expr.rs @@ -352,7 +352,12 @@ impl Expr { }) } - pub fn forall_with_pos(vars: Vec, triggers: Vec, body: Expr, pos: Position) -> Self { + pub fn forall_with_pos( + vars: Vec, + triggers: Vec, + body: Expr, + pos: Position, + ) -> Self { assert!( !vars.is_empty(), "A quantifier must have at least one variable." @@ -365,7 +370,6 @@ impl Expr { }) } - pub fn exists(vars: Vec, triggers: Vec, body: Expr) -> Self { assert!( !vars.is_empty(), From ebfdd1bb766264c5b0a7ae938b568a63e369262b Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Wed, 22 Feb 2023 23:07:08 +0100 Subject: [PATCH 47/74] Fix last 2 clippy warnings --- prusti-viper/src/verifier.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 51dadc38505..473e9e44afb 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -35,7 +35,7 @@ use prusti_server::{ VerificationRequestProcessing, ViperBackendConfig, }; use serde_json::json; -use std::collections::HashMap; +use rustc_hash::FxHashMap; /// A verifier is an object for verifying a single crate, potentially /// many times. @@ -142,12 +142,11 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { ) -> VerificationResult { let mut overall_result = VerificationResult::Success; let encoding_errors_count = self.encoder.count_encoding_errors(); - let error_manager = self.encoder.error_manager(); // we want quantifier_pos_ID + program_name + q_name as identifier because there are // different q_names for the same ID and each program reports independent results // key: (pos_id, program_name), key to result: q_name result: num_instantiations - let mut quantifier_instantiations: HashMap<(u64, String), HashMap> = - HashMap::new(); + let mut quantifier_instantiations: FxHashMap<(u64, String), FxHashMap> = + FxHashMap::default(); // if we are not running in an ide, we want the errors to be reported sortedly let mut prusti_errors: Vec<_> = vec![]; @@ -188,8 +187,8 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { program_name.clone(), verification_error ); - let mut prusti_error = - error_manager.translate_verification_error(&verification_error); + let mut prusti_error = self.encoder.error_manager() + .translate_verification_error(&verification_error); // annotate with counterexample, if requested if config::counterexample() { @@ -197,12 +196,12 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { if let Some(silicon_counterexample) = &verification_error.counterexample { - if let Some(def_id) = - error_manager.get_def_id(&verification_error) + if let Some(def_id) = self.encoder.error_manager() + .get_def_id(&verification_error) { let counterexample = counterexample_translation_refactored::backtranslate( &self.encoder, - error_manager.position_manager(), + self.encoder.error_manager().position_manager(), def_id, silicon_counterexample, ); @@ -220,8 +219,8 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } else if let Some(silicon_counterexample) = &verification_error.counterexample { - if let Some(def_id) = - error_manager.get_def_id(&verification_error) + if let Some(def_id) = self.encoder.error_manager() + .get_def_id(&verification_error) { let counterexample = counterexample_translation::backtranslate( @@ -269,11 +268,11 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { pos_id, } => { if config::report_viper_messages() { - match error_manager.position_manager().get_span_from_id(pos_id) { + match self.encoder.error_manager().position_manager().get_span_from_id(pos_id) { Some(span) => { let key = (pos_id, program_name.clone()); if !quantifier_instantiations.contains_key(&key) { - quantifier_instantiations.insert(key.clone(), HashMap::new()); + quantifier_instantiations.insert(key.clone(), FxHashMap::default()); } let map = quantifier_instantiations.get_mut(&key).unwrap(); // this replaces the old entry which is exactly what we want @@ -298,7 +297,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { pos_id, } => { if config::report_viper_messages() && pos_id != 0 { - match error_manager.position_manager().get_span_from_id(pos_id) { + match self.encoder.error_manager().position_manager().get_span_from_id(pos_id) { Some(span) => { PrustiError::message( format!("quantifierChosenTriggersMessage{}", From ca1d9d610aa380e526720ff616a99fdfa40a4bfc Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 23 Feb 2023 00:01:13 +0100 Subject: [PATCH 48/74] Updated viper-toolchain, added various comments / documentation, addressed a few reviews --- .../src/vir/program_normalization.rs | 6 ++-- prusti-server/src/process_verification.rs | 10 +++---- prusti-viper/src/encoder/encoder.rs | 2 ++ prusti-viper/src/encoder/procedure_encoder.rs | 5 ++++ prusti-viper/src/ide/encoding_info.rs | 3 +- prusti-viper/src/ide/vsc_span.rs | 1 + prusti-viper/src/verifier.rs | 8 +++--- prusti/src/callbacks.rs | 6 +--- prusti/src/ide_helper/call_finder.rs | 1 + prusti/src/ide_helper/compiler_info.rs | 20 ++++++------- prusti/src/ide_helper/fake_error.rs | 4 +++ prusti/src/ide_helper/query_signature.rs | 28 ++++++++++++------- viper-toolchain | 2 +- viper/src/verification_result.rs | 10 +++---- viper/src/verifier.rs | 12 ++++---- 15 files changed, 66 insertions(+), 52 deletions(-) diff --git a/prusti-common/src/vir/program_normalization.rs b/prusti-common/src/vir/program_normalization.rs index bea3001d397..7ef3760842e 100644 --- a/prusti-common/src/vir/program_normalization.rs +++ b/prusti-common/src/vir/program_normalization.rs @@ -7,7 +7,7 @@ use crate::vir::{program::Program, Position}; use log::{debug, trace}; use rustc_hash::{FxHashMap, FxHashSet}; -use viper::VerificationResultType; +use viper::VerificationResultKind; #[derive(Clone)] pub enum NormalizationInfo { @@ -102,8 +102,8 @@ impl NormalizationInfo { } /// Denormalize a verification result. - pub fn denormalize_result(&self, result: &mut VerificationResultType) { - if let VerificationResultType::Failure(ref mut ver_errors) = result { + pub fn denormalize_result(&self, result: &mut VerificationResultKind) { + if let VerificationResultKind::Failure(ref mut ver_errors) = result { ver_errors.iter_mut().for_each(|ver_error| { if let Some(pos) = ver_error.pos_id.as_mut() { self.denormalize_position_string(pos); diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index 561800984c1..c8b0a00a8f1 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -21,7 +21,7 @@ use std::{ }; use viper::{ jni_utils::JniUtils, smt_manager::SmtManager, Cache, PersistentCache, VerificationBackend, - VerificationContext, VerificationResult, VerificationResultType, Viper, + VerificationContext, VerificationResult, VerificationResultKind, Viper, }; use viper_sys::wrappers::{java, viper::*}; @@ -187,7 +187,7 @@ pub fn process_verification_request( let mut result = VerificationResult { item_name: request.program.get_name().to_string(), - result_type: VerificationResultType::Success, + result_type: VerificationResultKind::Success, cached: false, time_ms: 0, }; @@ -238,7 +238,7 @@ pub fn process_verification_request( // Don't cache Java exceptions, which might be due to misconfigured paths. if config::enable_cache() - && !matches!(result.result_type, VerificationResultType::JavaException(_)) + && !matches!(result.result_type, VerificationResultKind::JavaException(_)) { info!( "Storing new cached result {:?} for program {}", @@ -259,8 +259,8 @@ fn verify_and_poll_msgs( viper_program: viper::Program, viper_arc: &Arc, sender: mpsc::Sender, -) -> VerificationResultType { - let mut result_type = VerificationResultType::Success; +) -> VerificationResultKind { + let mut result_type = VerificationResultKind::Success; // get the reporter global reference outside of the thread scope because it needs to // be dropped by thread attached to the jvm. This is also why we pass it as reference diff --git a/prusti-viper/src/encoder/encoder.rs b/prusti-viper/src/encoder/encoder.rs index 24487801e03..0721fc4c6ea 100644 --- a/prusti-viper/src/encoder/encoder.rs +++ b/prusti-viper/src/encoder/encoder.rs @@ -104,6 +104,8 @@ pub struct Encoder<'v, 'tcx: 'v> { /// this requires special care when encoding array/slice accesses which may come with /// bound checks included in the MIR. pub(super) is_encoding_trigger: Cell, + /// When calls are being encoded, this Vec saves the spans of all items of the + /// contract so they can later be passed to prusti-assistant pub spans_of_call_contracts: RefCell>, } diff --git a/prusti-viper/src/encoder/procedure_encoder.rs b/prusti-viper/src/encoder/procedure_encoder.rs index b4d1f58a8f3..aeb7defa589 100644 --- a/prusti-viper/src/encoder/procedure_encoder.rs +++ b/prusti-viper/src/encoder/procedure_encoder.rs @@ -3388,6 +3388,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { substs, ).with_span(call_site_span)? }; + // store spans of items of the contracts for prusti-assistant self.store_contract_spans( called_def_id, &procedure_contract, @@ -3535,6 +3536,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { Ok(stmts) } + /// Collect all the available spans of the items of a contract + /// and store them. Purpose of this function is to later pass these + /// spans to prusti-assistant, so users can look up contracts of + /// function calls fn store_contract_spans( &self, called_def_id: ProcedureDefId, diff --git a/prusti-viper/src/ide/encoding_info.rs b/prusti-viper/src/ide/encoding_info.rs index 2c6641540b0..386a0ad94c6 100644 --- a/prusti-viper/src/ide/encoding_info.rs +++ b/prusti-viper/src/ide/encoding_info.rs @@ -1,10 +1,9 @@ use prusti_rustc_interface::span::{source_map::SourceMap, Span}; use serde::Serialize; - use super::vsc_span::VscSpan; + /// stores the spans of a calls contracts. /// obtained during encoding - #[derive(Serialize, Clone)] pub struct SpanOfCallContracts { pub defpath: String, diff --git a/prusti-viper/src/ide/vsc_span.rs b/prusti-viper/src/ide/vsc_span.rs index dd6481e27b2..d1c63fe8c38 100644 --- a/prusti-viper/src/ide/vsc_span.rs +++ b/prusti-viper/src/ide/vsc_span.rs @@ -1,5 +1,6 @@ use prusti_rustc_interface::span::{source_map::SourceMap, Span}; use serde::Serialize; + /// a representation of spans that is more usable with VSCode. #[derive(Serialize, Clone)] pub struct VscSpan { diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 473e9e44afb..bcf695c8043 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -169,8 +169,8 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } match result.result_type { // nothing to do - viper::VerificationResultType::Success => (), - viper::VerificationResultType::ConsistencyErrors(errors) => { + viper::VerificationResultKind::Success => (), + viper::VerificationResultKind::ConsistencyErrors(errors) => { for error in errors { PrustiError::internal( format!("consistency error in {program_name}: {error}"), @@ -180,7 +180,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } overall_result = VerificationResult::Failure; } - viper::VerificationResultType::Failure(errors) => { + viper::VerificationResultKind::Failure(errors) => { for verification_error in errors { debug!( "Verification error in {}: {:?}", @@ -251,7 +251,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { } overall_result = VerificationResult::Failure; } - viper::VerificationResultType::JavaException(exception) => { + viper::VerificationResultKind::JavaException(exception) => { error!("Java exception: {}", exception.get_stack_trace()); PrustiError::internal( format!("in {program_name}: {exception}"), diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 9cfd2b37795..9c447eb4c65 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -146,7 +146,6 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { } // collect and output Information used by IDE: - if !config::no_verify() && !config::skip_verification() { if config::selective_verify().is_none() { let verification_task = VerificationTask { @@ -165,10 +164,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { } } else if config::show_ide_info() && config::skip_verification() && !config::no_verify() { - // add a fake error - // for now maybe only for our use-case - // This stops cargo-prusti from successfully verifying crates - // it should not be able to after this invocation + // add a fake error, reason explained in issue #1261 fake_error(env) } }); diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 68bdd45e476..266ce4a75e8 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -9,6 +9,7 @@ use prusti_rustc_interface::{ span::Span, }; +/// The hir-visitor to collect all the function calls pub struct CallSpanFinder<'tcx> { pub env_query: EnvQuery<'tcx>, pub tcx: TyCtxt<'tcx>, diff --git a/prusti/src/ide_helper/compiler_info.rs b/prusti/src/ide_helper/compiler_info.rs index b22e6089e40..a4079dc4503 100644 --- a/prusti/src/ide_helper/compiler_info.rs +++ b/prusti/src/ide_helper/compiler_info.rs @@ -7,9 +7,8 @@ use prusti_rustc_interface::{ use prusti_viper::ide::vsc_span::VscSpan; use serde::{ser::SerializeStruct, Serialize}; -// create some struct storing all the information the IDE will ever need. -// needs to be transformable into json! - +/// This struct will be passed to prusti-assistant containing information +/// about the program that is currently being verified #[derive(Serialize)] pub struct IdeInfo { procedure_defs: Vec, @@ -47,13 +46,15 @@ impl IdeInfo { } } +/// A struct that contains either a reference to a procedure that can be verified +/// (for selective verification) or a function call (so a user can query +/// external_spec blocks for it). The name contains the defpath. pub struct ProcDef { pub name: String, pub defid: DefId, pub span: VscSpan, } -// defid is not needed for VSCode and not serializable as of now.. impl Serialize for ProcDef { fn serialize(&self, serializer: S) -> Result where @@ -66,7 +67,8 @@ impl Serialize for ProcDef { } } -// collect information about the program that will be passed to IDE: +/// collect information about the program that will be passed to IDE. +/// This should find all non-trusted functions that can be verified fn collect_procedures( env: &Environment<'_>, procedures: &Vec, @@ -114,14 +116,10 @@ fn collect_procedures( procs } +/// collect all the function calls, so the extension can query external_spec +/// templates for it fn collect_fncalls(env: &Environment<'_>) -> Vec<(String, DefId, Span)> { - // let l_hir = env.tcx().hir(); - // let hir_body = l_hir.body(); - let mut fnvisitor = call_finder::CallSpanFinder::new(env); - - // let mut adjusted_visitor = - // fnvisitor.visit_body(hir_body); env.tcx() .hir() .visit_all_item_likes_in_crate(&mut fnvisitor); diff --git a/prusti/src/ide_helper/fake_error.rs b/prusti/src/ide_helper/fake_error.rs index 91157ba9c89..bda5dec2bb1 100644 --- a/prusti/src/ide_helper/fake_error.rs +++ b/prusti/src/ide_helper/fake_error.rs @@ -1,6 +1,10 @@ use prusti_interface::environment::Environment; use prusti_rustc_interface::{errors::MultiSpan, span::DUMMY_SP}; +/// This error will be thrown when skipping verification (which is done +/// when we just collect information for prusti-assistant), because then +/// a successful result will be cached and subsequent actual verifications succeed +/// (see issue #1261) pub fn fake_error(env: Environment) { let sp = MultiSpan::from_span(DUMMY_SP); let message = String::from("[Prusti: FakeError]"); diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index bec314215b6..e8601368253 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -7,9 +7,8 @@ use prusti_rustc_interface::{ middle::ty::{self, Clause, DefIdTree, ImplSubject, PredicateKind, TyCtxt}, }; -// data structure to represent what we want to generate -// since we didnt manage to this with syn etc.. - +/// data structure to represent the code we want to generate +/// since we didnt manage to this with syn etc.. #[derive(Debug)] enum ExternSpecBlock { StandaloneFn { @@ -152,8 +151,10 @@ struct GenericArg { } impl fmt::Display for GenericArg { - // default formatter will include the traitbounds, - // otherwise just take .name field.. + /// For a generic argument, generate a string such as + /// T: Add + Display + /// default formatter will include the traitbounds, otherwise just take + /// .name field if they should be part of a where block fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let bounds_str = traitbounds_string(&self.bounds); write!(f, "{}: {bounds_str}", self.name) @@ -165,7 +166,13 @@ impl fmt::Display for GenericArg { } } -// the string for the where clause +/// the string for the where clause. Given a list of genericArgs, this would +/// generate a string of the form: +/// ``` +/// where +/// T: bound1 + bound2, +/// S: anotherbound, +/// ``` fn bounds_where_block(arglist: &[GenericArg]) -> String { let bounds_vec = arglist .iter() @@ -179,6 +186,8 @@ fn bounds_where_block(arglist: &[GenericArg]) -> String { } } +/// If a function or impl block has a list of generic arguments, this +/// will generate the string for it such as . fn generic_args_str(arglist: &Vec, include_bounds: bool) -> String { if !arglist.is_empty() { let res = arglist @@ -189,8 +198,7 @@ fn generic_args_str(arglist: &Vec, include_bounds: bool) -> String { } else { genarg.name.clone() } - }) // if we wanted to include defaults this should - // probably happen here + }) .collect::>() .join(", "); format!("<{res}>") @@ -199,7 +207,7 @@ fn generic_args_str(arglist: &Vec, include_bounds: bool) -> String { } } -// example result: Sized + PartialEq + Eq +/// example result: Sized + PartialEq + Eq fn traitbounds_string(boundlist: &Vec) -> String { if !boundlist.is_empty() { let res = boundlist @@ -408,8 +416,8 @@ fn get_parent_chain(defid: DefId, tcx: TyCtxt<'_>) -> String { defpath.join("::") } +/// indent all lines that are not empty by one tab fn indent(input: &String) -> String { - // indent all lines that are not empty by one tab let mut res = String::from("\t"); let len = input.len(); for (i, c) in input.chars().enumerate() { diff --git a/viper-toolchain b/viper-toolchain index 0391f0ffe80..31697ab4f00 100644 --- a/viper-toolchain +++ b/viper-toolchain @@ -1 +1 @@ -v-2023-01-31-0912 +v-2023-02-21-0716 diff --git a/viper/src/verification_result.rs b/viper/src/verification_result.rs index d3b1188f5be..d27d41b5aaf 100644 --- a/viper/src/verification_result.rs +++ b/viper/src/verification_result.rs @@ -9,19 +9,19 @@ use crate::{silicon_counterexample::SiliconCounterexample, JavaException}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct VerificationResult { pub item_name: String, - pub result_type: VerificationResultType, + pub result_type: VerificationResultKind, pub cached: bool, pub time_ms: u128, } impl VerificationResult { pub fn is_success(&self) -> bool { - matches!(self.result_type, VerificationResultType::Success) + matches!(self.result_type, VerificationResultKind::Success) } pub fn dummy_success() -> Self { VerificationResult { item_name: "".to_string(), - result_type: VerificationResultType::Success, + result_type: VerificationResultKind::Success, cached: false, time_ms: 0, } @@ -29,7 +29,7 @@ impl VerificationResult { } /// The result of a verification request on a Viper program. #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub enum VerificationResultType { +pub enum VerificationResultKind { /// The program verified. Success, /// The program did not verify. @@ -40,7 +40,7 @@ pub enum VerificationResultType { JavaException(JavaException), } -impl VerificationResultType { +impl VerificationResultKind { pub fn is_success(&self) -> bool { matches!(self, Self::Success) } diff --git a/viper/src/verifier.rs b/viper/src/verifier.rs index 08f9c0ea5ec..ef3a9b4d32b 100644 --- a/viper/src/verifier.rs +++ b/viper/src/verifier.rs @@ -11,7 +11,7 @@ use crate::{ silicon_counterexample::SiliconCounterexample, smt_manager::SmtManager, verification_backend::VerificationBackend, - verification_result::{VerificationError, VerificationResultType}, + verification_result::{VerificationError, VerificationResultKind}, }; use jni::{objects::JObject, JNIEnv}; use log::{debug, error, info}; @@ -108,7 +108,7 @@ impl<'a> Verifier<'a> { self } - pub fn verify(&mut self, program: Program) -> VerificationResultType { + pub fn verify(&mut self, program: Program) -> VerificationResultKind { self.ast_utils.with_local_frame(16, || { debug!( "Program to be verified:\n{}", @@ -119,7 +119,7 @@ impl<'a> Verifier<'a> { let consistency_errors = match self.ast_utils.check_consistency(program) { Ok(errors) => errors, Err(java_exception) => { - return VerificationResultType::JavaException(java_exception); + return VerificationResultKind::JavaException(java_exception); } }; ); @@ -129,7 +129,7 @@ impl<'a> Verifier<'a> { "The provided Viper program has {} consistency errors.", consistency_errors.len() ); - return VerificationResultType::ConsistencyErrors( + return VerificationResultKind::ConsistencyErrors( consistency_errors .into_iter() .map(|e| self.jni.to_string(e)) @@ -332,9 +332,9 @@ impl<'a> Verifier<'a> { )) } - VerificationResultType::Failure(errors) + VerificationResultKind::Failure(errors) } else { - VerificationResultType::Success + VerificationResultKind::Success } }) } From 137f9a27180c085354dbec8fb01965fe835c9ae7 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 23 Feb 2023 00:17:43 +0100 Subject: [PATCH 49/74] corrected tag for recent viper-ide nightly release --- viper-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viper-toolchain b/viper-toolchain index 31697ab4f00..42f75c30576 100644 --- a/viper-toolchain +++ b/viper-toolchain @@ -1 +1 @@ -v-2023-02-21-0716 +v-2023-02-18-0729 From f79e9665c24f143d24a8e4838a41085c88cf2ef3 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 23 Feb 2023 10:40:40 +0100 Subject: [PATCH 50/74] updated viper-toolchain again, this time to a compatible version --- viper-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viper-toolchain b/viper-toolchain index 42f75c30576..34d0cd245b3 100644 --- a/viper-toolchain +++ b/viper-toolchain @@ -1 +1 @@ -v-2023-02-18-0729 +v-2023-02-23-0933 From f2fa93aac8563967ab25fdbb48fa8717b345bfc1 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 23 Feb 2023 11:19:41 +0100 Subject: [PATCH 51/74] fixed error due to new scala version --- viper-sys/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viper-sys/build.rs b/viper-sys/build.rs index 9c4c259c18a..ede1655911d 100644 --- a/viper-sys/build.rs +++ b/viper-sys/build.rs @@ -90,7 +90,7 @@ fn main() { method!("wrapRefArray"), ]), java_class!("scala.math.BigInt", vec![ - constructor!(), + constructor!("(Ljava/math/BigInteger;)V"), ]), java_class!("scala.collection.mutable.ArrayBuffer", vec![ constructor!("(I)V"), From 5600499023d5d47de35da03af9a36db60cf0e240 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 23 Feb 2023 13:48:44 +0100 Subject: [PATCH 52/74] made testcases compile at least, but one is hard to update to new structure of verifier --- prusti-server/tests/basic_requests.rs | 27 +++++++++++++++------------ viper/tests/invalid_programs.rs | 4 ++-- viper/tests/simple_programs.rs | 4 ++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/prusti-server/tests/basic_requests.rs b/prusti-server/tests/basic_requests.rs index edb7aca6768..2a5b1083b53 100644 --- a/prusti-server/tests/basic_requests.rs +++ b/prusti-server/tests/basic_requests.rs @@ -4,7 +4,8 @@ use prusti_server::{ spawn_server_thread, tokio::runtime::Builder, PrustiClient, VerificationRequest, ViperBackendConfig, }; -use viper::VerificationResult; +use viper::VerificationResultKind; + lazy_static! { // only start the jvm & server once @@ -21,7 +22,7 @@ fn consistency_error() { }); match result { - VerificationResult::ConsistencyErrors(errors) => assert_eq!(errors.len(), 1), + VerificationResultKind::ConsistencyErrors(errors) => assert_eq!(errors.len(), 1), other => panic!("consistency errors not identified, instead found {other:?}"), } } @@ -31,17 +32,15 @@ fn empty_program() { let result = process_program(|_| ()); match result { - VerificationResult::Success => {} + VerificationResultKind::Success => {} other => panic!("empty program not verified successfully, instead found {other:?}"), } } -fn process_program(configure: F) -> VerificationResult +fn process_program(configure: F) -> VerificationResultKind where F: FnOnce(&mut Program), { - let client = PrustiClient::new(SERVER_ADDRESS.clone()).expect("Could not connect to server!"); - let mut program = Program { name: "dummy".to_string(), backend_types: vec![], @@ -61,10 +60,14 @@ where ), }; - Builder::new_current_thread() - .enable_all() - .build() - .expect("failed to construct Tokio runtime") - .block_on(client.verify(request)) - .expect("Verification request failed") + // Builder::new_current_thread() + // .enable_all() + // .build() + // .expect("failed to construct Tokio runtime") + // .block_on(PrustiClient::verify(SERVER_ADDRESS.clone(), request)) + // .expect("Verification request failed") + + // TODO @jthomme: either delete or update this function + // the success below is a placeholder so I can proceed + VerificationResultKind::Success } diff --git a/viper/tests/invalid_programs.rs b/viper/tests/invalid_programs.rs index 13fb3caa2ad..d2d401fe9f8 100644 --- a/viper/tests/invalid_programs.rs +++ b/viper/tests/invalid_programs.rs @@ -32,7 +32,7 @@ fn runtime_error() { assert!(matches!( verification_result, - VerificationResult::JavaException(_) + VerificationResultKind::JavaException(_) )); } @@ -99,7 +99,7 @@ where let verification_result = verifier.verify(program); match verification_result { - VerificationResult::ConsistencyErrors(_) => (), + VerificationResultKind::ConsistencyErrors(_) => (), other => panic!("consistency errors not identified, instead found {other:?}"), } } diff --git a/viper/tests/simple_programs.rs b/viper/tests/simple_programs.rs index 798f284c63a..2f491f9bdfa 100644 --- a/viper/tests/simple_programs.rs +++ b/viper/tests/simple_programs.rs @@ -56,7 +56,7 @@ fn failure_with_assert_false() { let verification_result = verifier.verify(program); - if let VerificationResult::Failure(errors) = verification_result { + if let VerificationResultKind::Failure(errors) = verification_result { assert_eq!(errors.len(), 1); assert_eq!( errors[0].full_id, @@ -204,7 +204,7 @@ fn failure_with_assign_if_and_assert() { let verification_result = verifier.verify(program); - if let VerificationResult::Failure(errors) = verification_result { + if let VerificationResultKind::Failure(errors) = verification_result { assert_eq!(errors.len(), 1); assert_eq!( errors[0].full_id, From 7e6ceafbf486642a1141d1c7d9c00027a152c3d7 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Thu, 23 Feb 2023 16:38:07 +0100 Subject: [PATCH 53/74] Fixed prusti-server test. Why do we get a 255 exit code on a successful verification? --- prusti-server/tests/basic_requests.rs | 28 +++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/prusti-server/tests/basic_requests.rs b/prusti-server/tests/basic_requests.rs index 2a5b1083b53..2e9bec1d7f1 100644 --- a/prusti-server/tests/basic_requests.rs +++ b/prusti-server/tests/basic_requests.rs @@ -2,9 +2,10 @@ use lazy_static::lazy_static; use prusti_common::vir::*; use prusti_server::{ spawn_server_thread, tokio::runtime::Builder, PrustiClient, VerificationRequest, - ViperBackendConfig, + ViperBackendConfig, ServerMessage, }; use viper::VerificationResultKind; +use futures_util::stream::StreamExt; lazy_static! { @@ -60,14 +61,21 @@ where ), }; - // Builder::new_current_thread() - // .enable_all() - // .build() - // .expect("failed to construct Tokio runtime") - // .block_on(PrustiClient::verify(SERVER_ADDRESS.clone(), request)) - // .expect("Verification request failed") + let get_result_type = |acc: VerificationResultKind, msg: ServerMessage| async move { + match msg { + ServerMessage::Termination(res) => res.result_type, + _ => acc, + } + }; - // TODO @jthomme: either delete or update this function - // the success below is a placeholder so I can proceed - VerificationResultKind::Success + Builder::new_current_thread() + .enable_all() + .build() + .expect("failed to construct Tokio runtime") + .block_on(async { + PrustiClient::verify(SERVER_ADDRESS.clone(), request) + .await + .fold(VerificationResultKind::Failure(vec![]), get_result_type) + .await + }) } From e8bddf0d67d403d65b55ae1325faa51b2f9ab80a Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 23 Feb 2023 18:32:40 +0100 Subject: [PATCH 54/74] Adjusted ui testcase that has additional note about existential quantifier now --- prusti-server/tests/basic_requests.rs | 7 +++---- prusti-tests/tests/verify/ui/forall_verify.rs | 3 --- prusti-tests/tests/verify/ui/forall_verify.stderr | 10 ++++++++-- prusti-tests/tests/verify/ui/forall_verify.stdout | 3 --- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/prusti-server/tests/basic_requests.rs b/prusti-server/tests/basic_requests.rs index 2e9bec1d7f1..9102460c677 100644 --- a/prusti-server/tests/basic_requests.rs +++ b/prusti-server/tests/basic_requests.rs @@ -1,12 +1,11 @@ +use futures_util::stream::StreamExt; use lazy_static::lazy_static; use prusti_common::vir::*; use prusti_server::{ - spawn_server_thread, tokio::runtime::Builder, PrustiClient, VerificationRequest, - ViperBackendConfig, ServerMessage, + spawn_server_thread, tokio::runtime::Builder, PrustiClient, ServerMessage, VerificationRequest, + ViperBackendConfig, }; use viper::VerificationResultKind; -use futures_util::stream::StreamExt; - lazy_static! { // only start the jvm & server once diff --git a/prusti-tests/tests/verify/ui/forall_verify.rs b/prusti-tests/tests/verify/ui/forall_verify.rs index 27bb02cc20e..42fd19e8cb3 100644 --- a/prusti-tests/tests/verify/ui/forall_verify.rs +++ b/prusti-tests/tests/verify/ui/forall_verify.rs @@ -25,9 +25,6 @@ fn test4() {} #[ensures(exists(|x: i32| identity(x) == x))] fn test5() {} -// TODO: Figure out why the error position is worse than for test3. I -// have checked the emitted Viper code (including the positions) and -// could not see any relevant differences. #[ensures(exists(|x: i32| identity(x) == x + 1))] fn test6() {} diff --git a/prusti-tests/tests/verify/ui/forall_verify.stderr b/prusti-tests/tests/verify/ui/forall_verify.stderr index 63d52476fb4..bc184727e7f 100644 --- a/prusti-tests/tests/verify/ui/forall_verify.stderr +++ b/prusti-tests/tests/verify/ui/forall_verify.stderr @@ -11,9 +11,15 @@ note: the error originates here | ^^^^^^^^^^^^^ error: [Prusti: verification error] postcondition might not hold. - --> $DIR/forall_verify.rs:32:1 + --> $DIR/forall_verify.rs:28:11 | -32 | fn test6() {} +28 | #[ensures(exists(|x: i32| identity(x) == x + 1))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the error originates here + --> $DIR/forall_verify.rs:29:1 + | +29 | fn test6() {} | ^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/prusti-tests/tests/verify/ui/forall_verify.stdout b/prusti-tests/tests/verify/ui/forall_verify.stdout index 29f01018e3e..db8ca2a9fe6 100644 --- a/prusti-tests/tests/verify/ui/forall_verify.stdout +++ b/prusti-tests/tests/verify/ui/forall_verify.stdout @@ -12,9 +12,6 @@ // A witness. -// TODO: Figure out why the error position is worse than for test3. I -// have checked the emitted Viper code (including the positions) and -// could not see any relevant differences. #![feature(type_ascription)] #![feature(stmt_expr_attributes)] From ede84511828bacb3fb3f7160e5e78f42699230bf Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 23 Feb 2023 21:37:21 +0100 Subject: [PATCH 55/74] Bugfixes for 2 testcases. All tests should be passing now --- prusti-viper/src/encoder/procedure_encoder.rs | 6 ++++-- prusti-viper/src/verifier.rs | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/prusti-viper/src/encoder/procedure_encoder.rs b/prusti-viper/src/encoder/procedure_encoder.rs index aeb7defa589..dcd859be860 100644 --- a/prusti-viper/src/encoder/procedure_encoder.rs +++ b/prusti-viper/src/encoder/procedure_encoder.rs @@ -3393,6 +3393,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { called_def_id, &procedure_contract, call_site_span, + substs, ); assert_one_magic_wand(procedure_contract.borrow_infos.len()).with_span(call_site_span)?; @@ -3545,14 +3546,15 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { called_def_id: ProcedureDefId, contract: &ProcedureContract<'tcx>, call_site_span: Span, + substs: ty::subst::SubstsRef<'tcx>, ) { let mut precondition_spans:Vec = contract - .functional_precondition(self.encoder.env(), self.substs) + .functional_precondition(self.encoder.env(), substs) .iter() .map(|(ts,_)| self.encoder.env().query.get_def_span(ts)) .collect(); let mut postcondition_spans: Vec = contract - .functional_postcondition(self.encoder.env(), self.substs) + .functional_postcondition(self.encoder.env(), substs) .iter() .map(|(ts,_)| self.encoder.env().query.get_def_span(ts)) .collect(); diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index bcf695c8043..b8f43b17eeb 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -196,12 +196,13 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { if let Some(silicon_counterexample) = &verification_error.counterexample { - if let Some(def_id) = self.encoder.error_manager() + let error_manager = self.encoder.error_manager(); + if let Some(def_id) = error_manager .get_def_id(&verification_error) { let counterexample = counterexample_translation_refactored::backtranslate( &self.encoder, - self.encoder.error_manager().position_manager(), + error_manager.position_manager(), def_id, silicon_counterexample, ); From 3572fea1f9ac7d97541fa2396aea265a2fc30cb8 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Fri, 24 Feb 2023 10:37:39 +0100 Subject: [PATCH 56/74] Add debug output --- prusti-server/src/process_verification.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index c8b0a00a8f1..8101f909621 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -6,7 +6,7 @@ use crate::{ServerMessage, VerificationRequest, ViperBackendConfig}; use futures::{lock, stream::Stream}; -use log::info; +use log::{info, debug}; use prusti_common::{ config, report::log::{report, to_legal_file_name}, @@ -271,12 +271,15 @@ fn verify_and_poll_msgs( let reporter = jni.unwrap_result(verifier_wrapper.call_reporter(*verifier.verifier_instance())); let rep_glob_ref = env.new_global_ref(reporter).unwrap(); + debug!("Starting viper message polling thread"); + // start thread for polling messages thread::scope(|scope| { let polling_thread = scope.spawn(|| polling_function(viper_arc, &rep_glob_ref, sender)); result_type = verifier.verify(viper_program); polling_thread.join().unwrap(); }); + debug!("Viper message polling thread terminated"); result_type } @@ -298,13 +301,14 @@ fn polling_function( let msg = reporter_wrapper .call_getNewMessage(reporter_instance) .unwrap(); + debug!("Polling thread received {}", jni.class_name(msg).as_str()); match jni.class_name(msg).as_str() { "viper.silver.reporter.QuantifierInstantiationsMessage" => { let msg_wrapper = silver::reporter::QuantifierInstantiationsMessage::with(env); let q_name = jni.get_string(jni.unwrap_result(msg_wrapper.call_quantifier(msg))); let q_inst = jni.unwrap_result(msg_wrapper.call_instantiations(msg)); - info!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); + debug!("QuantifierInstantiationsMessage: {} {}", q_name, q_inst); // also matches the "-aux" and "_precondition" quantifiers generated // we encoded the position id in the line and column number since this is not used by // prusti either way @@ -347,7 +351,7 @@ fn polling_function( let viper_triggers = jni.get_string(jni.unwrap_result(msg_wrapper.call_triggers__string(msg))); - info!( + debug!( "QuantifierChosenTriggersMessage: {} {} {}", viper_quant_str, viper_triggers, pos_id ); From bac27bf5c3209dadd467d489d0dfb23e8576a90e Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sat, 25 Feb 2023 01:48:58 +0100 Subject: [PATCH 57/74] Make cache save when not using a server --- Cargo.lock | 1 + prusti-server/src/process_verification.rs | 82 ++++++++++++++--------- prusti-viper/Cargo.toml | 1 + prusti-viper/src/verifier.rs | 8 +-- 4 files changed, 56 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed620f55ef3..ca090ca9afd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2320,6 +2320,7 @@ dependencies = [ "rustc-hash", "serde", "serde_json", + "tokio", "viper", "vir", ] diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index 8101f909621..d3e89ac53fd 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -6,7 +6,7 @@ use crate::{ServerMessage, VerificationRequest, ViperBackendConfig}; use futures::{lock, stream::Stream}; -use log::{info, debug}; +use log::{debug, info}; use prusti_common::{ config, report::log::{report, to_legal_file_name}, @@ -30,9 +30,22 @@ enum ServerRequest { SaveCache, } +struct ThreadJoin { + handle: Option>, +} + +impl Drop for ThreadJoin { + fn drop(&mut self) { + self.handle.take().unwrap().join().unwrap(); + } +} + pub struct VerificationRequestProcessing { mtx_rx_servermsg: lock::Mutex>, mtx_tx_verreq: sync::Mutex>, + // mtx_tx_verreq has to be dropped before thread_join + #[allow(dead_code)] + thread_join: ThreadJoin, } impl Default for VerificationRequestProcessing { @@ -52,39 +65,13 @@ impl VerificationRequestProcessing { let mtx_rx_servermsg = lock::Mutex::new(rx_servermsg); let mtx_tx_verreq = sync::Mutex::new(tx_verreq); - let ret = Self { + let handle = thread::spawn(move || verification_thread(rx_verreq, tx_servermsg)); + Self { mtx_rx_servermsg, mtx_tx_verreq, - }; - thread::spawn(|| Self::verification_thread(rx_verreq, tx_servermsg)); - ret - } - - fn verification_thread( - rx_verreq: mpsc::Receiver, - tx_servermsg: mpsc::Sender, - ) { - let mut stopwatch = Stopwatch::start("verification_request_processing", "JVM startup"); - let viper = Arc::new(Viper::new_with_args( - &config::viper_home(), - config::extra_jvm_args(), - )); - let mut cache = PersistentCache::load_cache(config::cache_path()); - stopwatch.start_next("attach thread to JVM"); - let verification_context = viper.attach_current_thread(); - stopwatch.finish(); - // loop { - while let Ok(request) = rx_verreq.recv() { - match request { - ServerRequest::Verification(verification_request) => process_verification_request( - &viper, - &mut cache, - &verification_context, - &tx_servermsg, - verification_request, - ), - ServerRequest::SaveCache => cache.save(), - } + thread_join: ThreadJoin { + handle: Some(handle), + }, } } @@ -116,6 +103,37 @@ impl VerificationRequestProcessing { .unwrap(); } } + +fn verification_thread( + rx_verreq: mpsc::Receiver, + tx_servermsg: mpsc::Sender, +) { + debug!("Verification thread started."); + let mut stopwatch = Stopwatch::start("verification_request_processing", "JVM startup"); + let viper = Arc::new(Viper::new_with_args( + &config::viper_home(), + config::extra_jvm_args(), + )); + let mut cache = PersistentCache::load_cache(config::cache_path()); + stopwatch.start_next("attach thread to JVM"); + let verification_context = viper.attach_current_thread(); + stopwatch.finish(); + + while let Ok(request) = rx_verreq.recv() { + match request { + ServerRequest::Verification(verification_request) => process_verification_request( + &viper, + &mut cache, + &verification_context, + &tx_servermsg, + verification_request, + ), + ServerRequest::SaveCache => cache.save(), + } + } + debug!("Verification thread finished."); +} + pub fn process_verification_request( viper_arc: &Arc, cache: impl Cache, diff --git a/prusti-viper/Cargo.toml b/prusti-viper/Cargo.toml index 44492bf3385..5a44eb094e5 100644 --- a/prusti-viper/Cargo.toml +++ b/prusti-viper/Cargo.toml @@ -28,6 +28,7 @@ derive_more = "0.99.16" itertools = "0.10.3" async-stream = "0.3.3" futures-util = "0.3.25" +tokio = { version = "1.20", features = ["io-util", "net", "rt", "sync"] } [dev-dependencies] lazy_static = "1.4" diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index b8f43b17eeb..99b322b8c0a 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -31,9 +31,10 @@ use futures_util::{pin_mut, Stream, StreamExt}; use prusti_interface::specs::typed; use prusti_rustc_interface::span::DUMMY_SP; use prusti_server::{ - spawn_server_thread, tokio::runtime::Builder, PrustiClient, ServerMessage, VerificationRequest, + spawn_server_thread, PrustiClient, ServerMessage, VerificationRequest, VerificationRequestProcessing, ViperBackendConfig, }; +use tokio::runtime::Builder; use serde_json::json; use rustc_hash::FxHashMap; @@ -121,9 +122,8 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { .build() .unwrap(); - let mut overall_result = VerificationResult::Success; - rt.block_on(async { - overall_result = if let Some(server_address) = config::server_address() { + let overall_result = rt.block_on(async { + if let Some(server_address) = config::server_address() { let verification_messages = Self::verify_requests_server(requests, server_address); self.handle_stream(verification_messages).await } else { From b373c14091f5725a4d2790538fd0413d06072d00 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sat, 25 Feb 2023 12:19:37 +0100 Subject: [PATCH 58/74] Gracefully close also the receiving end of the websocket. --- prusti-server/src/server.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index bc5fe7e1739..33c70028a91 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -55,9 +55,8 @@ where let json_verify = warp::path!("json" / "verify") .and(warp::filters::ws::ws()) .map(|ws: warp::filters::ws::Ws| { - ws.on_upgrade(|websocket| async { - let (mut ws_send, mut ws_recv) = websocket.split(); - let req_msg = ws_recv.next().await.unwrap().unwrap(); + ws.on_upgrade(|mut websocket| async { + let req_msg = websocket.next().await.unwrap().unwrap(); let verification_request = req_msg .to_str() .and_then(|s: &str| serde_json::from_str(s).unwrap()) @@ -65,35 +64,34 @@ where let stream = VERIFICATION_REQUEST_PROCESSING.verify(verification_request); pin_mut!(stream); while let Some(server_msg) = stream.next().await { - ws_send + websocket .send(warp::filters::ws::Message::text( serde_json::to_string(&server_msg).unwrap(), )) .await .unwrap(); } - ws_send.close().await.unwrap(); + websocket.close().await.unwrap(); }) }); let bincode_verify = warp::path!("bincode" / "verify") .and(warp::filters::ws::ws()) .map(|ws: warp::filters::ws::Ws| { - ws.on_upgrade(|websocket| async { - let (mut ws_send, mut ws_recv) = websocket.split(); - let req_msg = ws_recv.next().await.unwrap().unwrap(); + ws.on_upgrade(|mut websocket| async { + let req_msg = websocket.next().await.unwrap().unwrap(); let verification_request = bincode::deserialize(req_msg.as_bytes()).unwrap(); let stream = VERIFICATION_REQUEST_PROCESSING.verify(verification_request); pin_mut!(stream); while let Some(server_msg) = stream.next().await { - ws_send + websocket .send(warp::filters::ws::Message::binary( bincode::serialize(&server_msg).unwrap(), )) .await .unwrap(); } - ws_send.close().await.unwrap(); + websocket.close().await.unwrap(); }) }); let save_cache = warp::post() From e84688f1b6ef33796ccc2cf71307386baa24170e Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sat, 25 Feb 2023 13:34:36 +0100 Subject: [PATCH 59/74] Close the websocket connection correctly --- prusti-server/src/server.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/prusti-server/src/server.rs b/prusti-server/src/server.rs index 33c70028a91..87dc48714a4 100644 --- a/prusti-server/src/server.rs +++ b/prusti-server/src/server.rs @@ -55,8 +55,9 @@ where let json_verify = warp::path!("json" / "verify") .and(warp::filters::ws::ws()) .map(|ws: warp::filters::ws::Ws| { - ws.on_upgrade(|mut websocket| async { - let req_msg = websocket.next().await.unwrap().unwrap(); + ws.on_upgrade(|websocket| async { + let (mut ws_send, mut ws_recv) = websocket.split(); + let req_msg = ws_recv.next().await.unwrap().unwrap(); let verification_request = req_msg .to_str() .and_then(|s: &str| serde_json::from_str(s).unwrap()) @@ -64,34 +65,39 @@ where let stream = VERIFICATION_REQUEST_PROCESSING.verify(verification_request); pin_mut!(stream); while let Some(server_msg) = stream.next().await { - websocket + ws_send .send(warp::filters::ws::Message::text( serde_json::to_string(&server_msg).unwrap(), )) .await .unwrap(); } - websocket.close().await.unwrap(); + ws_send.close().await.unwrap(); + // receive the client close to complete the handshake + ws_recv.next().await.unwrap().unwrap(); }) }); let bincode_verify = warp::path!("bincode" / "verify") .and(warp::filters::ws::ws()) .map(|ws: warp::filters::ws::Ws| { - ws.on_upgrade(|mut websocket| async { - let req_msg = websocket.next().await.unwrap().unwrap(); + ws.on_upgrade(|websocket| async { + let (mut ws_send, mut ws_recv) = websocket.split(); + let req_msg = ws_recv.next().await.unwrap().unwrap(); let verification_request = bincode::deserialize(req_msg.as_bytes()).unwrap(); let stream = VERIFICATION_REQUEST_PROCESSING.verify(verification_request); pin_mut!(stream); while let Some(server_msg) = stream.next().await { - websocket + ws_send .send(warp::filters::ws::Message::binary( bincode::serialize(&server_msg).unwrap(), )) .await .unwrap(); } - websocket.close().await.unwrap(); + ws_send.close().await.unwrap(); + // receive the client close to complete the handshake + ws_recv.next().await.unwrap().unwrap(); }) }); let save_cache = warp::post() From c39ece5d3ad83afd528c54257677594cd47b759e Mon Sep 17 00:00:00 2001 From: hegglinc Date: Sat, 25 Feb 2023 21:37:58 +0100 Subject: [PATCH 60/74] Fix bug where successful selective verification is still cached In general, when skipping verification, we introduced a so called fake_error to make sure the compiler does not just cache this as successful verification and then skip later verifications even if that's not intended. Same issue occured for selective verification, when verifying a single method that is "correct", this would be cached. Now we throw a fake error in this case too, to avoid this. --- prusti/src/callbacks.rs | 10 +++++++--- prusti/src/ide_helper/fake_error.rs | 2 +- prusti/src/verifier.rs | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 9c447eb4c65..558c8e9b217 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -152,7 +152,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { procedures: annotated_procedures, types, }; - verify(env, def_spec, verification_task); + verify(&env, def_spec, verification_task); } else { let target_def_path = config::selective_verify().unwrap(); let procedures = annotated_procedures @@ -160,12 +160,16 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { .filter(|x| env.name.get_unique_item_name(*x) == target_def_path) .collect(); let selective_task = VerificationTask { procedures, types }; - verify(env, def_spec, selective_task); + // fake_error because otherwise a verification-success + // (for a single method for example) will cause this result + // to be cached by compiler at the moment + verify(&env, def_spec, selective_task); + fake_error(&env); } } else if config::show_ide_info() && config::skip_verification() && !config::no_verify() { // add a fake error, reason explained in issue #1261 - fake_error(env) + fake_error(&env) } }); diff --git a/prusti/src/ide_helper/fake_error.rs b/prusti/src/ide_helper/fake_error.rs index bda5dec2bb1..f059f7b4217 100644 --- a/prusti/src/ide_helper/fake_error.rs +++ b/prusti/src/ide_helper/fake_error.rs @@ -5,7 +5,7 @@ use prusti_rustc_interface::{errors::MultiSpan, span::DUMMY_SP}; /// when we just collect information for prusti-assistant), because then /// a successful result will be cached and subsequent actual verifications succeed /// (see issue #1261) -pub fn fake_error(env: Environment) { +pub fn fake_error(env: &Environment) { let sp = MultiSpan::from_span(DUMMY_SP); let message = String::from("[Prusti: FakeError]"); let help = None; diff --git a/prusti/src/verifier.rs b/prusti/src/verifier.rs index 72adf5bc0fd..8422a065e3f 100644 --- a/prusti/src/verifier.rs +++ b/prusti/src/verifier.rs @@ -10,7 +10,7 @@ use prusti_interface::{ use prusti_viper::verifier::Verifier; pub fn verify<'tcx>( - env: Environment<'tcx>, + env: &Environment<'tcx>, def_spec: typed::DefSpecificationMap, verification_task: VerificationTask<'tcx>, ) { @@ -50,7 +50,7 @@ pub fn verify<'tcx>( debug!("Dump borrow checker info..."); env.dump_borrowck_info(&verification_task.procedures); - let mut verifier = Verifier::new(&env, def_spec); + let mut verifier = Verifier::new(env, def_spec); let verification_result = verifier.verify(&verification_task); debug!("Verifier returned {:?}", verification_result); From f83a088de6c1196ce0e69338d9fcde200e965f42 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Mon, 27 Feb 2023 17:14:33 +0100 Subject: [PATCH 61/74] Fix bug in translation of spans to vscode-spans --- prusti-viper/src/ide/vsc_span.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prusti-viper/src/ide/vsc_span.rs b/prusti-viper/src/ide/vsc_span.rs index d1c63fe8c38..0ae88513354 100644 --- a/prusti-viper/src/ide/vsc_span.rs +++ b/prusti-viper/src/ide/vsc_span.rs @@ -22,10 +22,10 @@ impl VscSpan { if let Ok((l1, l2)) = sourcemap.is_valid_span(*sp) { Some(Self { - column_end: l2.col.0, column_start: l1.col.0, + column_end: l2.col.0, + line_start: l1.line, line_end: l2.line, - line_start: l2.line, file_name: fname, // the following 3 are not relevant here, we just want to be // able to reuse the existing methods and the parser From 8b4563febfe9892f0719597f145f2eaebbc0ed22 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Wed, 1 Mar 2023 13:38:59 +0100 Subject: [PATCH 62/74] Bump prusti version to 0.3.0 Prusti Assistant offers a few new features, and it checks for this version for them to be available. --- prusti/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prusti/Cargo.toml b/prusti/Cargo.toml index 6cbad447f27..a12d22b59b0 100644 --- a/prusti/Cargo.toml +++ b/prusti/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prusti" -version = "0.2.1" +version = "0.3.0" authors = ["Prusti Devs "] edition = "2021" From eaeef452b1ff1daf9842c8098fecd49c93e958eb Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sat, 4 Mar 2023 09:49:34 +0100 Subject: [PATCH 63/74] Update viper-toolchain version --- viper-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viper-toolchain b/viper-toolchain index 34d0cd245b3..1f4a874cf2d 100644 --- a/viper-toolchain +++ b/viper-toolchain @@ -1 +1 @@ -v-2023-02-23-0933 +v-2023-03-02-1938 From 82b0eb87ba117e21be70cbd3315566b242753540 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sun, 5 Mar 2023 01:05:45 +0100 Subject: [PATCH 64/74] Implement some comments (untested) --- Cargo.lock | 7 +- docs/dev-guide/src/config/flags.md | 38 +- prusti-server/src/process_verification.rs | 16 +- prusti-server/src/server_message.rs | 11 + prusti-server/src/verification_request.rs | 20 +- prusti-server/tests/basic_requests.rs | 15 +- prusti-smt-solver/Cargo.toml | 1 - prusti-utils/src/config.rs | 12 +- .../src/encoder/errors/position_manager.rs | 3 +- prusti-viper/src/verifier.rs | 325 ++++++++++-------- viper/src/verification_result.rs | 7 +- 11 files changed, 265 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca090ca9afd..2912b2dca49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2144,7 +2144,7 @@ dependencies = [ [[package]] name = "prusti" -version = "0.2.1" +version = "0.3.0" dependencies = [ "chrono", "env_logger", @@ -2252,7 +2252,6 @@ version = "0.1.0" dependencies = [ "async-std", "futures", - "log", ] [[package]] @@ -2832,9 +2831,9 @@ checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ "libc", "signal-hook-registry", diff --git a/docs/dev-guide/src/config/flags.md b/docs/dev-guide/src/config/flags.md index 8a6c0f0322e..2390b2212ab 100644 --- a/docs/dev-guide/src/config/flags.md +++ b/docs/dev-guide/src/config/flags.md @@ -54,18 +54,25 @@ | [`PRINT_DESUGARED_SPECS`](#print_desugared_specs) | `bool` | `false` | A | | [`PRINT_HASH`](#print_hash) | `bool` | `false` | A | | [`PRINT_TYPECKD_SPECS`](#print_typeckd_specs) | `bool` | `false` | A | +| [`QUERY_METHOD_SIGNATURE`](#query_method_signature) | `Option` | `None` | A | | [`QUIET`](#quiet) | `bool` | `false` | A* | +| [`REPORT_VIPER_MESSAGES`](#report_viper_messages) | `bool` | `false` | A | +| [`SELECTIVE_VERIFY`](#selective_verify) | `Option` | `None` | A | | [`SERVER_ADDRESS`](#server_address) | `Option` | `None` | A | | [`SERVER_MAX_CONCURRENCY`](#server_max_concurrency) | `Option` | `None` | A | | [`SERVER_MAX_STORED_VERIFIERS`](#server_max_stored_verifiers) | `Option` | `None` | A | +| [`SHOW_IDE_INFO`](#show_ide_info) | `bool` | `false` | A | | [`SIMPLIFY_ENCODING`](#simplify_encoding) | `bool` | `true` | A | | [`SKIP_UNSUPPORTED_FEATURES`](#skip_unsupported_features) | `bool` | `false` | A | +| [`SKIP_VERIFICATION`](#skip_verification) | `bool` | `false` | A | | [`SMT_QI_BOUND_GLOBAL`](#smt_qi_bound_global) | `Option` | `None` | A | -[`SMT_QI_BOUND_GLOBAL_KIND`](#smt_qi_bound_global_kind) | `Option` | `None` | A | +| [`SMT_QI_BOUND_GLOBAL_KIND`](#smt_qi_bound_global_kind) | `Option` | `None` | A | | [`SMT_QI_BOUND_TRACE`](#smt_qi_bound_trace) | `Option` | `None` | A | | [`SMT_QI_BOUND_TRACE_KIND`](#smt_qi_bound_trace_kind) | `Option` | `None` | A | -| [`SMT_QI_IGNORE_BUILTIN`](#smt_qi_ignore_builtin) | `bool` | `true` | A | | [`SMT_QI_EAGER_THRESHOLD`](#smt_qi_eager_threshold) | `u64` | `1000` | A | +| [`SMT_QI_IGNORE_BUILTIN`](#smt_qi_ignore_builtin) | `bool` | `true` | A | +| [`SMT_QI_PROFILE`](#smt_qi_profile) | `Option` | `None` | A | +| [`SMT_QI_PROFILE_FREQ`](#smt_qi_profile_freq) | `Option` | `None` | A | | [`SMT_SOLVER_PATH`](#smt_solver_path) | `Option` | `env::var("Z3_EXE")` | A | | [`SMT_SOLVER_WRAPPER_PATH`](#smt_solver_wrapper_path) | `Option` | `None` | A | | [`SMT_UNIQUE_TRIGGERS_BOUND`](#smt_unique_triggers_bound) | `Option` | `None` | A | @@ -344,12 +351,24 @@ When enabled, prints the hash of a verification request (the hash is used for ca When enabled, prints the type-checked specifications. +## `QUERY_METHOD_SIGNATURE` + +When enabled, @cedihegi I don't know what exactly happens :) + ## `QUIET` When enabled, user messages are not printed. Otherwise, messages output into `stderr`. > **Note:** `cargo prusti` sets this flag with `DEFAULT_PRUSTI_QUIET=true`. +## `REPORT_VIPER_MESSAGES` + +When enabled for both server and client, certain supported Viper messages will be reported to the user. + +## `SELECTIVE_VERIFY` + +When enabled, @cedihegi I don't know what exactly happens :) + ## `SERVER_ADDRESS` When set to an address and port (e.g. `"127.0.0.1:2468"`), Prusti will connect to the given server and use it for its verification backend. @@ -366,6 +385,10 @@ Maximum amount of instantiated Viper verifiers the server will keep around for r > **Note:** This does _not_ limit how many verification requests the server handles concurrently, only the size of what is essentially its verifier cache. +## `SHOW_IDE_INFO` + +When enabled, @cedihegi I don't know what exactly happens :) + ## `SIMPLIFY_ENCODING` When enabled, the encoded program is simplified before it is passed to the Viper backend. @@ -374,6 +397,10 @@ When enabled, the encoded program is simplified before it is passed to the Viper When enabled, features not supported by Prusti will be reported as warnings rather than errors. +## `SKIP_VERIFICATION` + +When enabled, @cedihegi I don't know what exactly happens :) + ## `SMT_QI_BOUND_GLOBAL` If not `None`, checks that the number of global quantifier instantiations reported by the SMT wrapper is smaller than the specified bound. @@ -401,6 +428,13 @@ If not `None`, checks that the number of quantifier instantiations in each trace ## `SMT_QI_IGNORE_BUILTIN` When enabled, ignores the built-in quantifiers in SMT quantifier instantiation bounds checking. +## `SMT_QI_PROFILE` + +When enabled, the Z3 backend periodically (and on finish) reports the number of quantifier instantiations to Viper. + +## `SMT_QI_PROFILE_FREQ` + +Frequency of the quantifier instantiation reporting of Z3 (every X instantiations, a report is issued). ## `SMT_QI_EAGER_THRESHOLD` diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index d3e89ac53fd..a752550254b 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -205,7 +205,7 @@ pub fn process_verification_request( let mut result = VerificationResult { item_name: request.program.get_name().to_string(), - result_type: VerificationResultKind::Success, + kind: VerificationResultKind::Success, cached: false, time_ms: 0, }; @@ -224,7 +224,7 @@ pub fn process_verification_request( }); } result.cached = true; - normalization_info.denormalize_result(&mut result.result_type); + normalization_info.denormalize_result(&mut result.kind); sender.send(ServerMessage::Termination(result)).unwrap(); return; } @@ -241,7 +241,7 @@ pub fn process_verification_request( new_viper_verifier(program_name, verification_context, request.backend_config); stopwatch.start_next("verification"); - result.result_type = if config::report_viper_messages() { + result.kind = if config::report_viper_messages() { verify_and_poll_msgs( verification_context, verifier, @@ -256,7 +256,7 @@ pub fn process_verification_request( // Don't cache Java exceptions, which might be due to misconfigured paths. if config::enable_cache() - && !matches!(result.result_type, VerificationResultKind::JavaException(_)) + && !matches!(result.kind, VerificationResultKind::JavaException(_)) { info!( "Storing new cached result {:?} for program {}", @@ -266,7 +266,7 @@ pub fn process_verification_request( cache.insert(hash, result.clone()); } - normalization_info.denormalize_result(&mut result.result_type); + normalization_info.denormalize_result(&mut result.kind); sender.send(ServerMessage::Termination(result)).unwrap(); }) } @@ -278,7 +278,7 @@ fn verify_and_poll_msgs( viper_arc: &Arc, sender: mpsc::Sender, ) -> VerificationResultKind { - let mut result_type = VerificationResultKind::Success; + let mut kind = VerificationResultKind::Success; // get the reporter global reference outside of the thread scope because it needs to // be dropped by thread attached to the jvm. This is also why we pass it as reference @@ -294,11 +294,11 @@ fn verify_and_poll_msgs( // start thread for polling messages thread::scope(|scope| { let polling_thread = scope.spawn(|| polling_function(viper_arc, &rep_glob_ref, sender)); - result_type = verifier.verify(viper_program); + kind = verifier.verify(viper_program); polling_thread.join().unwrap(); }); debug!("Viper message polling thread terminated"); - result_type + kind } fn polling_function( diff --git a/prusti-server/src/server_message.rs b/prusti-server/src/server_message.rs index fcf542d9d0d..8eacbb17a04 100644 --- a/prusti-server/src/server_message.rs +++ b/prusti-server/src/server_message.rs @@ -7,13 +7,24 @@ use viper::VerificationResult; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +/// A message from a Prusti server to a Prusti client. It may contain a result +/// of a verification or anything a server might send to a client. pub enum ServerMessage { + /// Contains the result of a verification and signals that this verification + /// has terminated. Termination(VerificationResult), + + /// A message created by the Viper backend with Z3 about + /// the number of instantiations of the named quantifier. QuantifierInstantiation { q_name: String, insts: u64, pos_id: u64, }, + + /// Also created by the Viper backend. The viper_quant is the expression the + /// quantifier was translated to in silver syntax while the triggers are the + /// triggers provided or automatically derived for this quantifier. QuantifierChosenTriggers { viper_quant: String, triggers: String, diff --git a/prusti-server/src/verification_request.rs b/prusti-server/src/verification_request.rs index 9b04d060c0e..8acc0a71745 100644 --- a/prusti-server/src/verification_request.rs +++ b/prusti-server/src/verification_request.rs @@ -52,19 +52,27 @@ impl ViperBackendConfig { verifier_args.extend(vec![ "--assertTimeout".to_string(), config::assert_timeout().to_string(), + ]); + let mut prover_args = vec![ "--proverConfigArgs".to_string(), // model.partial changes the default case of functions in counterexamples // to #unspecified format!( - "smt.qi.eager_threshold={} model.partial={} smt.qi.profile={} smt.qi.profile_freq={}", + "smt.qi.eager_threshold={} model.partial={}", config::smt_qi_eager_threshold(), config::counterexample(), - config::smt_qi_profile(), - config::smt_qi_profile_freq() ), - "--logLevel".to_string(), - "ERROR".to_string(), - ]); + ]; + + if let Some(smt_qi_profile) = config::smt_qi_profile() { + prover_args.push(format!("smt.qi.profile={}", smt_qi_profile)); + } + if let Some(smt_qi_profile_freq) = config::smt_qi_profile_freq() { + prover_args.push(format!("smt.qi.profile_freq={}", smt_qi_profile_freq)); + } + verifier_args.extend(prover_args); + + verifier_args.extend(vec!["--logLevel".to_string(), "ERROR".to_string()]); if let Some(check_timeout) = config::check_timeout() { verifier_args.push("--checkTimeout".to_string()); diff --git a/prusti-server/tests/basic_requests.rs b/prusti-server/tests/basic_requests.rs index 9102460c677..563f0701d47 100644 --- a/prusti-server/tests/basic_requests.rs +++ b/prusti-server/tests/basic_requests.rs @@ -60,21 +60,18 @@ where ), }; - let get_result_type = |acc: VerificationResultKind, msg: ServerMessage| async move { - match msg { - ServerMessage::Termination(res) => res.result_type, - _ => acc, - } - }; - Builder::new_current_thread() .enable_all() .build() .expect("failed to construct Tokio runtime") .block_on(async { PrustiClient::verify(SERVER_ADDRESS.clone(), request) + .collect::>() .await - .fold(VerificationResultKind::Failure(vec![]), get_result_type) - .await + .find_map(|&&m| match m { + ServerMessage::Termination(res) => Some(res.kind), + _ => None, + }) + .unwrap_or_else(VerificationResultKind::Failure(vec![])) }) } diff --git a/prusti-smt-solver/Cargo.toml b/prusti-smt-solver/Cargo.toml index d12af004aec..4793cb0a5c4 100644 --- a/prusti-smt-solver/Cargo.toml +++ b/prusti-smt-solver/Cargo.toml @@ -12,7 +12,6 @@ doctest = false # and no doc tests [dependencies] futures = "0.3" -log = { version = "0.4", features = ["release_max_level_info"] } [dependencies.async-std] version = "1.7.0" diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index e9e286fa3d3..cbe558af476 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -102,8 +102,8 @@ lazy_static::lazy_static! { settings.set_default("quiet", false).unwrap(); settings.set_default("assert_timeout", 10_000).unwrap(); settings.set_default("smt_qi_eager_threshold", 1000).unwrap(); - settings.set_default("smt_qi_profile", true).unwrap(); - settings.set_default("smt_qi_profile_freq", 10000).unwrap(); + settings.set_default::>("smt_qi_profile", None).unwrap(); + settings.set_default::>("smt_qi_profile_freq", None).unwrap(); settings.set_default("report_viper_messages", false).unwrap(); settings.set_default("use_more_complete_exhale", true).unwrap(); settings.set_default("skip_unsupported_features", false).unwrap(); @@ -504,13 +504,13 @@ pub fn smt_qi_eager_threshold() -> u64 { read_setting("smt_qi_eager_threshold") } -/// Set `qi.profile` value to the given one. -pub fn smt_qi_profile() -> bool { +/// Whether to make Z3 periodically report quantifier instantiations to Viper. +pub fn smt_qi_profile() -> Option { read_setting("smt_qi_profile") } -/// Set `qi.profile_freq` value to the given one. -pub fn smt_qi_profile_freq() -> u64 { +/// The frequency for the report of quantifier instantiations of Z3 to Viper. +pub fn smt_qi_profile_freq() -> Option { read_setting("smt_qi_profile_freq") } diff --git a/prusti-viper/src/encoder/errors/position_manager.rs b/prusti-viper/src/encoder/errors/position_manager.rs index 419b77329e1..ae9a4b71764 100644 --- a/prusti-viper/src/encoder/errors/position_manager.rs +++ b/prusti-viper/src/encoder/errors/position_manager.rs @@ -8,7 +8,7 @@ use vir_crate::polymorphic::Position; use rustc_hash::FxHashMap; use prusti_rustc_interface::span::source_map::SourceMap; use prusti_rustc_interface::errors::MultiSpan; -use log::debug; +use log::{debug, trace}; use prusti_interface::data::ProcedureDefId; /// Mapping from VIR positions to the source code that generated them. @@ -39,6 +39,7 @@ impl<'tcx> PositionManager<'tcx> let span = span.into(); let pos_id = self.next_pos_id; self.next_pos_id += 1; + trace!("Register position id {} for span {:?} in {:?}, ", pos_id, span, def_id); let pos = if let Some(primary_span) = span.primary_span() { let lines_info_res = self diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 99b322b8c0a..5ae1d379aad 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -148,173 +148,28 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { let mut quantifier_instantiations: FxHashMap<(u64, String), FxHashMap> = FxHashMap::default(); - // if we are not running in an ide, we want the errors to be reported sortedly let mut prusti_errors: Vec<_> = vec![]; pin_mut!(verification_messages); while let Some((program_name, server_msg)) = verification_messages.next().await { match server_msg { - ServerMessage::Termination(result) => { - if config::show_ide_info() { - PrustiError::message( - format!( - "ideVerificationResult{}", - serde_json::to_string(&IdeVerificationResult::from_res(&result)) - .unwrap() - ), - DUMMY_SP.into(), - ) - .emit(&self.env.diagnostic); - } - match result.result_type { - // nothing to do - viper::VerificationResultKind::Success => (), - viper::VerificationResultKind::ConsistencyErrors(errors) => { - for error in errors { - PrustiError::internal( - format!("consistency error in {program_name}: {error}"), - DUMMY_SP.into(), - ) - .emit(&self.env.diagnostic); - } - overall_result = VerificationResult::Failure; - } - viper::VerificationResultKind::Failure(errors) => { - for verification_error in errors { - debug!( - "Verification error in {}: {:?}", - program_name.clone(), - verification_error - ); - let mut prusti_error = self.encoder.error_manager() - .translate_verification_error(&verification_error); - - // annotate with counterexample, if requested - if config::counterexample() { - if config::unsafe_core_proof() { - if let Some(silicon_counterexample) = - &verification_error.counterexample - { - let error_manager = self.encoder.error_manager(); - if let Some(def_id) = error_manager - .get_def_id(&verification_error) - { - let counterexample = counterexample_translation_refactored::backtranslate( - &self.encoder, - error_manager.position_manager(), - def_id, - silicon_counterexample, - ); - prusti_error = - counterexample.annotate_error(prusti_error); - } else { - prusti_error = prusti_error.add_note( - format!( - "the verifier produced a counterexample for {program_name}, but it could not be mapped to source code" - ), - None, - ); - } - } - } else if let Some(silicon_counterexample) = - &verification_error.counterexample - { - if let Some(def_id) = self.encoder.error_manager() - .get_def_id(&verification_error) - { - let counterexample = - counterexample_translation::backtranslate( - &self.encoder, - def_id, - silicon_counterexample, - ); - prusti_error = - counterexample.annotate_error(prusti_error); - } else { - prusti_error = prusti_error.add_note( - format!( - "the verifier produced a counterexample for {program_name}, but it could not be mapped to source code" - ), - None, - ); - } - } - } - debug!("Prusti error: {:?}", prusti_error); - if prusti_error.is_disabled() { - prusti_error.cancel(); - } else if config::show_ide_info() { - prusti_error.emit(&self.env.diagnostic); - } else { - prusti_errors.push(prusti_error); - } - } - overall_result = VerificationResult::Failure; - } - viper::VerificationResultKind::JavaException(exception) => { - error!("Java exception: {}", exception.get_stack_trace()); - PrustiError::internal( - format!("in {program_name}: {exception}"), - DUMMY_SP.into(), - ) - .emit(&self.env.diagnostic); - overall_result = VerificationResult::Failure; - } - } - } + ServerMessage::Termination(result) => self.handle_termination_message(program_name, result, &mut prusti_errors, &mut overall_result), ServerMessage::QuantifierInstantiation { q_name, insts, pos_id, - } => { - if config::report_viper_messages() { - match self.encoder.error_manager().position_manager().get_span_from_id(pos_id) { - Some(span) => { - let key = (pos_id, program_name.clone()); - if !quantifier_instantiations.contains_key(&key) { - quantifier_instantiations.insert(key.clone(), FxHashMap::default()); - } - let map = quantifier_instantiations.get_mut(&key).unwrap(); - // this replaces the old entry which is exactly what we want - map.insert(q_name, insts); - let mut n: u64 = 0; - for insts in map.values() { - n += *insts; - } - PrustiError::message( - format!("quantifierInstantiationsMessage{}", - json!({"instantiations": n, "method": program_name}), - ), span.clone() - ).emit(&self.env.diagnostic); - }, - None => error!("#{insts} quantifier instantiations of {q_name} for unknown position id {pos_id} in verification of {program_name}"), - } - } - } + } => self.handle_quantifier_instantiation_message(program_name, q_name, insts, pos_id, &mut quantifier_instantiations), ServerMessage::QuantifierChosenTriggers { viper_quant, triggers, pos_id, - } => { - if config::report_viper_messages() && pos_id != 0 { - match self.encoder.error_manager().position_manager().get_span_from_id(pos_id) { - Some(span) => { - PrustiError::message( - format!("quantifierChosenTriggersMessage{}", - json!({"viper_quant": viper_quant, "triggers": triggers}), - ), span.clone() - ).emit(&self.env.diagnostic); - }, - None => error!("Invalid position id {pos_id} for viper quantifier {viper_quant} in verification of {program_name}"), - } - } - } + } => self.handle_quantifier_chosen_triggers_message(program_name, viper_quant, triggers, pos_id), } } // if we are in an ide, we already emit the errors asynchronously, otherwise we wait for - // all of them in order to sort them + // all of them because we want the errors to be reported sortedly if !config::show_ide_info() { prusti_errors.sort(); @@ -330,6 +185,177 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { overall_result } + fn handle_termination_message( + &self, + program_name: String, + result: viper::VerificationResult, + prusti_errors: &mut Vec, + overall_result: &mut VerificationResult + ) { + if config::show_ide_info() { + PrustiError::message( + format!( + "ideVerificationResult{}", + serde_json::to_string(&IdeVerificationResult::from_res(&result)) + .unwrap() + ), + DUMMY_SP.into(), + ) + .emit(&self.env.diagnostic); + } + match result.kind { + // nothing to do + viper::VerificationResultKind::Success => (), + viper::VerificationResultKind::ConsistencyErrors(errors) => { + for error in errors { + PrustiError::internal( + format!("consistency error in {program_name}: {error}"), + DUMMY_SP.into(), + ) + .emit(&self.env.diagnostic); + } + *overall_result = VerificationResult::Failure; + } + viper::VerificationResultKind::Failure(errors) => { + for verification_error in errors { + debug!( + "Verification error in {}: {:?}", + program_name.clone(), + verification_error + ); + let mut prusti_error = self.encoder.error_manager() + .translate_verification_error(&verification_error); + + // annotate with counterexample, if requested + if config::counterexample() { + if config::unsafe_core_proof() { + if let Some(silicon_counterexample) = + &verification_error.counterexample + { + let error_manager = self.encoder.error_manager(); + if let Some(def_id) = error_manager + .get_def_id(&verification_error) + { + let counterexample = counterexample_translation_refactored::backtranslate( + &self.encoder, + error_manager.position_manager(), + def_id, + silicon_counterexample, + ); + prusti_error = + counterexample.annotate_error(prusti_error); + } else { + prusti_error = prusti_error.add_note( + format!( + "the verifier produced a counterexample for {program_name}, but it could not be mapped to source code" + ), + None, + ); + } + } + } else if let Some(silicon_counterexample) = + &verification_error.counterexample + { + if let Some(def_id) = self.encoder.error_manager() + .get_def_id(&verification_error) + { + let counterexample = + counterexample_translation::backtranslate( + &self.encoder, + def_id, + silicon_counterexample, + ); + prusti_error = + counterexample.annotate_error(prusti_error); + } else { + prusti_error = prusti_error.add_note( + format!( + "the verifier produced a counterexample for {program_name}, but it could not be mapped to source code" + ), + None, + ); + } + } + } + + debug!("Prusti error: {:?}", prusti_error); + if prusti_error.is_disabled() { + prusti_error.cancel(); + } else if config::show_ide_info() { + prusti_error.emit(&self.env.diagnostic); + } else { + prusti_errors.push(prusti_error); + } + } + *overall_result = VerificationResult::Failure; + } + viper::VerificationResultKind::JavaException(exception) => { + error!("Java exception: {}", exception.get_stack_trace()); + PrustiError::internal( + format!("in {program_name}: {exception}"), + DUMMY_SP.into(), + ) + .emit(&self.env.diagnostic); + *overall_result = VerificationResult::Failure; + } + } + } + + fn handle_quantifier_instantiation_message( + &self, + program_name: String, + q_name: String, + insts: u64, + pos_id: u64, + quantifier_instantiations: &mut FxHashMap<(u64, String), FxHashMap> + ) { + if config::report_viper_messages() { + match self.encoder.error_manager().position_manager().get_span_from_id(pos_id) { + Some(span) => { + let key = (pos_id, program_name.clone()); + if !quantifier_instantiations.contains_key(&key) { + quantifier_instantiations.insert(key.clone(), FxHashMap::default()); + } + let map = quantifier_instantiations.get_mut(&key).unwrap(); + // this replaces the old entry which is exactly what we want + map.insert(q_name, insts); + let mut n: u64 = 0; + for insts in map.values() { + n += *insts; + } + PrustiError::message( + format!("quantifierInstantiationsMessage{}", + json!({"instantiations": n, "method": program_name}), + ), span.clone() + ).emit(&self.env.diagnostic); + }, + None => error!("#{insts} quantifier instantiations of {q_name} for unknown position id {pos_id} in verification of {program_name}"), + } + } + } + + fn handle_quantifier_chosen_triggers_message( + &self, + program_name: String, + viper_quant: String, + triggers: String, + pos_id: u64 + ) { + if config::report_viper_messages() && pos_id != 0 { + match self.encoder.error_manager().position_manager().get_span_from_id(pos_id) { + Some(span) => { + PrustiError::message( + format!("quantifierChosenTriggersMessage{}", + json!({"viper_quant": viper_quant, "triggers": triggers}), + ), span.clone() + ).emit(&self.env.diagnostic); + }, + None => error!("Invalid position id {pos_id} for viper quantifier {viper_quant} in verification of {program_name}"), + } + } + } + + /// Returns a list of (program_name, verification_requests) tuples. fn programs_to_requests(&self, programs: Vec) -> Vec<(String, VerificationRequest)> { let source_path = self.env.name.source_path(); let rust_program_name = source_path @@ -359,7 +385,6 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { verification_requests.collect() } - /// Returns a list of (program_name, verification_requests) tuples. fn verify_requests_server( verification_requests: Vec<(String, VerificationRequest)>, server_address: String, diff --git a/viper/src/verification_result.rs b/viper/src/verification_result.rs index d27d41b5aaf..3d9087f971c 100644 --- a/viper/src/verification_result.rs +++ b/viper/src/verification_result.rs @@ -9,19 +9,20 @@ use crate::{silicon_counterexample::SiliconCounterexample, JavaException}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub struct VerificationResult { pub item_name: String, - pub result_type: VerificationResultKind, + pub kind: VerificationResultKind, pub cached: bool, pub time_ms: u128, } impl VerificationResult { pub fn is_success(&self) -> bool { - matches!(self.result_type, VerificationResultKind::Success) + self.kind.is_success() } + pub fn dummy_success() -> Self { VerificationResult { item_name: "".to_string(), - result_type: VerificationResultKind::Success, + kind: VerificationResultKind::Success, cached: false, time_ms: 0, } From a492472f35933f6c3504342b99b43f3862080989 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sun, 5 Mar 2023 01:26:10 +0100 Subject: [PATCH 65/74] Fix Z3 arguments --- prusti-server/src/verification_request.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/prusti-server/src/verification_request.rs b/prusti-server/src/verification_request.rs index 8acc0a71745..4b3d3ba56b9 100644 --- a/prusti-server/src/verification_request.rs +++ b/prusti-server/src/verification_request.rs @@ -52,25 +52,23 @@ impl ViperBackendConfig { verifier_args.extend(vec![ "--assertTimeout".to_string(), config::assert_timeout().to_string(), - ]); - let mut prover_args = vec![ "--proverConfigArgs".to_string(), - // model.partial changes the default case of functions in counterexamples - // to #unspecified - format!( + ]); + // model.partial changes the default case of functions in counterexamples + // to #unspecified + let mut prover_args = format!( "smt.qi.eager_threshold={} model.partial={}", config::smt_qi_eager_threshold(), - config::counterexample(), - ), - ]; + config::counterexample() + ); if let Some(smt_qi_profile) = config::smt_qi_profile() { - prover_args.push(format!("smt.qi.profile={}", smt_qi_profile)); + prover_args = format!("{prover_args} smt.qi.profile={smt_qi_profile}"); } if let Some(smt_qi_profile_freq) = config::smt_qi_profile_freq() { - prover_args.push(format!("smt.qi.profile_freq={}", smt_qi_profile_freq)); + prover_args = format!("{prover_args} smt.qi.profile_freq={smt_qi_profile_freq}"); } - verifier_args.extend(prover_args); + verifier_args.push(prover_args); verifier_args.extend(vec!["--logLevel".to_string(), "ERROR".to_string()]); From f50a36734094e0f4bd155b8c22773bcec2d2886e Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Sun, 5 Mar 2023 08:42:53 +0100 Subject: [PATCH 66/74] Fix test and formatting error --- prusti-server/src/verification_request.rs | 11 ++++++----- prusti-server/tests/basic_requests.rs | 6 ++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/prusti-server/src/verification_request.rs b/prusti-server/src/verification_request.rs index 4b3d3ba56b9..a86ceee26ad 100644 --- a/prusti-server/src/verification_request.rs +++ b/prusti-server/src/verification_request.rs @@ -57,16 +57,17 @@ impl ViperBackendConfig { // model.partial changes the default case of functions in counterexamples // to #unspecified let mut prover_args = format!( - "smt.qi.eager_threshold={} model.partial={}", - config::smt_qi_eager_threshold(), - config::counterexample() - ); + "smt.qi.eager_threshold={} model.partial={}", + config::smt_qi_eager_threshold(), + config::counterexample() + ); if let Some(smt_qi_profile) = config::smt_qi_profile() { prover_args = format!("{prover_args} smt.qi.profile={smt_qi_profile}"); } if let Some(smt_qi_profile_freq) = config::smt_qi_profile_freq() { - prover_args = format!("{prover_args} smt.qi.profile_freq={smt_qi_profile_freq}"); + prover_args = + format!("{prover_args} smt.qi.profile_freq={smt_qi_profile_freq}"); } verifier_args.push(prover_args); diff --git a/prusti-server/tests/basic_requests.rs b/prusti-server/tests/basic_requests.rs index 563f0701d47..8a92b8adcd4 100644 --- a/prusti-server/tests/basic_requests.rs +++ b/prusti-server/tests/basic_requests.rs @@ -66,12 +66,14 @@ where .expect("failed to construct Tokio runtime") .block_on(async { PrustiClient::verify(SERVER_ADDRESS.clone(), request) + .await .collect::>() .await - .find_map(|&&m| match m { + .into_iter() + .find_map(|m| match m { ServerMessage::Termination(res) => Some(res.kind), _ => None, }) - .unwrap_or_else(VerificationResultKind::Failure(vec![])) + .unwrap_or_else(|| VerificationResultKind::Failure(vec![])) }) } From a1e6d9fab217d89c453bc2b5aee22f4b0a63ce48 Mon Sep 17 00:00:00 2001 From: cedric Date: Sun, 5 Mar 2023 14:28:46 +0100 Subject: [PATCH 67/74] Refactor / Fix according to code reviews --- docs/dev-guide/src/config/flags.md | 12 ++-- prusti-interface/src/prusti_error.rs | 24 +++---- prusti-utils/src/config.rs | 8 +-- prusti-viper/src/encoder/procedure_encoder.rs | 68 ++++++++----------- prusti-viper/src/ide/encoding_info.rs | 17 +++-- .../src/ide/ide_verification_result.rs | 14 ++-- prusti-viper/src/ide/vsc_span.rs | 37 ++++------ prusti-viper/src/verifier.rs | 2 +- prusti/src/callbacks.rs | 18 +++-- prusti/src/ide_helper/call_finder.rs | 2 +- prusti/src/ide_helper/compiler_info.rs | 35 +++------- 11 files changed, 105 insertions(+), 132 deletions(-) diff --git a/docs/dev-guide/src/config/flags.md b/docs/dev-guide/src/config/flags.md index 2390b2212ab..eb98e0910ee 100644 --- a/docs/dev-guide/src/config/flags.md +++ b/docs/dev-guide/src/config/flags.md @@ -57,7 +57,7 @@ | [`QUERY_METHOD_SIGNATURE`](#query_method_signature) | `Option` | `None` | A | | [`QUIET`](#quiet) | `bool` | `false` | A* | | [`REPORT_VIPER_MESSAGES`](#report_viper_messages) | `bool` | `false` | A | -| [`SELECTIVE_VERIFY`](#selective_verify) | `Option` | `None` | A | +| [`VERIFY_ONLY_DEFPATH`](#verify_only_defpath) | `Option` | `None` | A | | [`SERVER_ADDRESS`](#server_address) | `Option` | `None` | A | | [`SERVER_MAX_CONCURRENCY`](#server_max_concurrency) | `Option` | `None` | A | | [`SERVER_MAX_STORED_VERIFIERS`](#server_max_stored_verifiers) | `Option` | `None` | A | @@ -353,7 +353,7 @@ When enabled, prints the type-checked specifications. ## `QUERY_METHOD_SIGNATURE` -When enabled, @cedihegi I don't know what exactly happens :) +When set to a defpath, prusti will generate a template for an external specification for this method. The result is part of the CompilerInfo and will only be emitted if the `SHOW_IDE_INFO` flag is enabled too. ## `QUIET` @@ -365,9 +365,9 @@ When enabled, user messages are not printed. Otherwise, messages output into `st When enabled for both server and client, certain supported Viper messages will be reported to the user. -## `SELECTIVE_VERIFY` +## `VERIFY_ONLY_DEFPATH` -When enabled, @cedihegi I don't know what exactly happens :) +When set to the defpath of a local method, prusti will only verify the specified method. A fake error will be generated to avoid caching of a success. ## `SERVER_ADDRESS` @@ -387,7 +387,7 @@ Maximum amount of instantiated Viper verifiers the server will keep around for r ## `SHOW_IDE_INFO` -When enabled, @cedihegi I don't know what exactly happens :) +When enabled, we emit various json data structures containing information about the program, its encoding, and the results of the verification. This flag intended for prusti-assistant (IDE). ## `SIMPLIFY_ENCODING` @@ -399,7 +399,7 @@ When enabled, features not supported by Prusti will be reported as warnings rath ## `SKIP_VERIFICATION` -When enabled, @cedihegi I don't know what exactly happens :) +When enabled, verification will be skipped. Opposed to `NO_VERIFY`, this flag will cause fake errors to stop the compiler from caching the result. ## `SMT_QI_BOUND_GLOBAL` diff --git a/prusti-interface/src/prusti_error.rs b/prusti-interface/src/prusti_error.rs index 4596a11e137..21c9306d618 100644 --- a/prusti-interface/src/prusti_error.rs +++ b/prusti-interface/src/prusti_error.rs @@ -20,7 +20,7 @@ use prusti_rustc_interface::{errors::MultiSpan, span::Span}; /// `SpannedEncodingError` and similar types to something less confusing.) #[derive(Clone, Debug, PartialEq, Eq)] pub struct PrustiError { - kind: PrustiErrorKind, + kind: PrustiDiagnosticKind, /// If `true`, it should not be reported to the user. We need this in cases /// when the same error could be reported twice. /// @@ -35,7 +35,7 @@ pub struct PrustiError { } /// Determines how a `PrustiError` is reported. #[derive(Clone, Debug, PartialEq, Eq)] -pub enum PrustiErrorKind { +pub enum PrustiDiagnosticKind { Error, Warning, /// A warning which is only shown if at least one error is emitted. @@ -61,7 +61,7 @@ impl PrustiError { /// Private constructor. Use one of the following methods. fn new(message: String, span: MultiSpan) -> Self { PrustiError { - kind: PrustiErrorKind::Error, + kind: PrustiDiagnosticKind::Error, is_disabled: false, message, span: Box::new(span), @@ -115,7 +115,7 @@ impl PrustiError { pub fn warning(message: S, span: MultiSpan) -> Self { check_message(message.to_string()); let mut err = PrustiError::new(format!("[Prusti: warning] {}", message.to_string()), span); - err.kind = PrustiErrorKind::Warning; + err.kind = PrustiDiagnosticKind::Warning; err } @@ -124,7 +124,7 @@ impl PrustiError { pub fn warning_on_error(message: S, span: MultiSpan) -> Self { check_message(message.to_string()); let mut err = PrustiError::new(format!("[Prusti: warning] {}", message.to_string()), span); - err.kind = PrustiErrorKind::WarningOnError; + err.kind = PrustiDiagnosticKind::WarningOnError; err } @@ -151,17 +151,17 @@ impl PrustiError { pub fn message(message: S, span: MultiSpan) -> Self { check_message(message.to_string()); let mut msg = PrustiError::new(message.to_string(), span); - msg.kind = PrustiErrorKind::Message; + msg.kind = PrustiDiagnosticKind::Message; msg } /// Set that this Prusti error should be reported as a warning to the user pub fn set_warning(&mut self) { - self.kind = PrustiErrorKind::Warning; + self.kind = PrustiDiagnosticKind::Warning; } pub fn is_error(&self) -> bool { - matches!(self.kind, PrustiErrorKind::Error) + matches!(self.kind, PrustiDiagnosticKind::Error) } // FIXME: This flag is a temporary workaround for having duplicate errors @@ -194,25 +194,25 @@ impl PrustiError { pub fn emit(self, env_diagnostic: &EnvDiagnostic) { assert!(!self.is_disabled); match self.kind { - PrustiErrorKind::Error => env_diagnostic.span_err_with_help_and_notes( + PrustiDiagnosticKind::Error => env_diagnostic.span_err_with_help_and_notes( *self.span, &self.message, &self.help, &self.notes, ), - PrustiErrorKind::Warning => env_diagnostic.span_warn_with_help_and_notes( + PrustiDiagnosticKind::Warning => env_diagnostic.span_warn_with_help_and_notes( *self.span, &self.message, &self.help, &self.notes, ), - PrustiErrorKind::WarningOnError => env_diagnostic.span_warn_on_err_with_help_and_notes( + PrustiDiagnosticKind::WarningOnError => env_diagnostic.span_warn_on_err_with_help_and_notes( *self.span, &self.message, &self.help, &self.notes, ), - PrustiErrorKind::Message => env_diagnostic.span_note(*self.span, &self.message), + PrustiDiagnosticKind::Message => env_diagnostic.span_note(*self.span, &self.message), }; } diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index cbe558af476..f6366a3d53a 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -173,7 +173,7 @@ lazy_static::lazy_static! { // Flags specifically for Prusti-Assistant: settings.set_default("show_ide_info", false).unwrap(); settings.set_default("skip_verification", false).unwrap(); - settings.set_default::>("selective_verify", None).unwrap(); + settings.set_default::>("verify_only_defpath", None).unwrap(); settings.set_default::>("query_method_signature", None).unwrap(); @@ -1059,11 +1059,11 @@ pub fn skip_verification() -> bool { /// Used for selective verification, can be passed a String containing /// the DefPath of the method to be verified -pub fn selective_verify() -> Option { - read_setting("selective_verify") +pub fn verify_only_defpath() -> Option { + read_setting("verify_only_defpath") } -/// A flag that can be used to ask the compiler for the declaration / +/// A flag that can be used to ask the compiler for the declaration / /// signature of a method, used to automatically generate a skeleton /// for an external specification pub fn query_method_signature() -> Option { diff --git a/prusti-viper/src/encoder/procedure_encoder.rs b/prusti-viper/src/encoder/procedure_encoder.rs index dcd859be860..19d0635ac49 100644 --- a/prusti-viper/src/encoder/procedure_encoder.rs +++ b/prusti-viper/src/encoder/procedure_encoder.rs @@ -3388,13 +3388,15 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { substs, ).with_span(call_site_span)? }; - // store spans of items of the contracts for prusti-assistant - self.store_contract_spans( - called_def_id, - &procedure_contract, - call_site_span, - substs, - ); + if config::show_ide_info() { + // store spans of items of the contracts for prusti-assistant + self.store_contract_spans( + called_def_id, + &procedure_contract, + call_site_span, + substs, + ); + } assert_one_magic_wand(procedure_contract.borrow_infos.len()).with_span(call_site_span)?; @@ -3537,9 +3539,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { Ok(stmts) } - /// Collect all the available spans of the items of a contract + /// Collect all the available spans of the items of a contract /// and store them. Purpose of this function is to later pass these - /// spans to prusti-assistant, so users can look up contracts of + /// spans to prusti-assistant, so users can look up contracts of /// function calls fn store_contract_spans( &self, @@ -3548,25 +3550,21 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { call_site_span: Span, substs: ty::subst::SubstsRef<'tcx>, ) { - let mut precondition_spans:Vec = contract - .functional_precondition(self.encoder.env(), substs) - .iter() - .map(|(ts,_)| self.encoder.env().query.get_def_span(ts)) - .collect(); - let mut postcondition_spans: Vec = contract - .functional_postcondition(self.encoder.env(), substs) + + let mut pre_post = contract.functional_precondition(self.encoder.env(), substs); + pre_post.append(&mut contract.functional_postcondition(self.encoder.env(), substs)); + let mut contracts_def_ids: Vec = pre_post.iter().map(|(ts, _)| *ts).collect(); + + if let Some(Some(purity_defid)) = contract.specification.purity.extract_with_selective_replacement() { + contracts_def_ids.push(*purity_defid); + } + if let Some(Some(term_local_defid)) = contract.specification.terminates.extract_with_selective_replacement() { + contracts_def_ids.push(term_local_defid.to_def_id()); + } + let mut result = contracts_def_ids .iter() - .map(|(ts,_)| self.encoder.env().query.get_def_span(ts)) - .collect(); - let purity: Option = contract - .specification.purity - .extract_with_selective_replacement() - .copied().unwrap_or(None) - .map(|id| self.encoder.env().query.get_def_span(id)); - let terminates: Option = contract.specification.terminates - .extract_with_selective_replacement() - .copied().unwrap_or(None) - .map(|id| self.encoder.env().query.get_def_span(id)); + .map(|id| self.encoder.env().query.get_def_span(*id)) + .collect::>(); // for each pledge, if it has a lefthandside, join the 2, otherwise // just return span of the righthandside @@ -3581,27 +3579,15 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { } }) .collect(); - let mut result = vec![]; - result.append(&mut precondition_spans); - result.append(&mut postcondition_spans); result.append(&mut pledges); - if let Some(purity) = purity { - result.push(purity); - } - if let Some(terminates) = terminates { - result.push(terminates); - } - // let mut pure_opt = contract. let tcx = self.encoder.env().tcx(); - let contract_spans_opt = SpanOfCallContracts::new( + let contract_spans = SpanOfCallContracts::new( tcx.def_path_str(called_def_id), call_site_span, result, tcx.sess.source_map(), ); - if let Some(contract_spans) = contract_spans_opt { - self.encoder.spans_of_call_contracts.borrow_mut().push(contract_spans); - } + self.encoder.spans_of_call_contracts.borrow_mut().push(contract_spans); } #[allow(clippy::too_many_arguments)] diff --git a/prusti-viper/src/ide/encoding_info.rs b/prusti-viper/src/ide/encoding_info.rs index 386a0ad94c6..e5b0e85e61a 100644 --- a/prusti-viper/src/ide/encoding_info.rs +++ b/prusti-viper/src/ide/encoding_info.rs @@ -2,12 +2,15 @@ use prusti_rustc_interface::span::{source_map::SourceMap, Span}; use serde::Serialize; use super::vsc_span::VscSpan; -/// stores the spans of a calls contracts. -/// obtained during encoding +/// Represents the locations of specifications of a function call. +/// Generated for each encoded function call to be used by prusti-assistant. #[derive(Serialize, Clone)] pub struct SpanOfCallContracts { + /// the defpath of the method that is called pub defpath: String, + /// the span where this method is called pub call_span: VscSpan, + /// the spans of all the specifications of the called method pub contracts_spans: Vec, } @@ -17,17 +20,17 @@ impl SpanOfCallContracts { call_span: Span, contracts_spans: Vec, source_map: &SourceMap - ) -> Option { - let call_span = VscSpan::from_span(&call_span, source_map)?; + ) -> Self { + let call_span = VscSpan::from_span(&call_span, source_map); let contracts_spans = contracts_spans .iter() - .filter_map(|sp| VscSpan::from_span(sp, source_map)) + .map(|sp| VscSpan::from_span(sp, source_map)) .collect::>(); - Some(Self { + Self { defpath, call_span, contracts_spans, - }) + } } } diff --git a/prusti-viper/src/ide/ide_verification_result.rs b/prusti-viper/src/ide/ide_verification_result.rs index 1c315156bf7..378dc0a9bd4 100644 --- a/prusti-viper/src/ide/ide_verification_result.rs +++ b/prusti-viper/src/ide/ide_verification_result.rs @@ -1,18 +1,24 @@ use serde::Serialize; use viper::VerificationResult; -// since the existing verification result -// is not as trivially passed in json +/// Generated for each verification item, containing information +/// about the result of the verification. This information will be emitted +/// if the show_ide_info flag is set, and it's purpose is to be +/// consumed by prusti-assistant. #[derive(Serialize)] pub struct IdeVerificationResult { + /// the name / defpath of the method item_name: String, + /// whether the verification of that method was successful success: bool, + /// how long the verification took time_ms: u128, + /// whether this result was cached or is fresh cached: bool, } -impl IdeVerificationResult { - pub fn from_res(res: &VerificationResult) -> Self { +impl From<&VerificationResult> for IdeVerificationResult { + fn from(res: &VerificationResult) -> Self { Self { item_name: res.item_name.clone(), success: res.is_success(), diff --git a/prusti-viper/src/ide/vsc_span.rs b/prusti-viper/src/ide/vsc_span.rs index 0ae88513354..e452e74bec1 100644 --- a/prusti-viper/src/ide/vsc_span.rs +++ b/prusti-viper/src/ide/vsc_span.rs @@ -10,32 +10,25 @@ pub struct VscSpan { line_start: usize, file_name: String, is_primary: bool, - label: Option<()>, - expansion: Option<()>, } impl VscSpan { - pub fn from_span(sp: &Span, sourcemap: &SourceMap) -> Option { - let filename = sourcemap.span_to_filename(*sp); - let diag_filename = sourcemap.filename_for_diagnostics(&filename); - let fname = format!("{diag_filename}"); + pub fn from_span(sp: &Span, sourcemap: &SourceMap) -> Self { + let span_filename = sourcemap.span_to_filename(*sp); + let diag_filename = sourcemap.filename_for_diagnostics(&span_filename); + let file_name = format!("{diag_filename}"); - if let Ok((l1, l2)) = sourcemap.is_valid_span(*sp) { - Some(Self { - column_start: l1.col.0, - column_end: l2.col.0, - line_start: l1.line, - line_end: l2.line, - file_name: fname, - // the following 3 are not relevant here, we just want to be - // able to reuse the existing methods and the parser - // for spans in VSCode - is_primary: false, - label: None, - expansion: None, - }) - } else { - None + let (l1, l2) = sourcemap.is_valid_span(*sp).expect("Invalid span"); + Self { + column_start: l1.col.0, + column_end: l2.col.0, + line_start: l1.line, + line_end: l2.line, + file_name, + // the following one is not relevant here, we just want to be + // able to reuse the existing methods and the parser + // for spans in VSCode + is_primary: false, } } } diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index 5ae1d379aad..77fcee7fe30 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -196,7 +196,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { PrustiError::message( format!( "ideVerificationResult{}", - serde_json::to_string(&IdeVerificationResult::from_res(&result)) + serde_json::to_string(&IdeVerificationResult::from(&result)) .unwrap() ), DUMMY_SP.into(), diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 558c8e9b217..bb30bb792cc 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -147,14 +147,7 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // collect and output Information used by IDE: if !config::no_verify() && !config::skip_verification() { - if config::selective_verify().is_none() { - let verification_task = VerificationTask { - procedures: annotated_procedures, - types, - }; - verify(&env, def_spec, verification_task); - } else { - let target_def_path = config::selective_verify().unwrap(); + if let Some(target_def_path) = config::verify_only_defpath() { let procedures = annotated_procedures .into_iter() .filter(|x| env.name.get_unique_item_name(*x) == target_def_path) @@ -165,9 +158,14 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { // to be cached by compiler at the moment verify(&env, def_spec, selective_task); fake_error(&env); + } else { + let verification_task = VerificationTask { + procedures: annotated_procedures, + types, + }; + verify(&env, def_spec, verification_task); } - } else if config::show_ide_info() && config::skip_verification() && !config::no_verify() - { + } else if config::skip_verification() && !config::no_verify() { // add a fake error, reason explained in issue #1261 fake_error(&env) } diff --git a/prusti/src/ide_helper/call_finder.rs b/prusti/src/ide_helper/call_finder.rs index 266ce4a75e8..3f65a810951 100644 --- a/prusti/src/ide_helper/call_finder.rs +++ b/prusti/src/ide_helper/call_finder.rs @@ -88,7 +88,7 @@ impl<'tcx> Visitor<'tcx> for CallSpanFinder<'tcx> { } ExprKind::Binary(..) | ExprKind::AssignOp(..) | ExprKind::Unary(..) => { let resolve_res = self.resolve_expression(expr); - // this will already fail for standard addition + // this will already fail for builtin-operations if let Ok((method_def_id, resolved_def_id)) = resolve_res { let defpath_unresolved = self.tcx.def_path_str(method_def_id); let defpath_resolved = self.tcx.def_path_str(resolved_def_id); diff --git a/prusti/src/ide_helper/compiler_info.rs b/prusti/src/ide_helper/compiler_info.rs index a4079dc4503..c3bc5bb3804 100644 --- a/prusti/src/ide_helper/compiler_info.rs +++ b/prusti/src/ide_helper/compiler_info.rs @@ -5,7 +5,7 @@ use prusti_rustc_interface::{ span::{source_map::SourceMap, Span}, }; use prusti_viper::ide::vsc_span::VscSpan; -use serde::{ser::SerializeStruct, Serialize}; +use serde::Serialize; /// This struct will be passed to prusti-assistant containing information /// about the program that is currently being verified @@ -14,9 +14,6 @@ pub struct IdeInfo { procedure_defs: Vec, function_calls: Vec, queried_source: Option, - // additionally this will contain: - // function_calls: - // ... we'll see } impl IdeInfo { @@ -25,22 +22,22 @@ impl IdeInfo { procedures: &Vec, def_spec: &typed::DefSpecificationMap, ) -> Self { - let procs = collect_procedures(env, procedures, def_spec); + let procedure_defs = collect_procedures(env, procedures, def_spec); let source_map = env.tcx().sess.source_map(); - let fncalls: Vec = collect_fncalls(env) + let function_calls = collect_fncalls(env) .into_iter() .map(|(name, defid, sp)| ProcDef { name, defid, - span: VscSpan::from_span(&sp, source_map).unwrap(), + span: VscSpan::from_span(&sp, source_map), }) - .collect(); + .collect::>(); // For declaring external specifications: - let queried_source = query_signature::collect_queried_signature(env.tcx(), &fncalls); + let queried_source = query_signature::collect_queried_signature(env.tcx(), &function_calls); Self { - procedure_defs: procs, - function_calls: fncalls, + procedure_defs, + function_calls, queried_source, } } @@ -49,24 +46,14 @@ impl IdeInfo { /// A struct that contains either a reference to a procedure that can be verified /// (for selective verification) or a function call (so a user can query /// external_spec blocks for it). The name contains the defpath. +#[derive(Serialize)] pub struct ProcDef { pub name: String, + #[serde(skip)] pub defid: DefId, pub span: VscSpan, } -impl Serialize for ProcDef { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("ProcDef", 2)?; - state.serialize_field("name", &self.name)?; - state.serialize_field("span", &self.span)?; - state.end() - } -} - /// collect information about the program that will be passed to IDE. /// This should find all non-trusted functions that can be verified fn collect_procedures( @@ -79,7 +66,7 @@ fn collect_procedures( for defid in procedures { let defpath = env.name.get_unique_item_name(*defid); let span = env.query.get_def_span(defid); - let vscspan = VscSpan::from_span(&span, sourcemap).unwrap(); + let vscspan = VscSpan::from_span(&span, sourcemap); // Filter out the predicates and trusted methods, // since we don't want to allow selective verification From b1448436a5b41355fee43989c3bfba00dc7a65ec Mon Sep 17 00:00:00 2001 From: cedric Date: Sun, 5 Mar 2023 15:09:33 +0100 Subject: [PATCH 68/74] Format and move old comment to right location --- prusti-interface/src/prusti_error.rs | 13 +++++++------ prusti-utils/src/config.rs | 6 ++++-- prusti/src/callbacks.rs | 3 ++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/prusti-interface/src/prusti_error.rs b/prusti-interface/src/prusti_error.rs index 21c9306d618..e69c2e8c8a7 100644 --- a/prusti-interface/src/prusti_error.rs +++ b/prusti-interface/src/prusti_error.rs @@ -206,12 +206,13 @@ impl PrustiError { &self.help, &self.notes, ), - PrustiDiagnosticKind::WarningOnError => env_diagnostic.span_warn_on_err_with_help_and_notes( - *self.span, - &self.message, - &self.help, - &self.notes, - ), + PrustiDiagnosticKind::WarningOnError => env_diagnostic + .span_warn_on_err_with_help_and_notes( + *self.span, + &self.message, + &self.help, + &self.notes, + ), PrustiDiagnosticKind::Message => env_diagnostic.span_note(*self.span, &self.message), }; } diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index f6366a3d53a..d02d7f2bd0b 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -1051,8 +1051,10 @@ pub fn show_ide_info() -> bool { read_setting("show_ide_info") } -/// Very similar to no_verify but should only be used by prusti-assistant -/// to avoid interference +/// When enabled, verification is skipped. Similar to no_verify but needed +/// because no_verify is also set automatically for dependencies, independent +/// of whether the user passed this flag. In general only required because +/// of issue #1261 pub fn skip_verification() -> bool { read_setting("skip_verification") } diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index bb30bb792cc..cb3ae9cee36 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -134,7 +134,8 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { } CrossCrateSpecs::import_export_cross_crate(&mut env, &mut def_spec); - // determine procedures that have to be verified. + // TODO: can we replace `get_annotated_procedures` with information + // that is already in `def_spec`? let (annotated_procedures, types) = env.get_annotated_procedures_and_types(); if config::show_ide_info() && !config::no_verify() { From aaa3ea579ec2f01babcd67fea142570a1189b2e1 Mon Sep 17 00:00:00 2001 From: cedric Date: Sun, 5 Mar 2023 15:21:02 +0100 Subject: [PATCH 69/74] Address reviews and add comments --- prusti/src/ide_helper/query_signature.rs | 9 +++++---- prusti/src/verifier.rs | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index e8601368253..99dc1dd3fdb 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -7,8 +7,10 @@ use prusti_rustc_interface::{ middle::ty::{self, Clause, DefIdTree, ImplSubject, PredicateKind, TyCtxt}, }; -/// data structure to represent the code we want to generate -/// since we didnt manage to this with syn etc.. +/// Data structure used to generate an external specification template. +/// The main purpose of this struct is to strictly split the collection +/// of all this information from the displaying afterwards. +/// Allows users in prusti-assistant to query such a template for function calls. #[derive(Debug)] enum ExternSpecBlock { StandaloneFn { @@ -44,8 +46,7 @@ impl ExternSpecBlock { } DefKind::AssocFn => { // this will be None for traits - let impl_defid_opt = tcx.impl_of_method(defid); - match impl_defid_opt { + match tcx.impl_of_method(defid) { Some(impl_defid) => { // function is part of impl block let mut trait_name = None; diff --git a/prusti/src/verifier.rs b/prusti/src/verifier.rs index 8422a065e3f..d9d71452a7e 100644 --- a/prusti/src/verifier.rs +++ b/prusti/src/verifier.rs @@ -20,8 +20,6 @@ pub fn verify<'tcx>( warn!("The compiler reported an error, so the program will not be verified."); } else { debug!("Prepare verification task..."); - // TODO: can we replace `get_annotated_procedures` with information - // that is already in `def_spec`? debug!("Verification task: {:?}", &verification_task); user::message(format!( From 1f0ae076fcd47502d415f26208485cd2c4990b52 Mon Sep 17 00:00:00 2001 From: hegglinc Date: Tue, 7 Mar 2023 20:38:04 +0100 Subject: [PATCH 70/74] Improve displaying of generated extern spec block for trait --- prusti/src/ide_helper/query_signature.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 99dc1dd3fdb..988d3b5898e 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -130,7 +130,7 @@ impl fmt::Display for ExternSpecBlock { // do traits specify traitbounds too? writeln!(f, "#[extern_spec({path})]")?; writeln!(f, "trait {name}{generics_str} {where_block}{{")?; - writeln!(f, "{fn_sig}}}") + writeln!(f, "{fn_sig}\n}}") } ExternSpecBlock::StandaloneFn { parent_chain, From e562bca1b281bb6edfcc7be5f21d450e195e1593 Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Tue, 7 Mar 2023 23:13:01 +0100 Subject: [PATCH 71/74] Fix some quantifier reporting weirdness Also add some debug statements. --- prusti-viper/src/verifier.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index e5906aa7eaf..d45051f24e6 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -187,6 +187,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { prusti_errors: &mut Vec, overall_result: &mut VerificationResult ) { + debug!("Received termination message with result {result:?} in verification of {program_name}"); if config::show_ide_info() { PrustiError::message( format!( @@ -305,6 +306,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { quantifier_instantiations: &mut FxHashMap<(u64, String), FxHashMap> ) { if config::report_viper_messages() { + debug!("Received #{insts} quantifier instantiations of {q_name} for position id {pos_id} in verification of {program_name}"); match self.encoder.error_manager().position_manager().get_span_from_id(pos_id) { Some(span) => { let key = (pos_id, program_name.clone()); @@ -312,10 +314,18 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { quantifier_instantiations.insert(key.clone(), FxHashMap::default()); } let map = quantifier_instantiations.get_mut(&key).unwrap(); - // this replaces the old entry which is exactly what we want - map.insert(q_name, insts); + // for some reason, the aux quantifiers by the same z3 instance (same uniqueId + // in silicon) can have different amount of instantiations. + // e.g. we receive a message with 100 instantiations for a certain quantifier + // and afterwards a message with 20 instantiations for the same one. + // All verifying the same viper program and by the same z3 instance. + // Since I don't see a better way to take this into account than taking the + // maximum, that is exactly what we do here. + let old_inst = map.get(&q_name).unwrap_or(&0); + map.insert(q_name, std::cmp::max(insts, *old_inst)); let mut n: u64 = 0; - for insts in map.values() { + for (q_name, insts) in map.iter() { + debug!("Key: {q_name}, Value: {insts}"); n += *insts; } PrustiError::message( @@ -337,6 +347,7 @@ impl<'v, 'tcx> Verifier<'v, 'tcx> { pos_id: u64 ) { if config::report_viper_messages() && pos_id != 0 { + debug!("Received quantifier triggers {triggers} for quantifier {viper_quant} for position id {pos_id} in verification of {program_name}"); match self.encoder.error_manager().position_manager().get_span_from_id(pos_id) { Some(span) => { PrustiError::message( From 65c99d9e5c39a84eeda958d7998054a552c6174d Mon Sep 17 00:00:00 2001 From: Joseph Thommes Date: Wed, 8 Mar 2023 18:15:53 +0100 Subject: [PATCH 72/74] Make QI etc. also work for existential quantifiers --- .../mir/pure/specifications/encoder_poly.rs | 9 ++++++++- vir/defs/polymorphic/ast/expr.rs | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs index 4694cb3a318..2368165bca1 100644 --- a/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs +++ b/prusti-viper/src/encoder/mir/pure/specifications/encoder_poly.rs @@ -278,10 +278,17 @@ pub(super) fn encode_quantifier<'tcx>( }; if is_exists { - Ok(vir_crate::polymorphic::Expr::exists( + Ok(vir_crate::polymorphic::Expr::exists_with_pos( fixed_qvars, encoded_trigger_sets, final_body, + // we encode the position ID in the line number because that is used to name the + // quantifiers in z3 + vir_crate::polymorphic::Position::new( + i32::try_from(pos.id()).unwrap(), + i32::try_from(pos.id()).unwrap(), + pos.id(), + ), )) } else { Ok(vir_crate::polymorphic::Expr::forall_with_pos( diff --git a/vir/defs/polymorphic/ast/expr.rs b/vir/defs/polymorphic/ast/expr.rs index 0981c0a8199..1012a216621 100644 --- a/vir/defs/polymorphic/ast/expr.rs +++ b/vir/defs/polymorphic/ast/expr.rs @@ -383,6 +383,24 @@ impl Expr { }) } + pub fn exists_with_pos( + vars: Vec, + triggers: Vec, + body: Expr, + pos: Position, + ) -> Self { + assert!( + !vars.is_empty(), + "A quantifier must have at least one variable." + ); + Expr::Exists(Exists { + variables: vars, + triggers, + body: Box::new(body), + position: pos, + }) + } + pub fn ite(guard: Expr, left: Expr, right: Expr) -> Self { Expr::Cond(Cond { guard: Box::new(guard), From a299c144e1d1515feef51d20100159dfe258662e Mon Sep 17 00:00:00 2001 From: hegglinc Date: Thu, 9 Mar 2023 19:30:20 +0100 Subject: [PATCH 73/74] Check if current package is primary package before throwing fake_error In 2 places we throw fake errors to avoid caching of successes when we should not. This lead to problems with local dependencies, since they are verified too and the fake_error ended up being thrown too early. This bug is resolved now. --- prusti/src/callbacks.rs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 53873582a4f..ddb43206df5 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -148,20 +148,28 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { PrustiError::message(format!("compilerInfo{out}"), DUMMY_SP.into()) .emit(&env.diagnostic); } + // as long as we have to throw a fake error we need to check this + let is_primary_package = std::env::var("CARGO_PRIMARY_PACKAGE").is_ok(); // collect and output Information used by IDE: if !config::no_verify() && !config::skip_verification() { if let Some(target_def_path) = config::verify_only_defpath() { - let procedures = annotated_procedures - .into_iter() - .filter(|x| env.name.get_unique_item_name(*x) == target_def_path) - .collect(); - let selective_task = VerificationTask { procedures, types }; - // fake_error because otherwise a verification-success - // (for a single method for example) will cause this result - // to be cached by compiler at the moment - verify(&env, def_spec, selective_task); - fake_error(&env); + // if we do selective verification, then definitely only + // for methods of the primary package. Check needed because + // of the fake_error, otherwise verification stops early + // with local dependencies + if is_primary_package { + let procedures = annotated_procedures + .into_iter() + .filter(|x| env.name.get_unique_item_name(*x) == target_def_path) + .collect(); + let selective_task = VerificationTask { procedures, types }; + // fake_error because otherwise a verification-success + // (for a single method for example) will cause this result + // to be cached by compiler at the moment + verify(&env, def_spec, selective_task); + fake_error(&env); + } } else { let verification_task = VerificationTask { procedures: annotated_procedures, @@ -169,9 +177,9 @@ impl prusti_rustc_interface::driver::Callbacks for PrustiCompilerCalls { }; verify(&env, def_spec, verification_task); } - } else if config::skip_verification() && !config::no_verify() { + } else if config::skip_verification() && !config::no_verify() && is_primary_package { // add a fake error, reason explained in issue #1261 - fake_error(&env) + fake_error(&env); } }); From eb05efcdf2861e555869951f5a11d25a3eaeb41f Mon Sep 17 00:00:00 2001 From: hegglinc Date: Mon, 13 Mar 2023 14:44:14 +0100 Subject: [PATCH 74/74] Separate trait bounds from generic args and handle bad case First of all we separated trait bounds from generic args, since for example for a function signature within an impl block, the trait bounds at the function level can still refer to the generic args defined in the impl block. Secondly, we had a problem if a generic had duplicate traitbounds, with different generics, e.g.: `T: Foo + Foo`, this is now handled correctly, under the assumption that the predicates occurr in a certain order, i.e. projections come after their respective trait. So far this seems to be always the case, if it's not I don't see how this problem can be solved. --- prusti/src/ide_helper/query_signature.rs | 127 +++++++++++------------ 1 file changed, 60 insertions(+), 67 deletions(-) diff --git a/prusti/src/ide_helper/query_signature.rs b/prusti/src/ide_helper/query_signature.rs index 988d3b5898e..bd392f41025 100644 --- a/prusti/src/ide_helper/query_signature.rs +++ b/prusti/src/ide_helper/query_signature.rs @@ -21,6 +21,7 @@ enum ExternSpecBlock { name: String, path: String, generics: Vec, + bounds: HashMap>, function_sig: FunctionSignature, }, Impl { @@ -28,6 +29,7 @@ enum ExternSpecBlock { // the path trait_name: Option, generics: Vec, + bounds: HashMap>, function_sig: FunctionSignature, }, } @@ -60,11 +62,13 @@ impl ExternSpecBlock { } .to_string(); let generics = generic_params(tcx, impl_defid); + let bounds = trait_bounds(tcx, impl_defid); Some(ExternSpecBlock::Impl { name: self_ty, trait_name, generics, + bounds, function_sig: signature, }) } @@ -77,11 +81,13 @@ impl ExternSpecBlock { let traitname = tcx.opt_item_name(parent)?.to_string(); let parent_defpath = get_parent_chain(parent, tcx); let trait_params = generic_params(tcx, parent); + let bounds = trait_bounds(tcx, parent); Some(ExternSpecBlock::Trait { name: traitname, path: parent_defpath, generics: trait_params, + bounds, function_sig: signature, }) } else { @@ -105,16 +111,17 @@ impl fmt::Display for ExternSpecBlock { name, trait_name, generics, + bounds, function_sig, } => { let generics_str = generic_args_str(generics, false); - let where_block = bounds_where_block(generics); + let where_block = bounds_where_block(bounds); write!(f, "#[extern_spec]\nimpl{generics_str} ")?; if let Some(trait_name) = trait_name { write!(f, "{trait_name} for ")?; } - writeln!(f, "{name} {where_block}{{")?; + writeln!(f, "{name}{where_block}\n{{")?; let fn_sig = indent(&function_sig.to_string()); write!(f, "{fn_sig}\n}}") } @@ -122,14 +129,15 @@ impl fmt::Display for ExternSpecBlock { name, path, generics, + bounds, function_sig, } => { let fn_sig = indent(&function_sig.to_string()); let generics_str = generic_args_str(generics, false); - let where_block = bounds_where_block(generics); + let where_block = bounds_where_block(bounds); // do traits specify traitbounds too? writeln!(f, "#[extern_spec({path})]")?; - writeln!(f, "trait {name}{generics_str} {where_block}{{")?; + writeln!(f, "trait {name}{generics_str}{where_block}\n{{")?; writeln!(f, "{fn_sig}\n}}") } ExternSpecBlock::StandaloneFn { @@ -146,19 +154,14 @@ impl fmt::Display for ExternSpecBlock { #[derive(Debug, Clone)] struct GenericArg { name: String, - default_value: Option, - bounds: Vec, - projections: HashMap, + _default_value: Option, } impl fmt::Display for GenericArg { - /// For a generic argument, generate a string such as - /// T: Add + Display - /// default formatter will include the traitbounds, otherwise just take - /// .name field if they should be part of a where block fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let bounds_str = traitbounds_string(&self.bounds); - write!(f, "{}: {bounds_str}", self.name) + write!(f, "{}", self.name) + // Trait bounds will always be in where blocks since this + // complicates things a lot otherwise. // for now we ignore defaults since prusti doesnt accept them in // some cases.. // if self.default_value.is_some() { @@ -174,14 +177,13 @@ impl fmt::Display for GenericArg { /// T: bound1 + bound2, /// S: anotherbound, /// ``` -fn bounds_where_block(arglist: &[GenericArg]) -> String { - let bounds_vec = arglist +fn bounds_where_block(traitbounds: &HashMap>) -> String { + let bounds_vec = traitbounds .iter() - .filter(|arg| !arg.bounds.is_empty()) - .map(|arg| format!("\t{}: {}", arg.name, traitbounds_string(&arg.bounds))) + .map(|(arg, bounds)| format!("\t{}: {}", arg, traitbounds_string(bounds))) .collect::>(); if !bounds_vec.is_empty() { - format!("where\n{}\n", bounds_vec.join(",\n")) + format!("\nwhere\n{}", bounds_vec.join(",\n")) } else { "".to_string() } @@ -243,6 +245,7 @@ impl fmt::Display for TraitBound { struct FunctionSignature { name: String, generics: Vec, + bounds: HashMap>, arguments: Vec<(String, String)>, // (name, type) return_type: Option, } @@ -267,9 +270,11 @@ impl FunctionSignature { .collect(); let generics = generic_params(tcx, defid); + let bounds = trait_bounds(tcx, defid); Some(Self { name, generics, + bounds, arguments, return_type, }) @@ -290,44 +295,44 @@ impl fmt::Display for FunctionSignature { // fn(args) -> output where // bounds; fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let generics_str = generic_args_str(&self.generics, true); - // let where_block = bounds_where_block(&self.generics); + let generics_str = generic_args_str(&self.generics, false); + let where_block = bounds_where_block(&self.bounds); let args_str = self.arguments_string(); write!(f, "fn {}{}{}", self.name, generics_str, args_str)?; if let Some(ret_ty) = self.return_type.clone() { write!(f, " -> {ret_ty}")?; } - write!(f, ";") + write!(f, "{where_block};") } } fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { let generics = tcx.generics_of(defid); - let mut generic_params: HashMap = HashMap::new(); let substs = ty::subst::InternalSubsts::identity_for_item(tcx, defid); - // first we just collect all generic parameters - for param in generics.params.iter() { - let ident = param.name.to_string(); - if ident == "Self" { - continue; - } - let mut generic_arg = GenericArg { - name: ident.clone(), - default_value: None, - bounds: vec![], - projections: HashMap::new(), - }; - let default_opt = param.default_value(tcx); - if let Some(default_value) = default_opt { - generic_arg.default_value = Some(format!("{}", default_value.subst(tcx, substs))); - } - - generic_params.insert(ident, generic_arg); - } + generics + .params + .iter() + .filter_map(|param| { + let ident = param.name.to_string(); + if ident == "Self" { + None + } else { + let default_value = param + .default_value(tcx) + .map(|val| val.subst(tcx, substs).to_string()); + Some(GenericArg { + name: ident, + _default_value: default_value, + }) + } + }) + .collect() +} - // now add traitbounds and default types: +fn trait_bounds(tcx: TyCtxt<'_>, defid: DefId) -> HashMap> { + let mut traitbounds: HashMap> = HashMap::new(); let predicates = tcx.predicates_of(defid); for (predicate, _) in predicates.predicates { @@ -355,44 +360,32 @@ fn generic_params(tcx: TyCtxt<'_>, defid: DefId) -> Vec { args: trait_args, }; // add this bound to the given type. - generic_params - .get_mut(&self_ty) - .unwrap() // is failing appropriate? - .bounds - .push(bound); + if let Some(bounds) = traitbounds.get_mut(&self_ty) { + bounds.push(bound); + } else { + traitbounds.insert(self_ty, vec![bound]); + } } - // not sure if we should even handle this: PredicateKind::Clause(Clause::Projection(p)) => { let item_id = p.projection_ty.def_id; let self_ty = format!("{}", p.projection_ty.self_ty()); let trait_defid: DefId = p.projection_ty.trait_def_id(tcx); let trait_defpath = tcx.def_path_str(trait_defid); - let item_name = tcx.item_name(item_id); + let item_name = tcx.item_name(item_id).to_string(); let projection_term = format!("{}={}", item_name, p.term); - let genarg = generic_params.get_mut(&self_ty).unwrap(); - // not sure if this is actually completely correct. - // can a single generic type have the same traitbound - // more than once with different arguments? - // I would imagine yes.. may be a todo - genarg.projections.insert(trait_defpath, projection_term); + let bounds = traitbounds.get_mut(&self_ty).unwrap(); + let mut last_bound = bounds.pop().unwrap(); + if last_bound.name == trait_defpath { + last_bound.args.push(projection_term); + } + bounds.push(last_bound); } _ => {} } } - - // handling projections here is simpler than when printing: - // could not do this directly since projections - for el in generic_params.values_mut() { - for tb in &mut el.bounds { - if let Some(projection_term) = el.projections.get(&tb.name) { - tb.args.push(projection_term.clone()); - } - } - } - - generic_params.values().cloned().collect() + traitbounds } pub fn collect_queried_signature(tcx: TyCtxt<'_>, fncalls: &[ProcDef]) -> Option {