about summary refs log tree commit diff
path: root/compiler/rustc_mir_build/src/lints.rs
diff options
context:
space:
mode:
authorTomasz Miąsko <tomasz.miasko@gmail.com>2022-01-14 00:00:00 +0000
committerTomasz Miąsko <tomasz.miasko@gmail.com>2022-01-26 13:46:01 +0100
commit10b722cc79c52a242d8671243b46e0319296cb30 (patch)
tree26210348bba44429efd195a78a4f8d36470a9a1d /compiler/rustc_mir_build/src/lints.rs
parent788b1fe5b79a8b74215022f9df49b0eae68a50b9 (diff)
downloadrust-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.rs12
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,
         }
     }