Skip to content

Commit 21b4a9c

Browse files
committed
Auto merge of rust-lang#92889 - tmiasko:unbounded-recursion, r=ecstatic-morse
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. Fixes rust-lang#78474.
2 parents 563250a + 10b722c commit 21b4a9c

File tree

5 files changed

+108
-7
lines changed

5 files changed

+108
-7
lines changed

compiler/rustc_mir_build/src/lints.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
3333
if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) {
3434
return;
3535
}
36+
if vis.reachable_recursive_calls.is_empty() {
37+
return;
38+
}
3639

3740
vis.reachable_recursive_calls.sort();
3841

@@ -148,13 +151,14 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
148151
}
149152

150153
fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool {
154+
let terminator = self.body[bb].terminator();
155+
if terminator.unwind() == Some(&Some(target)) && terminator.successors().count() > 1 {
156+
return true;
157+
}
151158
// Don't traverse successors of recursive calls or false CFG edges.
152159
match self.body[bb].terminator().kind {
153160
TerminatorKind::Call { ref func, .. } => self.is_recursive_call(func),
154-
155-
TerminatorKind::FalseUnwind { unwind: Some(imaginary_target), .. }
156-
| TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == target,
157-
161+
TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == target,
158162
_ => false,
159163
}
160164
}

src/test/ui/lint/lint-unconditional-recursion.rs

+42
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,46 @@ pub fn panics(x: bool) {
149149
}
150150
}
151151

152+
pub fn unreachable1() {
153+
panic!();
154+
unreachable1(); // WARN unreachable statement
155+
}
156+
157+
pub fn unreachable2() {
158+
loop {}
159+
unreachable2(); // WARN unreachable statement
160+
}
161+
162+
pub fn drop_and_replace(mut a: Option<String>) { //~ ERROR function cannot return without recursing
163+
a = None;
164+
drop_and_replace(a);
165+
}
166+
167+
// Calls are assumed to return normally.
168+
pub fn call() -> String { //~ ERROR function cannot return without recursing
169+
let s = String::new();
170+
call();
171+
s
172+
}
173+
174+
// Arithmetic operations are assumed not to overflow.
175+
pub fn overflow_check(a: i32, b: i32) { //~ ERROR function cannot return without recursing
176+
let _ = a + b;
177+
overflow_check(a, b);
178+
}
179+
180+
pub struct Point {
181+
pub x: f32,
182+
pub y: f32,
183+
}
184+
185+
impl Default for Point {
186+
fn default() -> Self { //~ ERROR function cannot return without recursing
187+
Point {
188+
x: Default::default(),
189+
..Default::default()
190+
}
191+
}
192+
}
193+
152194
fn main() {}

src/test/ui/lint/lint-unconditional-recursion.stderr

+45-1
Original file line numberDiff line numberDiff line change
@@ -153,5 +153,49 @@ LL | self.as_ref()
153153
|
154154
= help: a `loop` may express intention better if this is on purpose
155155

156-
error: aborting due to 14 previous errors
156+
error: function cannot return without recursing
157+
--> $DIR/lint-unconditional-recursion.rs:162:1
158+
|
159+
LL | pub fn drop_and_replace(mut a: Option<String>) {
160+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
161+
LL | a = None;
162+
LL | drop_and_replace(a);
163+
| ------------------- recursive call site
164+
|
165+
= help: a `loop` may express intention better if this is on purpose
166+
167+
error: function cannot return without recursing
168+
--> $DIR/lint-unconditional-recursion.rs:168:1
169+
|
170+
LL | pub fn call() -> String {
171+
| ^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
172+
LL | let s = String::new();
173+
LL | call();
174+
| ------ recursive call site
175+
|
176+
= help: a `loop` may express intention better if this is on purpose
177+
178+
error: function cannot return without recursing
179+
--> $DIR/lint-unconditional-recursion.rs:175:1
180+
|
181+
LL | pub fn overflow_check(a: i32, b: i32) {
182+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
183+
LL | let _ = a + b;
184+
LL | overflow_check(a, b);
185+
| -------------------- recursive call site
186+
|
187+
= help: a `loop` may express intention better if this is on purpose
188+
189+
error: function cannot return without recursing
190+
--> $DIR/lint-unconditional-recursion.rs:186:5
191+
|
192+
LL | fn default() -> Self {
193+
| ^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
194+
...
195+
LL | ..Default::default()
196+
| ------------------ recursive call site
197+
|
198+
= help: a `loop` may express intention better if this is on purpose
199+
200+
error: aborting due to 18 previous errors
157201

src/test/ui/recursion/issue-83150.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ fn main() {
66
func(&mut iter)
77
}
88

9-
fn func<T: Iterator<Item = u8>>(iter: &mut T) {
9+
fn func<T: Iterator<Item = u8>>(iter: &mut T) { //~ WARN function cannot return without recursing
1010
func(&mut iter.map(|x| x + 1))
1111
}
+12-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1+
warning: function cannot return without recursing
2+
--> $DIR/issue-83150.rs:9:1
3+
|
4+
LL | fn func<T: Iterator<Item = u8>>(iter: &mut T) {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
6+
LL | func(&mut iter.map(|x| x + 1))
7+
| ------------------------------ recursive call site
8+
|
9+
= note: `#[warn(unconditional_recursion)]` on by default
10+
= help: a `loop` may express intention better if this is on purpose
11+
112
error[E0275]: overflow evaluating the requirement `Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range<u8>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>: Iterator`
213
|
314
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`)
415
= note: required because of the requirements on the impl of `Iterator` for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range<u8>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>`
516

6-
error: aborting due to previous error
17+
error: aborting due to previous error; 1 warning emitted
718

819
For more information about this error, try `rustc --explain E0275`.

0 commit comments

Comments
 (0)