diff options
| author | Tomasz Miąsko <tomasz.miasko@gmail.com> | 2022-01-14 00:00:00 +0000 |
|---|---|---|
| committer | Tomasz Miąsko <tomasz.miasko@gmail.com> | 2022-01-26 13:46:01 +0100 |
| commit | 10b722cc79c52a242d8671243b46e0319296cb30 (patch) | |
| tree | 26210348bba44429efd195a78a4f8d36470a9a1d /compiler/rustc_mir_build/src/lints.rs | |
| parent | 788b1fe5b79a8b74215022f9df49b0eae68a50b9 (diff) | |
| download | rust-10b722cc79c52a242d8671243b46e0319296cb30.tar.gz rust-10b722cc79c52a242d8671243b46e0319296cb30.zip | |
Ignore unwinding edges when checking for unconditional recursion
The unconditional recursion lint determines if all execution paths
eventually lead to a self-recursive call.
The implementation always follows unwinding edges which limits its
practical utility. For example, it would not lint function `f` because a
call to `g` might unwind. It also wouldn't lint function `h` because an
overflow check preceding the self-recursive call might unwind:
```rust
pub fn f() {
g();
f();
}
pub fn g() { /* ... */ }
pub fn h(a: usize) {
h(a + 1);
}
```
To avoid the issue, assume that terminators that might continue
execution along non-unwinding edges do so.
Diffstat (limited to 'compiler/rustc_mir_build/src/lints.rs')
| -rw-r--r-- | compiler/rustc_mir_build/src/lints.rs | 12 |
1 files changed, 8 insertions, 4 deletions
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index d348aaa899e..b21ca6028a2 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -33,6 +33,9 @@ crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) { return; } + if vis.reachable_recursive_calls.is_empty() { + return; + } vis.reachable_recursive_calls.sort(); @@ -148,13 +151,14 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { } fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { + let terminator = self.body[bb].terminator(); + if terminator.unwind() == Some(&Some(target)) && terminator.successors().count() > 1 { + return true; + } // Don't traverse successors of recursive calls or false CFG edges. match self.body[bb].terminator().kind { TerminatorKind::Call { ref func, .. } => self.is_recursive_call(func), - - TerminatorKind::FalseUnwind { unwind: Some(imaginary_target), .. } - | TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == target, - + TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == target, _ => false, } } |
