Skip to content

Commit 034fa91

Browse files
committed
Fix unconditional recursion lint wrt tail calls
1 parent 9fe31e6 commit 034fa91

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
@@ -194,8 +194,6 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx
194194
| TerminatorKind::CoroutineDrop
195195
| TerminatorKind::UnwindResume
196196
| TerminatorKind::Return
197-
// FIXME(explicit_tail_calls) Is this right??
198-
| TerminatorKind::TailCall { .. }
199197
| TerminatorKind::Unreachable
200198
| TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive),
201199

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

222227
fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow<Self::BreakVal> {
223228
// When we examine a node for the last time, remember it if it is a recursive call.
224229
let terminator = self.body[bb].terminator();
230+
231+
// FIXME(explicit_tail_calls): highlight tail calls as "recursive call site"
232+
//
233+
// We don't want to lint functions that recurse only through tail calls
234+
// (such as `fn g() { become () }`), so just adding `| TailCall { ... }`
235+
// here won't work.
236+
//
237+
// But at the same time we would like to highlight both calls in a function like
238+
// `fn f() { if false { become f() } else { f() } }`, so we need to figure something out.
225239
if self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) {
226240
self.reachable_recursive_calls.push(terminator.source_info.span);
227241
}
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 previous error
18+

0 commit comments

Comments
 (0)