Skip to content

Commit 37db951

Browse files
author
Carolyn Zech
committed
generate identical contract harnesses to manual ones
1 parent f26c019 commit 37db951

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ use rustc_session::config::{CrateType, OutputFilenames, OutputType};
4242
use rustc_session::output::out_filename;
4343
use rustc_smir::rustc_internal;
4444
use rustc_target::spec::PanicStrategy;
45+
use stable_mir::CrateDef;
4546
use stable_mir::mir::mono::{Instance, MonoItem};
46-
use stable_mir::{CrateDef, DefId};
4747
use std::any::Any;
4848
use std::collections::BTreeMap;
4949
use std::fmt::Write;
@@ -212,6 +212,27 @@ impl GotocCodegenBackend {
212212

213213
(gcx, items, contract_info)
214214
}
215+
216+
/// Given a contract harness, get the DefId of its target.
217+
/// For manual harnesses, extract it from the #[proof_for_contract] attribute.
218+
/// For automatic harnesses, extract the target from the harness's GenericArgs.
219+
fn target_def_id_for_harness(
220+
&self,
221+
tcx: TyCtxt,
222+
harness: &Instance,
223+
is_automatic_harness: bool,
224+
) -> Option<InternalDefId> {
225+
if is_automatic_harness {
226+
let kind = harness.args().0[0].expect_ty().kind();
227+
let (fn_to_verify_def, _) = kind.fn_def().unwrap();
228+
let def_id = fn_to_verify_def.def_id();
229+
let attrs = KaniAttributes::for_def_id(tcx, def_id);
230+
if attrs.has_contract() { Some(rustc_internal::internal(tcx, def_id)) } else { None }
231+
} else {
232+
let harness_attrs = KaniAttributes::for_def_id(tcx, harness.def.def_id());
233+
harness_attrs.interpret_for_contract_attribute().map(|(_, id, _)| id)
234+
}
235+
}
215236
}
216237

217238
impl CodegenBackend for GotocCodegenBackend {
@@ -275,8 +296,9 @@ impl CodegenBackend for GotocCodegenBackend {
275296
for harness in &unit.harnesses {
276297
let transformer = BodyTransformation::new(&queries, tcx, &unit);
277298
let model_path = units.harness_model_path(*harness).unwrap();
299+
let is_automatic_harness = units.is_automatic_harness(harness);
278300
let contract_metadata =
279-
contract_metadata_for_harness(tcx, harness.def.def_id());
301+
self.target_def_id_for_harness(tcx, harness, is_automatic_harness);
280302
let (gcx, items, contract_info) = self.codegen_items(
281303
tcx,
282304
&[MonoItem::Fn(*harness)],
@@ -453,11 +475,6 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
453475
}
454476
}
455477

456-
fn contract_metadata_for_harness(tcx: TyCtxt, def_id: DefId) -> Option<InternalDefId> {
457-
let attrs = KaniAttributes::for_def_id(tcx, def_id);
458-
attrs.interpret_for_contract_attribute().map(|(_, id, _)| id)
459-
}
460-
461478
fn check_target(session: &Session) {
462479
// The requirement below is needed to build a valid CBMC machine model
463480
// in function `machine_model_from_session` from

kani-compiler/src/kani_middle/codegen_units.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ impl CodegenUnits {
133133
self.units.iter()
134134
}
135135

136+
pub fn is_automatic_harness(&self, harness: &Harness) -> bool {
137+
self.harness_info.get(harness).is_some_and(|md| md.is_automatically_generated)
138+
}
139+
136140
/// We store which instance of modifies was generated.
137141
pub fn store_modifies(&mut self, harness_modifies: &[(Harness, AssignsContract)]) {
138142
for (harness, modifies) in harness_modifies {

kani-compiler/src/kani_middle/transform/automatic.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,24 @@
77
//! then transform its body to be a harness for that function.
88
99
use crate::args::ReachabilityType;
10+
use crate::kani_middle::attributes::KaniAttributes;
1011
use crate::kani_middle::codegen_units::CodegenUnit;
11-
use crate::kani_middle::kani_functions::{KaniIntrinsic, KaniModel};
12+
use crate::kani_middle::kani_functions::{KaniHook, KaniIntrinsic, KaniModel};
1213
use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction};
1314
use crate::kani_middle::transform::{TransformPass, TransformationType};
1415
use crate::kani_queries::QueryDb;
1516
use rustc_middle::ty::TyCtxt;
17+
use stable_mir::CrateDef;
1618
use stable_mir::mir::mono::Instance;
17-
use stable_mir::mir::{Body, Operand, Place, TerminatorKind};
18-
use stable_mir::ty::{FnDef, GenericArgKind, GenericArgs};
19+
use stable_mir::mir::{Body, Mutability, Operand, Place, TerminatorKind};
20+
use stable_mir::ty::{FnDef, GenericArgKind, GenericArgs, RigidTy, Ty};
1921
use tracing::debug;
2022

2123
#[derive(Debug)]
2224
pub struct AutomaticHarnessPass {
2325
/// The FnDef of KaniModel::Any
2426
kani_any: FnDef,
27+
init_contracts_hook: Instance,
2528
/// All of the automatic harness Instances that we generated in the CodegenUnits constructor
2629
automatic_harnesses: Vec<Instance>,
2730
}
@@ -37,6 +40,9 @@ impl AutomaticHarnessPass {
3740
let kani_fns = query_db.kani_functions();
3841
let harness_intrinsic = *kani_fns.get(&KaniIntrinsic::AutomaticHarness.into()).unwrap();
3942
let kani_any = *kani_fns.get(&KaniModel::Any.into()).unwrap();
43+
let init_contracts_hook = *kani_fns.get(&KaniHook::InitContracts.into()).unwrap();
44+
let init_contracts_hook =
45+
Instance::resolve(init_contracts_hook, &GenericArgs(vec![])).unwrap();
4046
let automatic_harnesses = unit
4147
.harnesses
4248
.iter()
@@ -46,7 +52,7 @@ impl AutomaticHarnessPass {
4652
def == harness_intrinsic
4753
})
4854
.collect::<Vec<_>>();
49-
Self { kani_any, automatic_harnesses }
55+
Self { kani_any, init_contracts_hook, automatic_harnesses }
5056
}
5157
}
5258

@@ -65,7 +71,7 @@ impl TransformPass for AutomaticHarnessPass {
6571
matches!(query_db.args().reachability_analysis, ReachabilityType::AllFns)
6672
}
6773

68-
fn transform(&mut self, _tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) {
74+
fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) {
6975
debug!(function=?instance.name(), "AutomaticHarnessPass::transform");
7076

7177
if !self.automatic_harnesses.contains(&instance) {
@@ -83,6 +89,23 @@ impl TransformPass for AutomaticHarnessPass {
8389
harness_body.clear_body(TerminatorKind::Return);
8490
let mut source = SourceInstruction::Terminator { bb: 0 };
8591

92+
// Contract harnesses need a free(NULL) statement, c.f. kani_core::init_contracts().
93+
let attrs = KaniAttributes::for_def_id(tcx, def.def_id());
94+
if attrs.has_contract() {
95+
let ret_local = harness_body.new_local(
96+
Ty::from_rigid_kind(RigidTy::Tuple(vec![])),
97+
source.span(harness_body.blocks()),
98+
Mutability::Not,
99+
);
100+
harness_body.insert_call(
101+
&self.init_contracts_hook,
102+
&mut source,
103+
InsertPosition::Before,
104+
vec![],
105+
Place::from(ret_local),
106+
);
107+
}
108+
86109
let mut arg_locals = vec![];
87110

88111
// For each argument of `fn_to_verify`, create a nondeterministic value of its type

0 commit comments

Comments
 (0)