Skip to content

Commit dcb4d6d

Browse files
authored
Emit an error when proof_for_contract function is not found (#3609)
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.
1 parent 00c648d commit dcb4d6d

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

kani-compiler/src/kani_middle/attributes.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
//! This module contains code for processing Rust attributes (like `kani::proof`).
44
5-
use std::collections::BTreeMap;
5+
use std::collections::{BTreeMap, HashSet};
66

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

477+
/// Check that the function specified in the `proof_for_contract` attribute
478+
/// is reachable and emit an error if it isn't
479+
pub fn check_proof_for_contract(&self, reachable_functions: &HashSet<DefId>) {
480+
if let Some((symbol, function, span)) = self.interpret_for_contract_attribute() {
481+
if !reachable_functions.contains(&function) {
482+
let err_msg = format!(
483+
"The function specified in the `proof_for_contract` attribute, `{symbol}`, was not found.\
484+
\nMake sure the function is reachable from the harness."
485+
);
486+
self.tcx.dcx().span_err(span, err_msg);
487+
}
488+
}
489+
}
490+
477491
/// Extract harness attributes for a given `def_id`.
478492
///
479493
/// We only extract attributes for harnesses that are local to the current crate.

kani-compiler/src/kani_middle/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ impl TyVisitor for FindUnsafeCell<'_> {
101101
pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem]) {
102102
// Avoid printing the same error multiple times for different instantiations of the same item.
103103
let mut def_ids = HashSet::new();
104+
let reachable_functions: HashSet<InternalDefId> = items
105+
.iter()
106+
.filter_map(|i| match i {
107+
MonoItem::Fn(instance) => Some(rustc_internal::internal(tcx, instance.def.def_id())),
108+
_ => None,
109+
})
110+
.collect();
104111
for item in items.iter().filter(|i| matches!(i, MonoItem::Fn(..) | MonoItem::Static(..))) {
105112
let def_id = match item {
106113
MonoItem::Fn(instance) => instance.def.def_id(),
@@ -110,9 +117,11 @@ pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem])
110117
}
111118
};
112119
if !def_ids.contains(&def_id) {
120+
let attributes = KaniAttributes::for_def_id(tcx, def_id);
113121
// Check if any unstable attribute was reached.
114-
KaniAttributes::for_def_id(tcx, def_id)
115-
.check_unstable_features(&queries.args().unstable_features);
122+
attributes.check_unstable_features(&queries.args().unstable_features);
123+
// Check whether all `proof_for_contract` functions are reachable
124+
attributes.check_proof_for_contract(&reachable_functions);
116125
def_ids.insert(def_id);
117126
}
118127
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
error: The function specified in the `proof_for_contract` attribute, `foo`, was not found.\
2+
Make sure the function is reachable from the harness.
3+
test.rs:\
4+
|\
5+
| #[kani::proof_for_contract(foo)]\
6+
| ^^^^^^^^^^^^^^
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: -Zfunction-contracts
4+
5+
// This test checks Kani's error when function specified in `proof_for_contract`
6+
// harness is not found (e.g. because it's not reachable from the harness)
7+
8+
#[kani::requires(true)]
9+
fn foo() {}
10+
11+
#[kani::proof_for_contract(foo)]
12+
fn check_foo() {
13+
assert!(true);
14+
}

0 commit comments

Comments
 (0)