Skip to content

Commit 150ccd1

Browse files
Fix the bug of while loop invariant contains no local variables (#4022)
This PR fixes the bug of while loop invariant contains no local variables. Previously, for a while loop, if the loop invariant is `#[kani::loop_invariant(true)]`, we will get an error: "The assumptions for loop-contracts transformation are violated by some other transformation. Please report github.com/model-checking/kani/issues/new?template=bug_report.md" By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Michael Tautschnig <[email protected]>
1 parent 1495e73 commit 150ccd1

File tree

3 files changed

+37
-6
lines changed

3 files changed

+37
-6
lines changed

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use stable_mir::mir::{
1919
AggregateKind, BasicBlock, BasicBlockIdx, Body, ConstOperand, Operand, Rvalue, Statement,
2020
StatementKind, Terminator, TerminatorKind, VarDebugInfoContents,
2121
};
22-
use stable_mir::ty::{FnDef, MirConst, RigidTy, UintTy};
22+
use stable_mir::ty::{FnDef, GenericArgKind, MirConst, RigidTy, TyKind, UintTy};
2323
use std::collections::{HashMap, HashSet, VecDeque};
2424
use std::fmt::Debug;
2525

@@ -261,7 +261,7 @@ impl LoopContractPass {
261261
} = &terminator.kind
262262
{
263263
// Get the function signature of the terminator call.
264-
let Some(RigidTy::FnDef(fn_def, ..)) = terminator_func
264+
let Some(RigidTy::FnDef(fn_def, genarg)) = terminator_func
265265
.ty(new_body.locals())
266266
.ok()
267267
.map(|fn_ty| fn_ty.kind().rigid().unwrap().clone())
@@ -286,10 +286,18 @@ impl LoopContractPass {
286286
Please report github.com/model-checking/kani/issues/new?template=bug_report.md"
287287
);
288288
}
289-
290-
let ori_condition_bb_idx =
291-
new_body.blocks()[terminator_target.unwrap()].terminator.successors()[1];
292-
self.make_invariant_closure_alive(new_body, ori_condition_bb_idx);
289+
let GenericArgKind::Type(arg_ty) = genarg.0[0] else { return false };
290+
let TyKind::RigidTy(RigidTy::Closure(_, genarg)) = arg_ty.kind() else {
291+
return false;
292+
};
293+
let GenericArgKind::Type(arg_ty) = genarg.0[2] else { return false };
294+
let TyKind::RigidTy(RigidTy::Tuple(args)) = arg_ty.kind() else { return false };
295+
// Check if the invariant involves any local variable
296+
if !args.is_empty() {
297+
let ori_condition_bb_idx =
298+
new_body.blocks()[terminator_target.unwrap()].terminator.successors()[1];
299+
self.make_invariant_closure_alive(new_body, ori_condition_bb_idx);
300+
}
293301

294302
contain_loop_contracts = true;
295303

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
main.loop_invariant_base.1\
2+
- Status: SUCCESS\
3+
- Description: "Check invariant before entry for loop main.0"
4+
5+
VERIFICATION:- SUCCESSFUL
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// kani-flags: -Z loop-contracts
5+
6+
//! Check the use of "true" in loop invariant
7+
8+
#![feature(stmt_expr_attributes)]
9+
#![feature(proc_macro_hygiene)]
10+
11+
#[kani::proof]
12+
fn main(){
13+
let mut i = 100;
14+
#[kani::loop_invariant(true)]
15+
while i > 1 {
16+
i /= 2;
17+
}
18+
}

0 commit comments

Comments
 (0)