Skip to content

Commit 4058625

Browse files
Fix bug: Kani unwinds loops with contract in generic function (with -Z loop-contracts) (model-checking#4232)
This PR fixes the bug that even with -Z loop-contracts, Kani still unwind the loop if the loop is inside a generic function. 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 4c18d03 commit 4058625

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,19 @@ impl LoopContractPass {
661661
let TyKind::RigidTy(RigidTy::Closure(_, genarg)) = arg_ty.kind() else {
662662
return false;
663663
};
664-
let GenericArgKind::Type(arg_ty) = genarg.0[2] else { return false };
664+
// We look for the args' types of the kani_register_loop_contract function
665+
// They are always stored in a tuple, which is next to the FnPtr generic args of kani_registered_loop_contract fn
666+
// All the generic args before the FnPtr are from the outer function
667+
let mut fnptrpos = 0;
668+
for (i, arg) in genarg.0.iter().enumerate() {
669+
if let GenericArgKind::Type(arg_ty) = arg
670+
&& let TyKind::RigidTy(RigidTy::FnPtr(_)) = arg_ty.kind()
671+
{
672+
fnptrpos = i;
673+
break;
674+
}
675+
}
676+
let GenericArgKind::Type(arg_ty) = genarg.0[fnptrpos + 1] else { return false };
665677
let TyKind::RigidTy(RigidTy::Tuple(args)) = arg_ty.kind() else { return false };
666678
// Check if the invariant involves any local variable
667679
if !args.is_empty() {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
sum::<u8>.loop_invariant_base.1\
2+
- Status: SUCCESS\
3+
- Description: "Check invariant before entry for loop sum::<u8>.0"
4+
5+
main.assertion.1\
6+
- Status: SUCCESS\
7+
- Description: "assertion failed: j <= 2560"
8+
9+
VERIFICATION:- SUCCESSFUL
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// kani-flags: -Z loop-contracts
5+
6+
//! Check loop in generic function
7+
8+
#![feature(proc_macro_hygiene)]
9+
#![feature(stmt_expr_attributes)]
10+
11+
fn sum<T: Clone>(a: &[T]) -> u32
12+
where
13+
u32: std::convert::From<T>,
14+
{
15+
let mut j: u32 = 0;
16+
let mut i: usize = 0;
17+
#[kani::loop_invariant(i<=10 && j <= (u8::MAX as u32) * (i as u32))]
18+
while i < 10 {
19+
j = j + std::convert::Into::<u32>::into(a[i].clone());
20+
i = i + 1;
21+
}
22+
j
23+
}
24+
25+
#[kani::proof]
26+
fn main() {
27+
let a: [u8; 10] = kani::any();
28+
let j = sum(a.as_slice());
29+
assert!(j <= 2560);
30+
}

0 commit comments

Comments
 (0)