Skip to content

Commit 2b211b1

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 2b211b1

14 files changed

+561
-218
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+18-139
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, 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};
@@ -56,7 +54,7 @@ use rustc_middle::ty::adjustment::{
5654
use rustc_middle::ty::error::TypeError;
5755
use rustc_middle::ty::relate::RelateResult;
5856
use rustc_middle::ty::visit::TypeVisitableExt;
59-
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
57+
use rustc_middle::ty::{self, GenericArgsRef, Ty};
6058
use rustc_session::parse::feature_err;
6159
use rustc_span::symbol::sym;
6260
use rustc_span::DesugaringKind;
@@ -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(
@@ -1724,6 +1700,22 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17241700
augment_error(&mut err);
17251701

17261702
if let Some(expr) = expression {
1703+
if let hir::ExprKind::Loop(
1704+
_,
1705+
_,
1706+
loop_src @ (hir::LoopSource::While | hir::LoopSource::ForLoop),
1707+
_,
1708+
) = expr.kind
1709+
{
1710+
let loop_type = if loop_src == hir::LoopSource::While {
1711+
"`while` loops"
1712+
} else {
1713+
"`for` loops"
1714+
};
1715+
1716+
err.note(format!("{loop_type} evaluate to unit type `()`"));
1717+
}
1718+
17271719
fcx.emit_coerce_suggestions(
17281720
&mut err,
17291721
expr,
@@ -1732,15 +1724,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17321724
None,
17331725
Some(coercion_error),
17341726
);
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-
}
17441727
}
17451728

17461729
let reported = err.emit_unless(unsized_return);
@@ -1814,110 +1797,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
18141797
);
18151798
}
18161799

1817-
fn note_unreachable_loop_return(
1818-
&self,
1819-
err: &mut Diag<'_>,
1820-
tcx: TyCtxt<'tcx>,
1821-
expr: &hir::Expr<'tcx>,
1822-
ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
1823-
ty: Ty<'tcx>,
1824-
) {
1825-
let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else {
1826-
return;
1827-
};
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-
}
1848-
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)
1865-
} else {
1866-
String::new()
1867-
};
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-
}
1895-
};
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}"));
1918-
}
1919-
}
1920-
19211800
fn report_return_mismatched_types<'a>(
19221801
&self,
19231802
cause: &ObligationCause<'tcx>,

compiler/rustc_hir_typeck/src/demand.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5757
|| self.suggest_into(err, expr, expr_ty, expected)
5858
|| self.suggest_floating_point_literal(err, expr, expected)
5959
|| self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
60-
|| self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty);
60+
|| self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty)
61+
|| self.suggest_returning_value_after_loop(err, expr, expected);
6162

6263
if !suggested {
6364
self.note_source_of_type_mismatch_constraint(

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+87-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use rustc_hir::def::Res;
1717
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
1818
use rustc_hir::lang_items::LangItem;
1919
use rustc_hir::{
20-
CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node,
21-
Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
20+
Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId,
21+
Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
2222
};
2323
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
2424
use rustc_infer::traits::{self};
@@ -1955,6 +1955,91 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19551955
false
19561956
}
19571957

1958+
// If the expr is a while or for loop and is the tail expr of its
1959+
// enclosing body suggest returning a value right after it
1960+
pub fn suggest_returning_value_after_loop(
1961+
&self,
1962+
err: &mut Diag<'_>,
1963+
expr: &hir::Expr<'tcx>,
1964+
expected: Ty<'tcx>,
1965+
) -> bool {
1966+
let hir = self.tcx.hir();
1967+
let enclosing_scope =
1968+
hir.get_enclosing_scope(expr.hir_id).map(|hir_id| self.tcx.hir_node(hir_id));
1969+
1970+
// Get tail expr of the enclosing block or body
1971+
let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
1972+
&& expr.is_some()
1973+
{
1974+
*expr
1975+
} else {
1976+
let body_def_id = hir.enclosing_body_owner(expr.hir_id);
1977+
let body_id = hir.body_owned_by(body_def_id);
1978+
let body = hir.body(body_id);
1979+
1980+
// Get tail expr of the body
1981+
match body.value.kind {
1982+
// Regular function body etc.
1983+
hir::ExprKind::Block(block, _) => block.expr,
1984+
// Anon const body (there's no block in this case)
1985+
hir::ExprKind::DropTemps(expr) => Some(expr),
1986+
_ => None,
1987+
}
1988+
};
1989+
1990+
let Some(tail_expr) = tail_expr else {
1991+
return false; // Body doesn't have a tail expr we can compare with
1992+
};
1993+
1994+
// Get the loop expr within the tail expr
1995+
let loop_expr_in_tail = match expr.kind {
1996+
hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
1997+
hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
1998+
match tail_expr.peel_drop_temps() {
1999+
Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
2000+
_ => return false, // Not really a for loop
2001+
}
2002+
}
2003+
_ => return false, // Not a while or a for loop
2004+
};
2005+
2006+
// If the expr is the loop expr in the tail
2007+
// then make the suggestion
2008+
if expr.hir_id == loop_expr_in_tail.hir_id {
2009+
let span = expr.span;
2010+
2011+
let (msg, suggestion) = if expected.is_never() {
2012+
(
2013+
"consider adding a diverging expression here",
2014+
"`loop {}` or `panic!(\"...\")`".to_string(),
2015+
)
2016+
} else {
2017+
("consider returning a value here", format!("`{expected}` value"))
2018+
};
2019+
2020+
let src_map = self.tcx.sess.source_map();
2021+
let suggestion = if src_map.is_multiline(expr.span) {
2022+
let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
2023+
format!("\n{indentation}/* {suggestion} */")
2024+
} else {
2025+
// If the entire expr is on a single line
2026+
// put the suggestion also on the same line
2027+
format!(" /* {suggestion} */")
2028+
};
2029+
2030+
err.span_suggestion_verbose(
2031+
span.shrink_to_hi(),
2032+
msg,
2033+
suggestion,
2034+
Applicability::MaybeIncorrect,
2035+
);
2036+
2037+
true
2038+
} else {
2039+
false
2040+
}
2041+
}
2042+
19582043
/// If the expected type is an enum (Issue #55250) with any variants whose
19592044
/// sole field is of the found type, suggest such variants. (Issue #42764)
19602045
pub(crate) fn suggest_compatible_variants(

0 commit comments

Comments
 (0)