Skip to content

Commit 5b6519b

Browse files
authored
Automatic Derivation Fixes (#4194)
Fixes and improvements to the derivation of Arbitrary in the compiler introduced in #4167, along with a fix for #4189 and some other small improvements to autoharness that I noticed along the way. Best reviewed commit by commit. Resolves #4189 Towards #3832 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 e197baf commit 5b6519b

File tree

10 files changed

+293
-179
lines changed

10 files changed

+293
-179
lines changed

kani-compiler/src/kani_middle/codegen_units.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::kani_middle::resolve::expect_resolve_fn;
1818
use crate::kani_middle::stubbing::{check_compatibility, harness_stub_map};
1919
use crate::kani_middle::{can_derive_arbitrary, implements_arbitrary};
2020
use crate::kani_queries::QueryDb;
21-
use fxhash::FxHashMap;
21+
use fxhash::{FxHashMap, FxHashSet};
2222
use kani_metadata::{
2323
ArtifactType, AssignsContract, AutoHarnessMetadata, AutoHarnessSkipReason, HarnessKind,
2424
HarnessMetadata, KaniMetadata,
@@ -390,8 +390,16 @@ fn automatic_harness_partition(
390390
crate_name: &str,
391391
kani_any_def: FnDef,
392392
) -> (Vec<Instance>, BTreeMap<String, AutoHarnessSkipReason>) {
393-
let crate_fns =
394-
stable_mir::all_local_items().into_iter().filter(|item| item.ty().kind().is_fn());
393+
let crate_fn_defs = stable_mir::local_crate().fn_defs().into_iter().collect::<FxHashSet<_>>();
394+
// Filter out CrateItems that are functions, but not functions defined in the crate itself, i.e., rustc-inserted functions
395+
// (c.f. https://github.com/model-checking/kani/issues/4189)
396+
let crate_fns = stable_mir::all_local_items().into_iter().filter(|item| {
397+
if let TyKind::RigidTy(RigidTy::FnDef(def, _)) = item.ty().kind() {
398+
crate_fn_defs.contains(&def)
399+
} else {
400+
false
401+
}
402+
});
395403

396404
let included_set = make_regex_set(args.autoharness_included_patterns.clone());
397405
let excluded_set = make_regex_set(args.autoharness_excluded_patterns.clone());
@@ -437,12 +445,15 @@ fn automatic_harness_partition(
437445
// Note that we've already filtered out generic functions, so we know that each of these arguments has a concrete type.
438446
let mut problematic_args = vec![];
439447
for (idx, arg) in body.arg_locals().iter().enumerate() {
440-
let implements_arbitrary = ty_arbitrary_cache.entry(arg.ty).or_insert_with(|| {
441-
implements_arbitrary(arg.ty, kani_any_def)
442-
|| can_derive_arbitrary(arg.ty, kani_any_def)
443-
});
448+
if !ty_arbitrary_cache.contains_key(&arg.ty) {
449+
let impls_arbitrary =
450+
implements_arbitrary(arg.ty, kani_any_def, &mut ty_arbitrary_cache)
451+
|| can_derive_arbitrary(arg.ty, kani_any_def, &mut ty_arbitrary_cache);
452+
ty_arbitrary_cache.insert(arg.ty, impls_arbitrary);
453+
}
454+
let impls_arbitrary = ty_arbitrary_cache.get(&arg.ty).unwrap();
444455

445-
if !(*implements_arbitrary) {
456+
if !impls_arbitrary {
446457
// Find the name of the argument by referencing var_debug_info.
447458
// Note that enumerate() starts at 0, while StableMIR argument_index starts at 1, hence the idx+1.
448459
let arg_name = body

kani-compiler/src/kani_middle/mod.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use std::collections::HashSet;
77

88
use crate::kani_queries::QueryDb;
9+
use fxhash::FxHashMap;
910
use rustc_hir::{def::DefKind, def_id::DefId as InternalDefId, def_id::LOCAL_CRATE};
1011
use rustc_middle::ty::TyCtxt;
1112
use rustc_smir::rustc_internal;
@@ -178,7 +179,19 @@ pub fn stable_fn_def(tcx: TyCtxt, def_id: InternalDefId) -> Option<FnDef> {
178179
/// ```
179180
/// So we select the terminator that calls T::kani::Arbitrary::any(), then try to resolve it to an Instance.
180181
/// `T` implements Arbitrary iff we successfully resolve the Instance.
181-
fn implements_arbitrary(ty: Ty, kani_any_def: FnDef) -> bool {
182+
fn implements_arbitrary(
183+
ty: Ty,
184+
kani_any_def: FnDef,
185+
ty_arbitrary_cache: &mut FxHashMap<Ty, bool>,
186+
) -> bool {
187+
if let Some(v) = ty_arbitrary_cache.get(&ty) {
188+
return *v;
189+
}
190+
191+
if ty.kind().rigid().is_none() {
192+
return false;
193+
}
194+
182195
let kani_any_body =
183196
Instance::resolve(kani_any_def, &GenericArgs(vec![GenericArgKind::Type(ty)]))
184197
.unwrap()
@@ -192,20 +205,32 @@ fn implements_arbitrary(ty: Ty, kani_any_def: FnDef) -> bool {
192205
if let TyKind::RigidTy(RigidTy::FnDef(def, args)) =
193206
func.ty(kani_any_body.arg_locals()).unwrap().kind()
194207
{
195-
return Instance::resolve(def, &args).is_ok();
208+
let res = Instance::resolve(def, &args).is_ok();
209+
ty_arbitrary_cache.insert(ty, res);
210+
return res;
196211
}
197212
}
198213
false
199214
}
200215

201216
/// Is `ty` a struct or enum whose fields/variants implement Arbitrary?
202-
fn can_derive_arbitrary(ty: Ty, kani_any_def: FnDef) -> bool {
203-
let variants_can_derive = |def: AdtDef| {
217+
fn can_derive_arbitrary(
218+
ty: Ty,
219+
kani_any_def: FnDef,
220+
ty_arbitrary_cache: &mut FxHashMap<Ty, bool>,
221+
) -> bool {
222+
let mut variants_can_derive = |def: AdtDef, args: GenericArgs| {
204223
for variant in def.variants_iter() {
205224
let fields = variant.fields();
206225
let mut fields_impl_arbitrary = true;
207-
for ty in fields.iter().map(|field| field.ty()) {
208-
fields_impl_arbitrary &= implements_arbitrary(ty, kani_any_def);
226+
for ty in fields.iter().map(|field| field.ty_with_args(&args)) {
227+
if let TyKind::RigidTy(RigidTy::Adt(..)) = ty.kind() {
228+
fields_impl_arbitrary &=
229+
can_derive_arbitrary(ty, kani_any_def, ty_arbitrary_cache);
230+
} else {
231+
fields_impl_arbitrary &=
232+
implements_arbitrary(ty, kani_any_def, ty_arbitrary_cache);
233+
}
209234
}
210235
if !fields_impl_arbitrary {
211236
return false;
@@ -214,16 +239,22 @@ fn can_derive_arbitrary(ty: Ty, kani_any_def: FnDef) -> bool {
214239
true
215240
};
216241

217-
if let TyKind::RigidTy(RigidTy::Adt(def, _)) = ty.kind() {
242+
if let TyKind::RigidTy(RigidTy::Adt(def, args)) = ty.kind() {
243+
for arg in &args.0 {
244+
if let GenericArgKind::Lifetime(..) = arg {
245+
return false;
246+
}
247+
}
248+
218249
match def.kind() {
219250
AdtKind::Enum => {
220251
// Enums with no variants cannot be instantiated
221252
if def.num_variants() == 0 {
222253
return false;
223254
}
224-
variants_can_derive(def)
255+
variants_can_derive(def, args)
225256
}
226-
AdtKind::Struct => variants_can_derive(def),
257+
AdtKind::Struct => variants_can_derive(def, args),
227258
AdtKind::Union => false,
228259
}
229260
} else {

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

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::kani_middle::kani_functions::{KaniHook, KaniIntrinsic, KaniModel};
1414
use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction};
1515
use crate::kani_middle::transform::{TransformPass, TransformationType};
1616
use crate::kani_queries::QueryDb;
17+
use fxhash::FxHashMap;
1718
use rustc_middle::ty::TyCtxt;
1819
use stable_mir::CrateDef;
1920
use stable_mir::mir::mono::Instance;
@@ -109,14 +110,14 @@ impl TransformPass for AutomaticArbitraryPass {
109110
let binding = instance.args();
110111
let ty = binding.0[0].expect_ty();
111112

112-
if implements_arbitrary(*ty, self.kani_any) {
113+
if implements_arbitrary(*ty, self.kani_any, &mut FxHashMap::default()) {
113114
return (false, body);
114115
}
115116

116-
if let TyKind::RigidTy(RigidTy::Adt(def, ..)) = ty.kind() {
117+
if let TyKind::RigidTy(RigidTy::Adt(def, args)) = ty.kind() {
117118
match def.kind() {
118-
AdtKind::Enum => (true, self.generate_enum_body(def, body)),
119-
AdtKind::Struct => (true, self.generate_struct_body(def, body)),
119+
AdtKind::Enum => (true, self.generate_enum_body(def, args, body)),
120+
AdtKind::Struct => (true, self.generate_struct_body(def, args, body)),
120121
AdtKind::Union => unexpected_ty(ty),
121122
}
122123
} else {
@@ -151,7 +152,8 @@ impl AutomaticArbitraryPass {
151152
/// This function will panic if a field type does not implement Arbitrary.
152153
fn call_kani_any_for_variant(
153154
&self,
154-
def: AdtDef,
155+
adt_def: AdtDef,
156+
adt_args: &GenericArgs,
155157
body: &mut MutableBody,
156158
source: &mut SourceInstruction,
157159
variant: VariantDef,
@@ -160,7 +162,7 @@ impl AutomaticArbitraryPass {
160162
let mut field_locals = vec![];
161163

162164
// Construct nondeterministic values for each of the variant's fields
163-
for ty in fields.iter().map(|field| field.ty()) {
165+
for ty in fields.iter().map(|field| field.ty_with_args(adt_args)) {
164166
let lcl = self.call_kani_any_for_ty(body, ty, source);
165167
field_locals.push(lcl);
166168
}
@@ -173,7 +175,7 @@ impl AutomaticArbitraryPass {
173175
);
174176
let mut assign_instr = SourceInstruction::Terminator { bb: source.bb() - 1 };
175177
let rvalue = Rvalue::Aggregate(
176-
AggregateKind::Adt(def, variant.idx, GenericArgs(vec![]), None, None),
178+
AggregateKind::Adt(adt_def, variant.idx, adt_args.clone(), None, None),
177179
field_locals.into_iter().map(|lcl| Operand::Move(lcl.into())).collect(),
178180
);
179181
body.assign_to(Place::from(0), rvalue, &mut assign_instr, InsertPosition::Before);
@@ -193,7 +195,7 @@ impl AutomaticArbitraryPass {
193195
/// _ => Enum::LastVariant
194196
/// }
195197
/// ```
196-
fn generate_enum_body(&self, def: AdtDef, body: Body) -> Body {
198+
fn generate_enum_body(&self, def: AdtDef, args: GenericArgs, body: Body) -> Body {
197199
// Autoharness only deems a function with an enum eligible if it has at least one variant, c.f. `can_derive_arbitrary`
198200
assert!(def.num_variants() > 0);
199201

@@ -220,7 +222,7 @@ impl AutomaticArbitraryPass {
220222
let mut branches: Vec<(u128, BasicBlockIdx)> = vec![];
221223
for variant in def.variants_iter() {
222224
let target_bb =
223-
self.call_kani_any_for_variant(def, &mut new_body, &mut source, variant);
225+
self.call_kani_any_for_variant(def, &args, &mut new_body, &mut source, variant);
224226
branches.push((variant.idx.to_index() as u128, target_bb));
225227
}
226228

@@ -246,53 +248,37 @@ impl AutomaticArbitraryPass {
246248
/// ...
247249
/// }
248250
/// ```
249-
fn generate_struct_body(&self, def: AdtDef, body: Body) -> Body {
251+
fn generate_struct_body(&self, def: AdtDef, args: GenericArgs, body: Body) -> Body {
250252
assert_eq!(def.num_variants(), 1);
251253

252254
let mut new_body = MutableBody::from(body);
253255
new_body.clear_body(TerminatorKind::Unreachable);
254256
let mut source = SourceInstruction::Terminator { bb: 0 };
255257

256258
let variant = def.variants()[0];
257-
self.call_kani_any_for_variant(def, &mut new_body, &mut source, variant);
259+
self.call_kani_any_for_variant(def, &args, &mut new_body, &mut source, variant);
258260

259261
new_body.into()
260262
}
261263
}
262264
/// Transform the dummy body of an automatic_harness Kani intrinsic to be a proof harness for a given function.
263265
#[derive(Debug)]
264266
pub struct AutomaticHarnessPass {
265-
/// The FnDef of KaniModel::Any
266267
kani_any: FnDef,
267268
init_contracts_hook: Instance,
268-
/// All of the automatic harness Instances that we generated in the CodegenUnits constructor
269-
automatic_harnesses: Vec<Instance>,
269+
kani_autoharness_intrinsic: FnDef,
270270
}
271271

272272
impl AutomaticHarnessPass {
273-
// FIXME: this is a bit clunky.
274-
// Historically, in codegen_crate, we reset the BodyTransformation cache on a per-unit basis,
275-
// so the BodyTransformation constructor only accepts a CodegenUnit and thus this constructor can only accept a unit.
276-
// Later, we changed codegen to reset the cache on a per-harness basis (for uninitialized memory instrumentation).
277-
// So BodyTransformation should really be changed to reflect that, so that this constructor can just save the one automatic harness it should transform
278-
// and not all of the possibilities.
279-
pub fn new(unit: &CodegenUnit, query_db: &QueryDb) -> Self {
273+
pub fn new(query_db: &QueryDb) -> Self {
280274
let kani_fns = query_db.kani_functions();
281-
let harness_intrinsic = *kani_fns.get(&KaniIntrinsic::AutomaticHarness.into()).unwrap();
275+
let kani_autoharness_intrinsic =
276+
*kani_fns.get(&KaniIntrinsic::AutomaticHarness.into()).unwrap();
282277
let kani_any = *kani_fns.get(&KaniModel::Any.into()).unwrap();
283278
let init_contracts_hook = *kani_fns.get(&KaniHook::InitContracts.into()).unwrap();
284279
let init_contracts_hook =
285280
Instance::resolve(init_contracts_hook, &GenericArgs(vec![])).unwrap();
286-
let automatic_harnesses = unit
287-
.harnesses
288-
.iter()
289-
.cloned()
290-
.filter(|harness| {
291-
let (def, _) = harness.ty().kind().fn_def().unwrap();
292-
def == harness_intrinsic
293-
})
294-
.collect::<Vec<_>>();
295-
Self { kani_any, init_contracts_hook, automatic_harnesses }
281+
Self { kani_any, init_contracts_hook, kani_autoharness_intrinsic }
296282
}
297283
}
298284

@@ -314,7 +300,7 @@ impl TransformPass for AutomaticHarnessPass {
314300
fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) {
315301
debug!(function=?instance.name(), "AutomaticHarnessPass::transform");
316302

317-
if !self.automatic_harnesses.contains(&instance) {
303+
if instance.def.def_id() != self.kani_autoharness_intrinsic.def_id() {
318304
return (false, body);
319305
}
320306

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl BodyTransformation {
7575
let safety_check_type = CheckType::new_safety_check_assert_assume(queries);
7676
let unsupported_check_type = CheckType::new_unsupported_check_assert_assume_false(queries);
7777
// This has to come first, since creating harnesses affects later stubbing and contract passes.
78-
transformer.add_pass(queries, AutomaticHarnessPass::new(unit, queries));
78+
transformer.add_pass(queries, AutomaticHarnessPass::new(queries));
7979
transformer.add_pass(queries, AutomaticArbitraryPass::new(unit, queries));
8080
transformer.add_pass(queries, FnStubPass::new(&unit.stubs));
8181
transformer.add_pass(queries, ExternFnStubPass::new(&unit.stubs));

0 commit comments

Comments
 (0)