about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs155
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs89
-rw-r--r--tests/ui/coercion/coerce-loop-issue-122561.rs110
-rw-r--r--tests/ui/coercion/coerce-loop-issue-122561.stderr299
-rw-r--r--tests/ui/did_you_mean/compatible-variants.stderr3
-rw-r--r--tests/ui/for-loop-while/break-while-condition.stderr18
-rw-r--r--tests/ui/issues/issue-27042.stderr2
-rw-r--r--tests/ui/issues/issue-50585.stderr6
-rw-r--r--tests/ui/typeck/issue-100285.rs4
-rw-r--r--tests/ui/typeck/issue-100285.stderr39
-rw-r--r--tests/ui/typeck/issue-98982.rs4
-rw-r--r--tests/ui/typeck/issue-98982.stderr18
13 files changed, 545 insertions, 205 deletions
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 4165fa7f07d..8de9402af20 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -37,11 +37,9 @@
 
 use crate::errors::SuggestBoxingForReturnImplTrait;
 use crate::FnCtxt;
-use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
+use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::Expr;
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
 use rustc_infer::infer::type_variable::TypeVariableOrigin;
 use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -95,22 +93,6 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
 
 type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
 
-struct CollectRetsVisitor<'tcx> {
-    ret_exprs: Vec<&'tcx hir::Expr<'tcx>>,
-}
-
-impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
-    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-        match expr.kind {
-            hir::ExprKind::Ret(_) => self.ret_exprs.push(expr),
-            // `return` in closures does not return from the outer function
-            hir::ExprKind::Closure(_) => return,
-            _ => {}
-        }
-        intravisit::walk_expr(self, expr);
-    }
-}
-
 /// Coercing a mutable reference to an immutable works, while
 /// coercing `&T` to `&mut T` should be forbidden.
 fn coerce_mutbls<'tcx>(
@@ -1601,7 +1583,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
 
                 let mut err;
                 let mut unsized_return = false;
-                let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
                 match *cause.code() {
                     ObligationCauseCode::ReturnNoExpression => {
                         err = struct_span_code_err!(
@@ -1636,11 +1617,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                         if !fcx.tcx.features().unsized_locals {
                             unsized_return = self.is_return_ty_definitely_unsized(fcx);
                         }
-                        if let Some(expression) = expression
-                            && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind
-                        {
-                            intravisit::walk_block(&mut visitor, loop_blk);
-                        }
                     }
                     ObligationCauseCode::ReturnValue(id) => {
                         err = self.report_return_mismatched_types(
@@ -1741,6 +1717,22 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                 augment_error(&mut err);
 
                 if let Some(expr) = expression {
+                    if let hir::ExprKind::Loop(
+                        _,
+                        _,
+                        loop_src @ (hir::LoopSource::While | hir::LoopSource::ForLoop),
+                        _,
+                    ) = expr.kind
+                    {
+                        let loop_type = if loop_src == hir::LoopSource::While {
+                            "`while` loops"
+                        } else {
+                            "`for` loops"
+                        };
+
+                        err.note(format!("{loop_type} evaluate to unit type `()`"));
+                    }
+
                     fcx.emit_coerce_suggestions(
                         &mut err,
                         expr,
@@ -1749,15 +1741,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                         None,
                         Some(coercion_error),
                     );
-                    if visitor.ret_exprs.len() > 0 {
-                        self.note_unreachable_loop_return(
-                            &mut err,
-                            fcx.tcx,
-                            &expr,
-                            &visitor.ret_exprs,
-                            expected,
-                        );
-                    }
                 }
 
                 let reported = err.emit_unless(unsized_return);
@@ -1831,110 +1814,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         );
     }
 
-    fn note_unreachable_loop_return(
-        &self,
-        err: &mut Diag<'_>,
-        tcx: TyCtxt<'tcx>,
-        expr: &hir::Expr<'tcx>,
-        ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
-        ty: Ty<'tcx>,
-    ) {
-        let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else {
-            return;
-        };
-        let mut span: MultiSpan = vec![loop_span].into();
-        span.push_span_label(loop_span, "this might have zero elements to iterate on");
-        const MAXITER: usize = 3;
-        let iter = ret_exprs.iter().take(MAXITER);
-        for ret_expr in iter {
-            span.push_span_label(
-                ret_expr.span,
-                "if the loop doesn't execute, this value would never get returned",
-            );
-        }
-        err.span_note(
-            span,
-            "the function expects a value to always be returned, but loops might run zero times",
-        );
-        if MAXITER < ret_exprs.len() {
-            err.note(format!(
-                "if the loop doesn't execute, {} other values would never get returned",
-                ret_exprs.len() - MAXITER
-            ));
-        }
-        let hir = tcx.hir();
-        let item = hir.get_parent_item(expr.hir_id);
-        let ret_msg = "return a value for the case when the loop has zero elements to iterate on";
-        let ret_ty_msg =
-            "otherwise consider changing the return type to account for that possibility";
-        let node = tcx.hir_node(item.into());
-        if let Some(body_id) = node.body_id()
-            && let Some(sig) = node.fn_sig()
-            && let hir::ExprKind::Block(block, _) = hir.body(body_id).value.kind
-            && !ty.is_never()
-        {
-            let indentation = if let None = block.expr
-                && let [.., last] = &block.stmts
-            {
-                tcx.sess.source_map().indentation_before(last.span).unwrap_or_else(String::new)
-            } else if let Some(expr) = block.expr {
-                tcx.sess.source_map().indentation_before(expr.span).unwrap_or_else(String::new)
-            } else {
-                String::new()
-            };
-            if let None = block.expr
-                && let [.., last] = &block.stmts
-            {
-                err.span_suggestion_verbose(
-                    last.span.shrink_to_hi(),
-                    ret_msg,
-                    format!("\n{indentation}/* `{ty}` value */"),
-                    Applicability::MaybeIncorrect,
-                );
-            } else if let Some(expr) = block.expr {
-                err.span_suggestion_verbose(
-                    expr.span.shrink_to_hi(),
-                    ret_msg,
-                    format!("\n{indentation}/* `{ty}` value */"),
-                    Applicability::MaybeIncorrect,
-                );
-            }
-            let mut sugg = match sig.decl.output {
-                hir::FnRetTy::DefaultReturn(span) => {
-                    vec![(span, " -> Option<()>".to_string())]
-                }
-                hir::FnRetTy::Return(ty) => {
-                    vec![
-                        (ty.span.shrink_to_lo(), "Option<".to_string()),
-                        (ty.span.shrink_to_hi(), ">".to_string()),
-                    ]
-                }
-            };
-            for ret_expr in ret_exprs {
-                match ret_expr.kind {
-                    hir::ExprKind::Ret(Some(expr)) => {
-                        sugg.push((expr.span.shrink_to_lo(), "Some(".to_string()));
-                        sugg.push((expr.span.shrink_to_hi(), ")".to_string()));
-                    }
-                    hir::ExprKind::Ret(None) => {
-                        sugg.push((ret_expr.span.shrink_to_hi(), " Some(())".to_string()));
-                    }
-                    _ => {}
-                }
-            }
-            if let None = block.expr
-                && let [.., last] = &block.stmts
-            {
-                sugg.push((last.span.shrink_to_hi(), format!("\n{indentation}None")));
-            } else if let Some(expr) = block.expr {
-                sugg.push((expr.span.shrink_to_hi(), format!("\n{indentation}None")));
-            }
-            err.multipart_suggestion(ret_ty_msg, sugg, Applicability::MaybeIncorrect);
-        } else {
-            err.help(format!("{ret_msg}, {ret_ty_msg}"));
-        }
-    }
-
     fn report_return_mismatched_types<'a>(
         &self,
         cause: &ObligationCause<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 618d90a07ec..60c0b1872fa 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -57,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             || self.suggest_into(err, expr, expr_ty, expected)
             || self.suggest_floating_point_literal(err, expr, expected)
             || self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
-            || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty);
+            || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty)
+            || self.suggest_returning_value_after_loop(err, expr, expected);
 
         if !suggested {
             self.note_source_of_type_mismatch_constraint(
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 133778a416c..d00fea4fae4 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -18,8 +18,8 @@ use rustc_hir::def::Res;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{
-    CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node,
-    Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
+    Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId,
+    Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
 };
 use rustc_hir_analysis::collect::suggest_impl_trait;
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
@@ -1942,6 +1942,91 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         false
     }
 
+    // If the expr is a while or for loop and is the tail expr of its
+    // enclosing body suggest returning a value right after it
+    pub fn suggest_returning_value_after_loop(
+        &self,
+        err: &mut Diag<'_>,
+        expr: &hir::Expr<'tcx>,
+        expected: Ty<'tcx>,
+    ) -> bool {
+        let hir = self.tcx.hir();
+        let enclosing_scope =
+            hir.get_enclosing_scope(expr.hir_id).map(|hir_id| self.tcx.hir_node(hir_id));
+
+        // Get tail expr of the enclosing block or body
+        let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
+            && expr.is_some()
+        {
+            *expr
+        } else {
+            let body_def_id = hir.enclosing_body_owner(expr.hir_id);
+            let body_id = hir.body_owned_by(body_def_id);
+            let body = hir.body(body_id);
+
+            // Get tail expr of the body
+            match body.value.kind {
+                // Regular function body etc.
+                hir::ExprKind::Block(block, _) => block.expr,
+                // Anon const body (there's no block in this case)
+                hir::ExprKind::DropTemps(expr) => Some(expr),
+                _ => None,
+            }
+        };
+
+        let Some(tail_expr) = tail_expr else {
+            return false; // Body doesn't have a tail expr we can compare with
+        };
+
+        // Get the loop expr within the tail expr
+        let loop_expr_in_tail = match expr.kind {
+            hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
+            hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
+                match tail_expr.peel_drop_temps() {
+                    Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
+                    _ => return false, // Not really a for loop
+                }
+            }
+            _ => return false, // Not a while or a for loop
+        };
+
+        // If the expr is the loop expr in the tail
+        // then make the suggestion
+        if expr.hir_id == loop_expr_in_tail.hir_id {
+            let span = expr.span;
+
+            let (msg, suggestion) = if expected.is_never() {
+                (
+                    "consider adding a diverging expression here",
+                    "`loop {}` or `panic!(\"...\")`".to_string(),
+                )
+            } else {
+                ("consider returning a value here", format!("`{expected}` value"))
+            };
+
+            let src_map = self.tcx.sess.source_map();
+            let suggestion = if src_map.is_multiline(expr.span) {
+                let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
+                format!("\n{indentation}/* {suggestion} */")
+            } else {
+                // If the entire expr is on a single line
+                // put the suggestion also on the same line
+                format!(" /* {suggestion} */")
+            };
+
+            err.span_suggestion_verbose(
+                span.shrink_to_hi(),
+                msg,
+                suggestion,
+                Applicability::MaybeIncorrect,
+            );
+
+            true
+        } else {
+            false
+        }
+    }
+
     /// If the expected type is an enum (Issue #55250) with any variants whose
     /// sole field is of the found type, suggest such variants. (Issue #42764)
     pub(crate) fn suggest_compatible_variants(
diff --git a/tests/ui/coercion/coerce-loop-issue-122561.rs b/tests/ui/coercion/coerce-loop-issue-122561.rs
new file mode 100644
index 00000000000..e08884ad6a4
--- /dev/null
+++ b/tests/ui/coercion/coerce-loop-issue-122561.rs
@@ -0,0 +1,110 @@
+// Regression test for #122561
+
+fn for_infinite() -> bool {
+    for i in 0.. {
+    //~^ ERROR mismatched types
+        return false;
+    }
+}
+
+fn for_finite() -> String {
+    for i in 0..5 {
+    //~^ ERROR mismatched types
+        return String::from("test");
+    }
+}
+
+fn for_zero_times() -> bool {
+    for i in 0..0 {
+    //~^ ERROR mismatched types
+        return true;
+    }
+}
+
+fn for_never_type() -> ! {
+    for i in 0..5 {
+    //~^ ERROR mismatched types
+    }
+}
+
+// Entire function on a single line.
+// Tests that we format the suggestion
+// correctly in this case
+fn for_single_line() -> bool { for i in 0.. { return false; } }
+//~^ ERROR mismatched types
+
+// Loop in an anon const in function args
+// Tests that we:
+// a. deal properly with this complex case
+// b. format the suggestion correctly so
+//    that it's readable
+fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool {
+//~^ ERROR `for` is not allowed in a `const`
+//~| ERROR mismatched types
+    true
+}
+
+fn while_inifinite() -> bool {
+    while true {
+    //~^ ERROR mismatched types
+    //~| WARN denote infinite loops with `loop { ... }` [while_true]
+        return true;
+    }
+}
+
+fn while_finite() -> bool {
+    let mut i = 0;
+    while i < 3 {
+    //~^ ERROR mismatched types
+        i += 1;
+        return true;
+    }
+}
+
+fn while_zero_times() -> bool {
+    while false {
+    //~^ ERROR mismatched types
+        return true;
+    }
+}
+
+fn while_never_type() -> ! {
+    while true {
+    //~^ ERROR mismatched types
+    //~| WARN denote infinite loops with `loop { ... }` [while_true]
+    }
+}
+
+// No type mismatch error in this case
+fn loop_() -> bool {
+    loop {
+        return true;
+    }
+}
+
+const C: i32 = {
+    for i in 0.. {
+    //~^ ERROR `for` is not allowed in a `const`
+    //~| ERROR mismatched types
+    }
+};
+
+fn main() {
+    let _ = [10; {
+        for i in 0..5 {
+        //~^ ERROR `for` is not allowed in a `const`
+        //~| ERROR mismatched types
+        }
+    }];
+
+    let _ = [10; {
+        while false {
+        //~^ ERROR mismatched types
+        }
+    }];
+
+
+    let _ = |a: &[(); for x in 0..2 {}]| {};
+    //~^ ERROR `for` is not allowed in a `const`
+    //~| ERROR mismatched types
+}
diff --git a/tests/ui/coercion/coerce-loop-issue-122561.stderr b/tests/ui/coercion/coerce-loop-issue-122561.stderr
new file mode 100644
index 00000000000..0f77fd1364d
--- /dev/null
+++ b/tests/ui/coercion/coerce-loop-issue-122561.stderr
@@ -0,0 +1,299 @@
+warning: denote infinite loops with `loop { ... }`
+  --> $DIR/coerce-loop-issue-122561.rs:48:5
+   |
+LL |     while true {
+   |     ^^^^^^^^^^ help: use `loop`
+   |
+   = note: `#[warn(while_true)]` on by default
+
+warning: denote infinite loops with `loop { ... }`
+  --> $DIR/coerce-loop-issue-122561.rs:72:5
+   |
+LL |     while true {
+   |     ^^^^^^^^^^ help: use `loop`
+
+error[E0658]: `for` is not allowed in a `const`
+  --> $DIR/coerce-loop-issue-122561.rs:41:24
+   |
+LL | fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool {
+   |                        ^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #87575 <https://github.com/rust-lang/rust/issues/87575> for more information
+   = help: add `#![feature(const_for)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `for` is not allowed in a `const`
+  --> $DIR/coerce-loop-issue-122561.rs:86:5
+   |
+LL | /     for i in 0.. {
+LL | |
+LL | |
+LL | |     }
+   | |_____^
+   |
+   = note: see issue #87575 <https://github.com/rust-lang/rust/issues/87575> for more information
+   = help: add `#![feature(const_for)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `for` is not allowed in a `const`
+  --> $DIR/coerce-loop-issue-122561.rs:94:9
+   |
+LL | /         for i in 0..5 {
+LL | |
+LL | |
+LL | |         }
+   | |_________^
+   |
+   = note: see issue #87575 <https://github.com/rust-lang/rust/issues/87575> for more information
+   = help: add `#![feature(const_for)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `for` is not allowed in a `const`
+  --> $DIR/coerce-loop-issue-122561.rs:107:23
+   |
+LL |     let _ = |a: &[(); for x in 0..2 {}]| {};
+   |                       ^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #87575 <https://github.com/rust-lang/rust/issues/87575> for more information
+   = help: add `#![feature(const_for)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:41:24
+   |
+LL | fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool {
+   |                        ^^^^^^^^^^^^^^^^ expected `usize`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL | fn for_in_arg(a: &[(); for x in 0..2 {} /* `usize` value */]) -> bool {
+   |                                         +++++++++++++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:86:5
+   |
+LL | /     for i in 0.. {
+LL | |
+LL | |
+LL | |     }
+   | |_____^ expected `i32`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~     }
+LL +     /* `i32` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:4:5
+   |
+LL |   fn for_infinite() -> bool {
+   |                        ---- expected `bool` because of return type
+LL | /     for i in 0.. {
+LL | |
+LL | |         return false;
+LL | |     }
+   | |_____^ expected `bool`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~     }
+LL +     /* `bool` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:11:5
+   |
+LL |   fn for_finite() -> String {
+   |                      ------ expected `String` because of return type
+LL | /     for i in 0..5 {
+LL | |
+LL | |         return String::from("test");
+LL | |     }
+   | |_____^ expected `String`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~     }
+LL +     /* `String` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:18:5
+   |
+LL |   fn for_zero_times() -> bool {
+   |                          ---- expected `bool` because of return type
+LL | /     for i in 0..0 {
+LL | |
+LL | |         return true;
+LL | |     }
+   | |_____^ expected `bool`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~     }
+LL +     /* `bool` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:25:5
+   |
+LL |   fn for_never_type() -> ! {
+   |                          - expected `!` because of return type
+LL | /     for i in 0..5 {
+LL | |
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+   = note: `for` loops evaluate to unit type `()`
+help: consider adding a diverging expression here
+   |
+LL ~     }
+LL +     /* `loop {}` or `panic!("...")` */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:33:32
+   |
+LL | fn for_single_line() -> bool { for i in 0.. { return false; } }
+   |                         ----   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
+   |                         |
+   |                         expected `bool` because of return type
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL | fn for_single_line() -> bool { for i in 0.. { return false; } /* `bool` value */ }
+   |                                                               ++++++++++++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:48:5
+   |
+LL |   fn while_inifinite() -> bool {
+   |                           ---- expected `bool` because of return type
+LL | /     while true {
+LL | |
+LL | |
+LL | |         return true;
+LL | |     }
+   | |_____^ expected `bool`, found `()`
+   |
+   = note: `while` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~     }
+LL +     /* `bool` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:57:5
+   |
+LL |   fn while_finite() -> bool {
+   |                        ---- expected `bool` because of return type
+LL |       let mut i = 0;
+LL | /     while i < 3 {
+LL | |
+LL | |         i += 1;
+LL | |         return true;
+LL | |     }
+   | |_____^ expected `bool`, found `()`
+   |
+   = note: `while` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~     }
+LL +     /* `bool` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:65:5
+   |
+LL |   fn while_zero_times() -> bool {
+   |                            ---- expected `bool` because of return type
+LL | /     while false {
+LL | |
+LL | |         return true;
+LL | |     }
+   | |_____^ expected `bool`, found `()`
+   |
+   = note: `while` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~     }
+LL +     /* `bool` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:72:5
+   |
+LL |   fn while_never_type() -> ! {
+   |                            - expected `!` because of return type
+LL | /     while true {
+LL | |
+LL | |
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+   = note: `while` loops evaluate to unit type `()`
+help: consider adding a diverging expression here
+   |
+LL ~     }
+LL +     /* `loop {}` or `panic!("...")` */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:94:9
+   |
+LL | /         for i in 0..5 {
+LL | |
+LL | |
+LL | |         }
+   | |_________^ expected `usize`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~         }
+LL +         /* `usize` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:101:9
+   |
+LL | /         while false {
+LL | |
+LL | |         }
+   | |_________^ expected `usize`, found `()`
+   |
+   = note: `while` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL ~         }
+LL +         /* `usize` value */
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-loop-issue-122561.rs:107:23
+   |
+LL |     let _ = |a: &[(); for x in 0..2 {}]| {};
+   |                       ^^^^^^^^^^^^^^^^ expected `usize`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL |     let _ = |a: &[(); for x in 0..2 {} /* `usize` value */]| {};
+   |                                        +++++++++++++++++++
+
+error: aborting due to 18 previous errors; 2 warnings emitted
+
+Some errors have detailed explanations: E0308, E0658.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/did_you_mean/compatible-variants.stderr b/tests/ui/did_you_mean/compatible-variants.stderr
index f2bbd8ced8f..2c75537ca19 100644
--- a/tests/ui/did_you_mean/compatible-variants.stderr
+++ b/tests/ui/did_you_mean/compatible-variants.stderr
@@ -11,6 +11,7 @@ LL | |     }
    |
    = note:   expected enum `Option<()>`
            found unit type `()`
+   = note: `while` loops evaluate to unit type `()`
 help: try adding an expression at the end of the block
    |
 LL ~     }
@@ -49,6 +50,7 @@ LL | |     }
    |
    = note:   expected enum `Option<()>`
            found unit type `()`
+   = note: `for` loops evaluate to unit type `()`
 help: try adding an expression at the end of the block
    |
 LL ~     }
@@ -106,6 +108,7 @@ LL |         while false {}
    |
    = note:   expected enum `Option<()>`
            found unit type `()`
+   = note: `while` loops evaluate to unit type `()`
 help: try adding an expression at the end of the block
    |
 LL ~         while false {}
diff --git a/tests/ui/for-loop-while/break-while-condition.stderr b/tests/ui/for-loop-while/break-while-condition.stderr
index 48b29f44fa1..07a57424575 100644
--- a/tests/ui/for-loop-while/break-while-condition.stderr
+++ b/tests/ui/for-loop-while/break-while-condition.stderr
@@ -20,6 +20,12 @@ LL | |             }
    |
    = note:   expected type `!`
            found unit type `()`
+   = note: `while` loops evaluate to unit type `()`
+help: consider adding a diverging expression here
+   |
+LL ~             }
+LL +             /* `loop {}` or `panic!("...")` */
+   |
 
 error[E0308]: mismatched types
   --> $DIR/break-while-condition.rs:24:13
@@ -31,14 +37,12 @@ LL | |             }
    |
    = note:   expected type `!`
            found unit type `()`
-note: the function expects a value to always be returned, but loops might run zero times
-  --> $DIR/break-while-condition.rs:24:13
+   = note: `while` loops evaluate to unit type `()`
+help: consider adding a diverging expression here
+   |
+LL ~             }
+LL +             /* `loop {}` or `panic!("...")` */
    |
-LL |             while false {
-   |             ^^^^^^^^^^^ this might have zero elements to iterate on
-LL |                 return
-   |                 ------ if the loop doesn't execute, this value would never get returned
-   = help: return a value for the case when the loop has zero elements to iterate on, otherwise consider changing the return type to account for that possibility
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/issues/issue-27042.stderr b/tests/ui/issues/issue-27042.stderr
index ba39399e46e..6586e61f2f6 100644
--- a/tests/ui/issues/issue-27042.stderr
+++ b/tests/ui/issues/issue-27042.stderr
@@ -40,6 +40,8 @@ error[E0308]: mismatched types
 LL | /         'c:
 LL | |         for _ in None { break }; // but here we cite the whole loop
    | |_______________________________^ expected `i32`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
 
 error[E0308]: mismatched types
   --> $DIR/issue-27042.rs:15:9
diff --git a/tests/ui/issues/issue-50585.stderr b/tests/ui/issues/issue-50585.stderr
index 13181f1cf7f..e7f13e63475 100644
--- a/tests/ui/issues/issue-50585.stderr
+++ b/tests/ui/issues/issue-50585.stderr
@@ -13,6 +13,12 @@ error[E0308]: mismatched types
    |
 LL |     |y: Vec<[(); for x in 0..2 {}]>| {};
    |                  ^^^^^^^^^^^^^^^^ expected `usize`, found `()`
+   |
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
+   |
+LL |     |y: Vec<[(); for x in 0..2 {} /* `usize` value */]>| {};
+   |                                   +++++++++++++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/typeck/issue-100285.rs b/tests/ui/typeck/issue-100285.rs
index 460e0457105..bea4b2bc2bb 100644
--- a/tests/ui/typeck/issue-100285.rs
+++ b/tests/ui/typeck/issue-100285.rs
@@ -1,4 +1,4 @@
-fn foo(n: i32) -> i32 { //~ HELP otherwise consider changing the return type to account for that possibility
+fn foo(n: i32) -> i32 {
     for i in 0..0 { //~ ERROR mismatched types [E0308]
        if n < 0 {
         return i;
@@ -14,7 +14,7 @@ fn foo(n: i32) -> i32 { //~ HELP otherwise consider changing the return type to
           return 5;
         }
 
-    } //~ HELP return a value for the case when the loop has zero elements to iterate on
+    } //~ HELP consider returning a value here
 }
 
 fn main() {}
diff --git a/tests/ui/typeck/issue-100285.stderr b/tests/ui/typeck/issue-100285.stderr
index c0deb63af59..6f86fd18e0f 100644
--- a/tests/ui/typeck/issue-100285.stderr
+++ b/tests/ui/typeck/issue-100285.stderr
@@ -12,47 +12,12 @@ LL | |
 LL | |     }
    | |_____^ expected `i32`, found `()`
    |
-note: the function expects a value to always be returned, but loops might run zero times
-  --> $DIR/issue-100285.rs:2:5
-   |
-LL |     for i in 0..0 {
-   |     ^^^^^^^^^^^^^ this might have zero elements to iterate on
-LL |        if n < 0 {
-LL |         return i;
-   |         -------- if the loop doesn't execute, this value would never get returned
-LL |         } else if n < 10 {
-LL |           return 1;
-   |           -------- if the loop doesn't execute, this value would never get returned
-LL |         } else if n < 20 {
-LL |           return 2;
-   |           -------- if the loop doesn't execute, this value would never get returned
-   = note: if the loop doesn't execute, 3 other values would never get returned
-help: return a value for the case when the loop has zero elements to iterate on
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
    |
 LL ~     }
 LL ~     /* `i32` value */
    |
-help: otherwise consider changing the return type to account for that possibility
-   |
-LL ~ fn foo(n: i32) -> Option<i32> {
-LL |     for i in 0..0 {
-LL |        if n < 0 {
-LL ~         return Some(i);
-LL |         } else if n < 10 {
-LL ~           return Some(1);
-LL |         } else if n < 20 {
-LL ~           return Some(2);
-LL |         } else if n < 30 {
-LL ~           return Some(3);
-LL |         } else if n < 40 {
-LL ~           return Some(4);
-LL |         } else {
-LL ~           return Some(5);
-LL |         }
-LL |
-LL ~     }
-LL ~     None
-   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/typeck/issue-98982.rs b/tests/ui/typeck/issue-98982.rs
index f875d20fa4c..3eff9fbe360 100644
--- a/tests/ui/typeck/issue-98982.rs
+++ b/tests/ui/typeck/issue-98982.rs
@@ -1,7 +1,7 @@
-fn foo() -> i32 { //~ HELP otherwise consider changing the return type to account for that possibility
+fn foo() -> i32 {
     for i in 0..0 { //~ ERROR mismatched types [E0308]
         return i;
-    } //~ HELP return a value for the case when the loop has zero elements to iterate on
+    } //~ HELP consider returning a value here
 }
 
 fn main() {}
diff --git a/tests/ui/typeck/issue-98982.stderr b/tests/ui/typeck/issue-98982.stderr
index d8d5a86b157..3d40ff4d5af 100644
--- a/tests/ui/typeck/issue-98982.stderr
+++ b/tests/ui/typeck/issue-98982.stderr
@@ -8,26 +8,12 @@ LL | |         return i;
 LL | |     }
    | |_____^ expected `i32`, found `()`
    |
-note: the function expects a value to always be returned, but loops might run zero times
-  --> $DIR/issue-98982.rs:2:5
-   |
-LL |     for i in 0..0 {
-   |     ^^^^^^^^^^^^^ this might have zero elements to iterate on
-LL |         return i;
-   |         -------- if the loop doesn't execute, this value would never get returned
-help: return a value for the case when the loop has zero elements to iterate on
+   = note: `for` loops evaluate to unit type `()`
+help: consider returning a value here
    |
 LL ~     }
 LL ~     /* `i32` value */
    |
-help: otherwise consider changing the return type to account for that possibility
-   |
-LL ~ fn foo() -> Option<i32> {
-LL |     for i in 0..0 {
-LL ~         return Some(i);
-LL ~     }
-LL ~     None
-   |
 
 error: aborting due to 1 previous error