Skip to content

Commit

Permalink
Emit an error when proof_for_contract function is not found (#3609)
Browse files Browse the repository at this point in the history
Currently, Kani panics if the function specified in the
`proof_for_contract` attribute is not found (e.g. because the function
is not reachable) (see #3467). This PR adds an error message pointing
out the issue.

Towards #3467 

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.
  • Loading branch information
zhassan-aws authored Oct 19, 2024
1 parent 00c648d commit dcb4d6d
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 3 deletions.
16 changes: 15 additions & 1 deletion kani-compiler/src/kani_middle/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! This module contains code for processing Rust attributes (like `kani::proof`).

use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashSet};

use kani_metadata::{CbmcSolver, HarnessAttributes, HarnessKind, Stub};
use quote::ToTokens;
Expand Down Expand Up @@ -474,6 +474,20 @@ impl<'tcx> KaniAttributes<'tcx> {
|| self.map.contains_key(&KaniAttributeKind::ProofForContract)
}

/// Check that the function specified in the `proof_for_contract` attribute
/// is reachable and emit an error if it isn't
pub fn check_proof_for_contract(&self, reachable_functions: &HashSet<DefId>) {
if let Some((symbol, function, span)) = self.interpret_for_contract_attribute() {
if !reachable_functions.contains(&function) {
let err_msg = format!(
"The function specified in the `proof_for_contract` attribute, `{symbol}`, was not found.\
\nMake sure the function is reachable from the harness."
);
self.tcx.dcx().span_err(span, err_msg);
}
}
}

/// Extract harness attributes for a given `def_id`.
///
/// We only extract attributes for harnesses that are local to the current crate.
Expand Down
13 changes: 11 additions & 2 deletions kani-compiler/src/kani_middle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ impl TyVisitor for FindUnsafeCell<'_> {
pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem]) {
// Avoid printing the same error multiple times for different instantiations of the same item.
let mut def_ids = HashSet::new();
let reachable_functions: HashSet<InternalDefId> = items
.iter()
.filter_map(|i| match i {
MonoItem::Fn(instance) => Some(rustc_internal::internal(tcx, instance.def.def_id())),
_ => None,
})
.collect();
for item in items.iter().filter(|i| matches!(i, MonoItem::Fn(..) | MonoItem::Static(..))) {
let def_id = match item {
MonoItem::Fn(instance) => instance.def.def_id(),
Expand All @@ -110,9 +117,11 @@ pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem])
}
};
if !def_ids.contains(&def_id) {
let attributes = KaniAttributes::for_def_id(tcx, def_id);
// Check if any unstable attribute was reached.
KaniAttributes::for_def_id(tcx, def_id)
.check_unstable_features(&queries.args().unstable_features);
attributes.check_unstable_features(&queries.args().unstable_features);
// Check whether all `proof_for_contract` functions are reachable
attributes.check_proof_for_contract(&reachable_functions);
def_ids.insert(def_id);
}
}
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/unknown-contract-harness/expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error: The function specified in the `proof_for_contract` attribute, `foo`, was not found.\
Make sure the function is reachable from the harness.
test.rs:\
|\
| #[kani::proof_for_contract(foo)]\
| ^^^^^^^^^^^^^^
14 changes: 14 additions & 0 deletions tests/ui/unknown-contract-harness/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT
// kani-flags: -Zfunction-contracts

// This test checks Kani's error when function specified in `proof_for_contract`
// harness is not found (e.g. because it's not reachable from the harness)

#[kani::requires(true)]
fn foo() {}

#[kani::proof_for_contract(foo)]
fn check_foo() {
assert!(true);
}

0 comments on commit dcb4d6d

Please sign in to comment.