Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically add loop guards without preconditions as invariants #887

Merged
merged 24 commits into from
May 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
52af7c6
Add loop guards without preconditions to invariant
JonasAlaif Feb 28, 2022
1b9ed1a
Avoid unnecessary encoding
JonasAlaif Feb 28, 2022
fc978a9
Clippy fix and ignore AccessPredicate expressions
JonasAlaif Feb 28, 2022
9fc7dc0
Fix issue with resolving function calls
JonasAlaif Feb 28, 2022
d4fe137
Clippy fixes
JonasAlaif Feb 28, 2022
d334808
Remove unnecessary check of MethodCall
JonasAlaif Feb 28, 2022
af55365
Make use of ExprFolder
JonasAlaif Feb 28, 2022
50a77fd
Fix typo
JonasAlaif Feb 28, 2022
ddc7e82
Make warning function more general
JonasAlaif Feb 28, 2022
ef272f0
Fmt fix
JonasAlaif Feb 28, 2022
2b0306d
Fix comment
JonasAlaif Mar 1, 2022
2e91033
Merge branch 'master' into loop-inv-fix
JonasAlaif Mar 23, 2022
e1f3e66
Change asserts to assumes after unrolling
JonasAlaif Mar 23, 2022
2506646
Merge branch 'master' into loop-inv-fix
JonasAlaif Apr 22, 2022
6a3b715
Clippy fix
JonasAlaif Apr 22, 2022
df53a1e
Track pure functions preventing unrolling
JonasAlaif Apr 22, 2022
51f7312
Clippy fix
JonasAlaif Apr 22, 2022
e178ca1
Merge branch 'master' into loop-inv-fix
JonasAlaif Apr 27, 2022
4ada2cd
Only emit warning on verification failure
JonasAlaif Apr 27, 2022
283776b
Merge branch 'master' into loop-inv-fix
JonasAlaif May 13, 2022
4ed6b78
Distinguish between warning types
JonasAlaif May 13, 2022
0eddf8a
clippy fix
JonasAlaif May 13, 2022
df8ecd7
Merge branch 'master' into loop-inv-fix
JonasAlaif May 20, 2022
caee09b
Update test with new expected errors
JonasAlaif May 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 36 additions & 19 deletions prusti-interface/src/environment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
use std::path::PathBuf;

use rustc_errors::MultiSpan;
use rustc_errors::{DiagnosticBuilder, EmissionGuarantee, MultiSpan};
use rustc_span::{Span, symbol::Symbol};
use std::collections::HashSet;
use log::{debug, trace};
Expand Down Expand Up @@ -76,6 +76,7 @@ pub struct Environment<'tcx> {
bodies: RefCell<HashMap<LocalDefId, CachedBody<'tcx>>>,
external_bodies: RefCell<HashMap<DefId, CachedExternalBody<'tcx>>>,
tcx: TyCtxt<'tcx>,
warn_buffer: RefCell<Vec<rustc_errors::Diagnostic>>,
}

impl<'tcx> Environment<'tcx> {
Expand All @@ -85,6 +86,7 @@ impl<'tcx> Environment<'tcx> {
tcx,
bodies: RefCell::new(HashMap::new()),
external_bodies: RefCell::new(HashMap::new()),
warn_buffer: RefCell::new(Vec::new()),
}
}

Expand Down Expand Up @@ -142,15 +144,12 @@ impl<'tcx> Environment<'tcx> {
// self.state.session.span_err(sp, msg);
// }

/// Emits an error message.
pub fn span_err_with_help_and_notes<S: Into<MultiSpan> + Clone>(
&self,
fn configure_diagnostic<S: Into<MultiSpan> + Clone, T: EmissionGuarantee>(
diagnostic: &mut DiagnosticBuilder<T>,
sp: S,
msg: &str,
help: &Option<String>,
notes: &[(String, Option<S>)],
notes: &[(String, Option<S>)]
) {
let mut diagnostic = self.tcx.sess.struct_err(msg);
diagnostic.set_span(sp);
if let Some(help_msg) = help {
diagnostic.help(help_msg);
Expand All @@ -162,10 +161,25 @@ impl<'tcx> Environment<'tcx> {
diagnostic.note(note_msg);
}
}
diagnostic.emit();
}

/// Emits an error message.
pub fn span_err_with_help_and_notes<S: Into<MultiSpan> + Clone>(
&self,
sp: S,
msg: &str,
help: &Option<String>,
notes: &[(String, Option<S>)],
) {
let mut diagnostic = self.tcx.sess.struct_err(msg);
Self::configure_diagnostic(&mut diagnostic, sp, help, notes);
for warn in self.warn_buffer.borrow_mut().iter_mut() {
self.tcx.sess.diagnostic().emit_diagnostic(warn);
}
diagnostic.emit();
}

/// Emits a warning message.
pub fn span_warn_with_help_and_notes<S: Into<MultiSpan> + Clone>(
&self,
sp: S,
Expand All @@ -174,20 +188,23 @@ impl<'tcx> Environment<'tcx> {
notes: &[(String, Option<S>)],
) {
let mut diagnostic = self.tcx.sess.struct_warn(msg);
diagnostic.set_span(sp);
if let Some(help_msg) = help {
diagnostic.help(help_msg);
}
for (note_msg, opt_note_sp) in notes {
if let Some(note_sp) = opt_note_sp {
diagnostic.span_note(note_sp.clone(), note_msg);
} else {
diagnostic.note(note_msg);
}
}
Self::configure_diagnostic(&mut diagnostic, sp, help, notes);
diagnostic.emit();
}

/// Buffers a warning message, to be emitted on error.
pub fn span_warn_on_err_with_help_and_notes<S: Into<MultiSpan> + Clone>(
&self,
sp: S,
msg: &str,
help: &Option<String>,
notes: &[(String, Option<S>)],
) {
let mut diagnostic = self.tcx.sess.struct_warn(msg);
Self::configure_diagnostic(&mut diagnostic, sp, help, notes);
diagnostic.buffer(&mut self.warn_buffer.borrow_mut());
}

/// Returns true if an error has been emitted
pub fn has_errors(&self) -> bool {
self.tcx.sess.has_errors().is_some()
Expand Down
61 changes: 49 additions & 12 deletions prusti-interface/src/prusti_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use ::log::warn;
/// `SpannedEncodingError` and similar types to something less confusing.)
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PrustiError {
is_error: bool,
kind: PrustiErrorKind,
/// If `true`, it should not be reported to the user. We need this in cases
/// when the same error could be reported twice.
///
Expand All @@ -34,6 +34,13 @@ pub struct PrustiError {
help: Option<String>,
notes: Vec<(String, Option<MultiSpan>)>,
}
/// Determines how a `PrustiError` is reported.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PrustiErrorKind {
Error, Warning,
/// A warning which is only shown if at least one error is emitted.
WarningOnError,
}

impl PartialOrd for PrustiError {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Expand All @@ -51,7 +58,7 @@ impl PrustiError {
/// Private constructor. Use one of the following methods.
fn new(message: String, span: MultiSpan) -> Self {
PrustiError {
is_error: true,
kind: PrustiErrorKind::Error,
is_disabled: false,
message,
span,
Expand Down Expand Up @@ -101,6 +108,29 @@ impl PrustiError {
)
}

/// Report a non-fatal issue
pub fn warning<S: ToString>(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
}

/// Report a non-fatal issue only if there are errors
/// (e.g. cannot automatically include loop guard as an invariant)
pub fn warning_on_error<S: ToString>(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
}

/// Report an internal error of Prusti (e.g. failure of the fold-unfold)
pub fn internal<S: ToString>(message: S, span: MultiSpan) -> Self {
check_message(message.to_string());
Expand All @@ -122,11 +152,11 @@ impl PrustiError {

/// Set that this Prusti error should be reported as a warning to the user
pub fn set_warning(&mut self) {
self.is_error = false;
self.kind = PrustiErrorKind::Warning;
}

pub fn is_error(&self) -> bool {
self.is_error
matches!(self.kind, PrustiErrorKind::Error)
}

// FIXME: This flag is a temporary workaround for having duplicate errors
Expand All @@ -152,24 +182,31 @@ impl PrustiError {
self.notes.push((message.to_string(), opt_span));
}

/// Report the encoding error using the compiler's interface
/// Report the encoding error using the compiler's interface.
/// Warnings are not immediately emitted, but buffered and only shown
/// if an error is emitted (i.e. verification failure)
pub fn emit(self, env: &Environment) {
assert!(!self.is_disabled);
if self.is_error {
env.span_err_with_help_and_notes(
match self.kind {
PrustiErrorKind::Error => env.span_err_with_help_and_notes(
self.span,
&self.message,
&self.help,
&self.notes,
);
} else {
env.span_warn_with_help_and_notes(
),
PrustiErrorKind::Warning => env.span_warn_with_help_and_notes(
self.span,
&self.message,
&self.help,
&self.notes,
);
}
),
PrustiErrorKind::WarningOnError => env.span_warn_on_err_with_help_and_notes(
self.span,
&self.message,
&self.help,
&self.notes,
),
};
}

/// Cancel the error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ use prusti_contracts::*;
fn test1() {
let mut i = 0;
while i < 5 {
i += 1; //~ ERROR: attempt to add with overflow
i += 1;
}
let mut j = 0;
assert!(i == j);
assert!(i == j); //~ ERROR: the asserted expression might not hold
}

fn test2() {
let mut i = 0;
while i < 5 {
assert!(i < 5); //~ ERROR: the asserted expression might not hold
assert!(i < 5);
i += 1;
}
let mut j = 0;
assert!(i == j);
assert!(i == j); //~ ERROR: the asserted expression might not hold
}

fn test3() {
Expand All @@ -35,7 +35,7 @@ fn test4(v: &mut[i32]) {
let mut zero_num_start = 0;
let mut i = 0;
while i < v.len() {
assert!(i < v.len()); //~ ERROR: the asserted expression might not hold
assert!(i < v.len());
i += 1;
}
}
Expand All @@ -44,8 +44,8 @@ fn test5(v: &mut[i32]) {
let mut zero_num_start = 0;
let mut i = 0;
while i < v.len() {
if v[i] == 0 { //~ ERROR: the array or slice index may be out of bounds
zero_num_start += 1;
if v[i] == 0 {
zero_num_start += 1; //~ ERROR: attempt to add with overflow
}
i += 1;
}
Expand Down
Loading