Skip to content

Commit 538951b

Browse files
committed
Fix unconditional recursion lint wrt tail calls
1 parent 5d5c421 commit 538951b

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

compiler/rustc_mir_build/src/lints.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,6 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx
196196
| TerminatorKind::CoroutineDrop
197197
| TerminatorKind::UnwindResume
198198
| TerminatorKind::Return
199-
// FIXME(explicit_tail_calls) Is this right??
200-
| TerminatorKind::TailCall { .. }
201199
| TerminatorKind::Unreachable
202200
| TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive),
203201

@@ -218,12 +216,28 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx
218216
| TerminatorKind::FalseUnwind { .. }
219217
| TerminatorKind::Goto { .. }
220218
| TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()),
219+
220+
// Note that tail call terminator technically returns to the caller,
221+
// but for purposes of this lint it makes sense to count it as possibly recursive,
222+
// since it's still a call.
223+
//
224+
// If this'll be repurposed for something else, this might need to be changed.
225+
TerminatorKind::TailCall { .. } => ControlFlow::Continue(()),
221226
}
222227
}
223228

224229
fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow<Self::BreakVal> {
225230
// When we examine a node for the last time, remember it if it is a recursive call.
226231
let terminator = self.body[bb].terminator();
232+
233+
// FIXME(explicit_tail_calls): highlight tail calls as "recursive call site"
234+
//
235+
// We don't want to lint functions that recurse only through tail calls
236+
// (such as `fn g() { become () }`), so just adding `| TailCall { ... }`
237+
// here won't work.
238+
//
239+
// But at the same time we would like to highlight both calls in a function like
240+
// `fn f() { if false { become f() } else { f() } }`, so we need to figure something out.
227241
if self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) {
228242
self.reachable_recursive_calls.push(terminator.source_info.span);
229243
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#![allow(incomplete_features, dead_code)]
2+
#![deny(unconditional_recursion)] //~ note: the lint level is defined here
3+
#![feature(explicit_tail_calls)]
4+
5+
fn f(x: bool) {
6+
//~^ error: function cannot return without recursing
7+
//~| note: cannot return without recursing
8+
if x {
9+
become f(!x)
10+
} else {
11+
f(!x) //~ note: recursive call site
12+
}
13+
}
14+
15+
// This should *not* lint, tail-recursive functions which never return is a reasonable thing
16+
fn g(x: bool) {
17+
if x {
18+
become g(!x)
19+
} else {
20+
become g(!x)
21+
}
22+
}
23+
24+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: function cannot return without recursing
2+
--> $DIR/lint-unconditional-recursion-tail-calls.rs:5:1
3+
|
4+
LL | fn f(x: bool) {
5+
| ^^^^^^^^^^^^^ cannot return without recursing
6+
...
7+
LL | f(!x)
8+
| ----- recursive call site
9+
|
10+
= help: a `loop` may express intention better if this is on purpose
11+
note: the lint level is defined here
12+
--> $DIR/lint-unconditional-recursion-tail-calls.rs:2:9
13+
|
14+
LL | #![deny(unconditional_recursion)]
15+
| ^^^^^^^^^^^^^^^^^^^^^^^
16+
17+
error: aborting due to 1 previous error
18+

0 commit comments

Comments
 (0)