Skip to content

Commit 3ca3dca

Browse files
committed
Remove note about iteration count in coerce
and replace it with a simple note suggesting returning a value. The type mismatch error was never due to how many times the loop iterates. It is more because of the peculiar structure of what the for loop desugars to. So the note talking about iteration count didn't make sense
1 parent 760e567 commit 3ca3dca

14 files changed

+450
-200
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+50-122
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,9 @@
3737
3838
use crate::errors::SuggestBoxingForReturnImplTrait;
3939
use crate::FnCtxt;
40-
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
40+
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag};
4141
use rustc_hir as hir;
4242
use rustc_hir::def_id::{DefId, LocalDefId};
43-
use rustc_hir::intravisit::{self, Visitor};
44-
use rustc_hir::Expr;
4543
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4644
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
4745
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -95,22 +93,6 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
9593

9694
type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
9795

98-
struct CollectRetsVisitor<'tcx> {
99-
ret_exprs: Vec<&'tcx hir::Expr<'tcx>>,
100-
}
101-
102-
impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
103-
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
104-
match expr.kind {
105-
hir::ExprKind::Ret(_) => self.ret_exprs.push(expr),
106-
// `return` in closures does not return from the outer function
107-
hir::ExprKind::Closure(_) => return,
108-
_ => {}
109-
}
110-
intravisit::walk_expr(self, expr);
111-
}
112-
}
113-
11496
/// Coercing a mutable reference to an immutable works, while
11597
/// coercing `&T` to `&mut T` should be forbidden.
11698
fn coerce_mutbls<'tcx>(
@@ -1593,7 +1575,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15931575

15941576
let mut err;
15951577
let mut unsized_return = false;
1596-
let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
15971578
match *cause.code() {
15981579
ObligationCauseCode::ReturnNoExpression => {
15991580
err = struct_span_code_err!(
@@ -1619,11 +1600,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16191600
if !fcx.tcx.features().unsized_locals {
16201601
unsized_return = self.is_return_ty_definitely_unsized(fcx);
16211602
}
1622-
if let Some(expression) = expression
1623-
&& let hir::ExprKind::Loop(loop_blk, ..) = expression.kind
1624-
{
1625-
intravisit::walk_block(&mut visitor, loop_blk);
1626-
}
16271603
}
16281604
ObligationCauseCode::ReturnValue(id) => {
16291605
err = self.report_return_mismatched_types(
@@ -1732,15 +1708,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17321708
None,
17331709
Some(coercion_error),
17341710
);
1735-
if visitor.ret_exprs.len() > 0 {
1736-
self.note_unreachable_loop_return(
1737-
&mut err,
1738-
fcx.tcx,
1739-
&expr,
1740-
&visitor.ret_exprs,
1741-
expected,
1742-
);
1743-
}
1711+
1712+
self.note_unreachable_loop_return(&mut err, fcx.tcx, &expr, expected);
17441713
}
17451714

17461715
let reported = err.emit_unless(unsized_return);
@@ -1819,102 +1788,61 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
18191788
err: &mut Diag<'_>,
18201789
tcx: TyCtxt<'tcx>,
18211790
expr: &hir::Expr<'tcx>,
1822-
ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
18231791
ty: Ty<'tcx>,
18241792
) {
1825-
let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else {
1793+
let hir::ExprKind::Loop(_, _, _, _) = expr.kind else {
18261794
return;
18271795
};
1828-
let mut span: MultiSpan = vec![loop_span].into();
1829-
span.push_span_label(loop_span, "this might have zero elements to iterate on");
1830-
const MAXITER: usize = 3;
1831-
let iter = ret_exprs.iter().take(MAXITER);
1832-
for ret_expr in iter {
1833-
span.push_span_label(
1834-
ret_expr.span,
1835-
"if the loop doesn't execute, this value would never get returned",
1836-
);
1837-
}
1838-
err.span_note(
1839-
span,
1840-
"the function expects a value to always be returned, but loops might run zero times",
1841-
);
1842-
if MAXITER < ret_exprs.len() {
1843-
err.note(format!(
1844-
"if the loop doesn't execute, {} other values would never get returned",
1845-
ret_exprs.len() - MAXITER
1846-
));
1847-
}
1796+
18481797
let hir = tcx.hir();
1849-
let item = hir.get_parent_item(expr.hir_id);
1850-
let ret_msg = "return a value for the case when the loop has zero elements to iterate on";
1851-
let ret_ty_msg =
1852-
"otherwise consider changing the return type to account for that possibility";
1853-
let node = tcx.hir_node(item.into());
1854-
if let Some(body_id) = node.body_id()
1855-
&& let Some(sig) = node.fn_sig()
1856-
&& let hir::ExprKind::Block(block, _) = hir.body(body_id).value.kind
1857-
&& !ty.is_never()
1858-
{
1859-
let indentation = if let None = block.expr
1860-
&& let [.., last] = &block.stmts
1861-
{
1862-
tcx.sess.source_map().indentation_before(last.span).unwrap_or_else(String::new)
1863-
} else if let Some(expr) = block.expr {
1864-
tcx.sess.source_map().indentation_before(expr.span).unwrap_or_else(String::new)
1798+
let body_def_id = hir.enclosing_body_owner(expr.hir_id);
1799+
let body_id = hir.body_owned_by(body_def_id);
1800+
let body = hir.body(body_id);
1801+
1802+
let span = match body.value.kind {
1803+
// Regular block as in a function body
1804+
hir::ExprKind::Block(block, _) => {
1805+
if let None = block.expr
1806+
&& let [.., last] = &block.stmts
1807+
{
1808+
Some(last.span)
1809+
} else if let Some(expr) = block.expr {
1810+
Some(expr.span)
1811+
} else {
1812+
None
1813+
}
1814+
}
1815+
// Anon const body (there's no enclosing block in this case)
1816+
hir::ExprKind::DropTemps(expr) => Some(expr.span),
1817+
_ => None,
1818+
};
1819+
1820+
if let Some(span) = span {
1821+
let (msg, suggestion) = if ty.is_never() {
1822+
(
1823+
"consider adding a diverging expression here",
1824+
"`loop {}` or `panic!(\"...\")`".to_string(),
1825+
)
18651826
} else {
1866-
String::new()
1827+
("consider returning a value here", format!("`{ty}` value"))
18671828
};
1868-
if let None = block.expr
1869-
&& let [.., last] = &block.stmts
1870-
{
1871-
err.span_suggestion_verbose(
1872-
last.span.shrink_to_hi(),
1873-
ret_msg,
1874-
format!("\n{indentation}/* `{ty}` value */"),
1875-
Applicability::MaybeIncorrect,
1876-
);
1877-
} else if let Some(expr) = block.expr {
1878-
err.span_suggestion_verbose(
1879-
expr.span.shrink_to_hi(),
1880-
ret_msg,
1881-
format!("\n{indentation}/* `{ty}` value */"),
1882-
Applicability::MaybeIncorrect,
1883-
);
1884-
}
1885-
let mut sugg = match sig.decl.output {
1886-
hir::FnRetTy::DefaultReturn(span) => {
1887-
vec![(span, " -> Option<()>".to_string())]
1888-
}
1889-
hir::FnRetTy::Return(ty) => {
1890-
vec![
1891-
(ty.span.shrink_to_lo(), "Option<".to_string()),
1892-
(ty.span.shrink_to_hi(), ">".to_string()),
1893-
]
1894-
}
1829+
1830+
let src_map = tcx.sess.source_map();
1831+
let suggestion = if src_map.is_multiline(expr.span) {
1832+
let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
1833+
format!("\n{indentation}/* {suggestion} */")
1834+
} else {
1835+
// If the entire expr is on a single line
1836+
// put the suggestion also on the same line
1837+
format!(" /* {suggestion} */")
18951838
};
1896-
for ret_expr in ret_exprs {
1897-
match ret_expr.kind {
1898-
hir::ExprKind::Ret(Some(expr)) => {
1899-
sugg.push((expr.span.shrink_to_lo(), "Some(".to_string()));
1900-
sugg.push((expr.span.shrink_to_hi(), ")".to_string()));
1901-
}
1902-
hir::ExprKind::Ret(None) => {
1903-
sugg.push((ret_expr.span.shrink_to_hi(), " Some(())".to_string()));
1904-
}
1905-
_ => {}
1906-
}
1907-
}
1908-
if let None = block.expr
1909-
&& let [.., last] = &block.stmts
1910-
{
1911-
sugg.push((last.span.shrink_to_hi(), format!("\n{indentation}None")));
1912-
} else if let Some(expr) = block.expr {
1913-
sugg.push((expr.span.shrink_to_hi(), format!("\n{indentation}None")));
1914-
}
1915-
err.multipart_suggestion(ret_ty_msg, sugg, Applicability::MaybeIncorrect);
1916-
} else {
1917-
err.help(format!("{ret_msg}, {ret_ty_msg}"));
1839+
1840+
err.span_suggestion_verbose(
1841+
span.shrink_to_hi(),
1842+
msg,
1843+
suggestion,
1844+
Applicability::MaybeIncorrect,
1845+
);
19181846
}
19191847
}
19201848

library/stdarch

Submodule stdarch updated 68 files

src/tools/cargo

Submodule cargo updated 208 files
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Regression test for #122561
2+
3+
fn for_infinite() -> bool {
4+
for i in 0.. {
5+
//~^ ERROR mismatched types
6+
return false;
7+
}
8+
}
9+
10+
fn for_finite() -> String {
11+
for i in 0..5 {
12+
//~^ ERROR mismatched types
13+
return String::from("test");
14+
}
15+
}
16+
17+
fn for_never_type() -> ! {
18+
for i in 0..0 {
19+
//~^ ERROR mismatched types
20+
}
21+
}
22+
23+
// Entire function on a single line.
24+
// Tests that we format the suggestion
25+
// correctly in this case
26+
fn for_single_line() -> bool { for i in 0.. { return false; } }
27+
//~^ ERROR mismatched types
28+
29+
// Loop in an anon const in function args
30+
// Tests that we:
31+
// a. deal properly with this complex case
32+
// b. format the suggestion correctly so
33+
// that it's readable
34+
fn for_in_arg(a: &[(); for x in 0..2 {}]) -> bool {
35+
//~^ ERROR `for` is not allowed in a `const`
36+
//~| ERROR mismatched types
37+
true
38+
}
39+
40+
fn while_true() -> bool {
41+
while true {
42+
//~^ ERROR mismatched types
43+
//~| WARN denote infinite loops with `loop { ... }` [while_true]
44+
return true;
45+
}
46+
}
47+
48+
fn while_false() -> bool {
49+
while false {
50+
//~^ ERROR mismatched types
51+
return true;
52+
}
53+
}
54+
55+
fn while_true_never_type() -> ! {
56+
while true {
57+
//~^ ERROR mismatched types
58+
//~| WARN denote infinite loops with `loop { ... }` [while_true]
59+
}
60+
}
61+
62+
// No type mismatch error in this case
63+
fn loop_() -> bool {
64+
loop {
65+
return true;
66+
}
67+
}
68+
69+
const C: i32 = {
70+
for i in 0.. {
71+
//~^ ERROR `for` is not allowed in a `const`
72+
//~| ERROR mismatched types
73+
}
74+
};
75+
76+
fn main() {
77+
let _ = [10; {
78+
for i in 0..5 {
79+
//~^ ERROR `for` is not allowed in a `const`
80+
//~| ERROR mismatched types
81+
}
82+
}];
83+
84+
let _ = [10; {
85+
while false {
86+
//~^ ERROR mismatched types
87+
}
88+
}];
89+
90+
91+
let _ = |a: &[(); for x in 0..2 {}]| {};
92+
//~^ ERROR `for` is not allowed in a `const`
93+
//~| ERROR mismatched types
94+
}

0 commit comments

Comments
 (0)