Skip to content

Commit b0b798e

Browse files
authored
Rollup merge of #100094 - lyming2007:issue-98982, r=estebank
Detect type mismatch due to loop that might never iterate When loop as tail expression causes a miss match type E0308 error, recursively get the return statement and add diagnostic information on it.
2 parents bb71929 + 9815667 commit b0b798e

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed

compiler/rustc_typeck/src/check/coercion.rs

+48-1
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@
3838
use crate::astconv::AstConv;
3939
use crate::check::FnCtxt;
4040
use rustc_errors::{
41-
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
41+
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
4242
};
4343
use rustc_hir as hir;
4444
use rustc_hir::def_id::DefId;
45+
use rustc_hir::intravisit::{self, Visitor};
46+
use rustc_hir::Expr;
4547
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
4648
use rustc_infer::infer::{Coercion, InferOk, InferResult};
4749
use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt};
@@ -87,6 +89,19 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
8789

8890
type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
8991

92+
struct CollectRetsVisitor<'tcx> {
93+
ret_exprs: Vec<&'tcx hir::Expr<'tcx>>,
94+
}
95+
96+
impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
97+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
98+
if let hir::ExprKind::Ret(_) = expr.kind {
99+
self.ret_exprs.push(expr);
100+
}
101+
intravisit::walk_expr(self, expr);
102+
}
103+
}
104+
90105
/// Coercing a mutable reference to an immutable works, while
91106
/// coercing `&T` to `&mut T` should be forbidden.
92107
fn coerce_mutbls<'tcx>(
@@ -1481,6 +1496,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
14811496

14821497
let mut err;
14831498
let mut unsized_return = false;
1499+
let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
14841500
match *cause.code() {
14851501
ObligationCauseCode::ReturnNoExpression => {
14861502
err = struct_span_err!(
@@ -1506,6 +1522,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15061522
if !fcx.tcx.features().unsized_locals {
15071523
unsized_return = self.is_return_ty_unsized(fcx, blk_id);
15081524
}
1525+
if let Some(expression) = expression
1526+
&& let hir::ExprKind::Loop(loop_blk, ..) = expression.kind {
1527+
intravisit::walk_block(& mut visitor, loop_blk);
1528+
}
15091529
}
15101530
ObligationCauseCode::ReturnValue(id) => {
15111531
err = self.report_return_mismatched_types(
@@ -1551,12 +1571,39 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15511571
);
15521572
}
15531573

1574+
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
1575+
self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
1576+
}
15541577
err.emit_unless(unsized_return);
15551578

15561579
self.final_ty = Some(fcx.tcx.ty_error());
15571580
}
15581581
}
15591582
}
1583+
fn note_unreachable_loop_return<'a>(
1584+
&self,
1585+
err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>,
1586+
expr: &hir::Expr<'tcx>,
1587+
ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
1588+
) {
1589+
let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;};
1590+
let mut span: MultiSpan = vec![loop_span].into();
1591+
span.push_span_label(loop_span, "this might have zero elements to iterate on".to_string());
1592+
for ret_expr in ret_exprs {
1593+
span.push_span_label(
1594+
ret_expr.span,
1595+
"if the loop doesn't execute, this value would never get returned".to_string(),
1596+
);
1597+
}
1598+
err.span_note(
1599+
span,
1600+
"the function expects a value to always be returned, but loops might run zero times",
1601+
);
1602+
err.help(
1603+
"return a value for the case when the loop has zero elements to iterate on, or \
1604+
consider changing the return type to account for that possibility",
1605+
);
1606+
}
15601607

15611608
fn report_return_mismatched_types<'a>(
15621609
&self,

src/test/ui/for-loop-while/break-while-condition.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ LL | | }
3131
|
3232
= note: expected type `!`
3333
found unit type `()`
34+
note: the function expects a value to always be returned, but loops might run zero times
35+
--> $DIR/break-while-condition.rs:24:13
36+
|
37+
LL | while false {
38+
| ^^^^^^^^^^^ this might have zero elements to iterate on
39+
LL | return
40+
| ------ if the loop doesn't execute, this value would never get returned
41+
= help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
3442

3543
error: aborting due to 3 previous errors
3644

src/test/ui/typeck/issue-98982.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn foo() -> i32 {
2+
for i in 0..0 {
3+
//~^ ERROR: mismatched types [E0308]
4+
return i;
5+
}
6+
//~| help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
7+
}
8+
9+
fn main() {}

src/test/ui/typeck/issue-98982.stderr

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-98982.rs:2:5
3+
|
4+
LL | fn foo() -> i32 {
5+
| --- expected `i32` because of return type
6+
LL | / for i in 0..0 {
7+
LL | |
8+
LL | | return i;
9+
LL | | }
10+
| |_____^ expected `i32`, found `()`
11+
|
12+
note: the function expects a value to always be returned, but loops might run zero times
13+
--> $DIR/issue-98982.rs:2:5
14+
|
15+
LL | for i in 0..0 {
16+
| ^^^^^^^^^^^^^ this might have zero elements to iterate on
17+
LL |
18+
LL | return i;
19+
| -------- if the loop doesn't execute, this value would never get returned
20+
= help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
21+
22+
error: aborting due to previous error
23+
24+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)