Skip to content

Commit 0d52c10

Browse files
committed
Do not emit iteration note in coerce if loop does iterate
This fix does not cover all the cases when the loop iterates such as when the iterate value is a local instead of a literal
1 parent fa374a8 commit 0d52c10

File tree

3 files changed

+572
-7
lines changed

3 files changed

+572
-7
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+113-7
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@
3636
//! ```
3737
3838
use crate::FnCtxt;
39+
use rustc_ast::{LitKind, UnOp};
3940
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
4041
use rustc_hir as hir;
4142
use rustc_hir::def_id::DefId;
4243
use rustc_hir::intravisit::{self, Visitor};
43-
use rustc_hir::Expr;
44+
use rustc_hir::{Expr, ExprField, ExprKind, LangItem, QPath};
4445
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4546
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
4647
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -57,6 +58,7 @@ use rustc_middle::ty::relate::RelateResult;
5758
use rustc_middle::ty::visit::TypeVisitableExt;
5859
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
5960
use rustc_session::parse::feature_err;
61+
use rustc_span::source_map::Spanned;
6062
use rustc_span::symbol::sym;
6163
use rustc_span::DesugaringKind;
6264
use rustc_target::spec::abi::Abi;
@@ -1688,6 +1690,23 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16881690
let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else {
16891691
return;
16901692
};
1693+
1694+
let hir = tcx.hir();
1695+
let parent_node = tcx.hir_node(hir.get_parent_item(expr.hir_id).into());
1696+
let parent_block = if let Some(body_id) = parent_node.body_id()
1697+
&& let hir::ExprKind::Block(block, _) = hir.body(body_id).value.kind
1698+
{
1699+
Some(block)
1700+
} else {
1701+
None
1702+
};
1703+
1704+
if let Some(block) = parent_block
1705+
&& Self::loop_iterates_atleast_once(block)
1706+
{
1707+
return;
1708+
}
1709+
16911710
let mut span: MultiSpan = vec![loop_span].into();
16921711
span.push_span_label(loop_span, "this might have zero elements to iterate on");
16931712
const MAXITER: usize = 3;
@@ -1708,15 +1727,11 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17081727
ret_exprs.len() - MAXITER
17091728
));
17101729
}
1711-
let hir = tcx.hir();
1712-
let item = hir.get_parent_item(expr.hir_id);
17131730
let ret_msg = "return a value for the case when the loop has zero elements to iterate on";
17141731
let ret_ty_msg =
17151732
"otherwise consider changing the return type to account for that possibility";
1716-
let node = tcx.hir_node(item.into());
1717-
if let Some(body_id) = node.body_id()
1718-
&& let Some(sig) = node.fn_sig()
1719-
&& let hir::ExprKind::Block(block, _) = hir.body(body_id).value.kind
1733+
if let Some(block) = parent_block
1734+
&& let Some(sig) = parent_node.fn_sig()
17201735
&& !ty.is_never()
17211736
{
17221737
let indentation = if let None = block.expr
@@ -1781,6 +1796,97 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17811796
}
17821797
}
17831798

1799+
/// Returns `true` if the given `block` is a loop
1800+
/// and it will definitely iterate at least once
1801+
fn loop_iterates_atleast_once(block: &hir::Block<'tcx>) -> bool {
1802+
// Check if `block` is a for loop and extract
1803+
// the expr it iterate over as `iter_target`
1804+
let Some(Expr {
1805+
kind:
1806+
ExprKind::DropTemps(Expr {
1807+
kind:
1808+
ExprKind::Match(
1809+
Expr {
1810+
kind:
1811+
ExprKind::Call(
1812+
Expr {
1813+
kind:
1814+
ExprKind::Path(
1815+
QPath::LangItem(LangItem::IntoIterIntoIter, ..),
1816+
..,
1817+
),
1818+
..
1819+
},
1820+
[Expr { kind: iter_target, .. }],
1821+
),
1822+
..
1823+
},
1824+
..,
1825+
),
1826+
..
1827+
}),
1828+
..
1829+
}) = block.expr
1830+
else {
1831+
return false; // Block is not a for loop
1832+
};
1833+
1834+
// Peel away any ref if present
1835+
let iter_target = match iter_target {
1836+
ExprKind::AddrOf(.., deref) => deref.kind,
1837+
_ => *iter_target,
1838+
};
1839+
1840+
// Computes value of a literal expr
1841+
// Takes into account any enclosing neg unary expr if present
1842+
fn get_literal<'a>(expr_kind: &ExprKind<'a>) -> Option<i128> {
1843+
match expr_kind {
1844+
ExprKind::Lit(Spanned { node: LitKind::Int(lit, ..), .. }) => {
1845+
i128::try_from(lit.get()).ok()
1846+
}
1847+
ExprKind::Unary(
1848+
UnOp::Neg,
1849+
Expr {
1850+
kind: ExprKind::Lit(Spanned { node: LitKind::Int(lit, ..), .. }), ..
1851+
},
1852+
) => i128::try_from(lit.get()).map(|v| -1 * v).ok(),
1853+
_ => None,
1854+
}
1855+
}
1856+
1857+
// Check if `iter_target` will be iterated over at least once.
1858+
// We support only exclusive, inclusive and infinite range
1859+
// literals and array literals
1860+
match iter_target {
1861+
// Exclusive range
1862+
ExprKind::Struct(
1863+
QPath::LangItem(LangItem::Range, ..),
1864+
[
1865+
ExprField { expr: Expr { kind: start, .. }, .. },
1866+
ExprField { expr: Expr { kind: end, .. }, .. },
1867+
],
1868+
..,
1869+
) => match (get_literal(start), get_literal(end)) {
1870+
(Some(start), Some(end)) => end > start,
1871+
_ => false,
1872+
},
1873+
// Inclusive range
1874+
ExprKind::Call(
1875+
Expr {
1876+
kind: ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)), ..
1877+
},
1878+
[Expr { kind: start, .. }, Expr { kind: end, .. }],
1879+
) => match (get_literal(start), get_literal(end)) {
1880+
(Some(start), Some(end)) => end >= start,
1881+
_ => false,
1882+
},
1883+
// Infinite range
1884+
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), ..) => true,
1885+
ExprKind::Array(items) => items.len() > 0,
1886+
_ => false,
1887+
}
1888+
}
1889+
17841890
fn report_return_mismatched_types<'a>(
17851891
&self,
17861892
cause: &ObligationCause<'tcx>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Regression test for #122561
2+
// Checks that we do our best not to emit the note
3+
// saying the loop might run zero times if it is
4+
// clear it will certainly run at least once
5+
6+
7+
// ----- Should not emit note for these -----
8+
fn atleast_once_iter_range() -> bool {
9+
for i in 0..3 {
10+
//~^ ERROR mismatched types
11+
return false;
12+
}
13+
}
14+
15+
fn atleast_once_iter_range_neg() -> bool {
16+
for i in -3..-1 {
17+
//~^ ERROR mismatched types
18+
return false;
19+
}
20+
}
21+
22+
fn atleast_once_iter_range_inclusive() -> bool {
23+
for i in 0..=3 {
24+
//~^ ERROR mismatched types
25+
return false;
26+
}
27+
}
28+
29+
fn atleast_once_iter_range_inclusive_neg() -> bool {
30+
for i in -3..=-1 {
31+
//~^ ERROR mismatched types
32+
return false;
33+
}
34+
}
35+
36+
fn atleast_once_iter_infinite_range() -> bool {
37+
for i in 0.. {
38+
//~^ ERROR mismatched types
39+
return false;
40+
}
41+
}
42+
43+
fn atleast_once_iter_infinite_range_neg() -> bool {
44+
for i in -3.. {
45+
//~^ ERROR mismatched types
46+
return false;
47+
}
48+
}
49+
50+
fn atleast_once_iter_array() -> bool {
51+
for i in [1, 2, 3] {
52+
//~^ ERROR mismatched types
53+
return false;
54+
}
55+
}
56+
57+
fn atleast_once_iter_array_ref() -> bool {
58+
for i in &[1, 2, 3] {
59+
//~^ ERROR mismatched types
60+
return false;
61+
}
62+
}
63+
64+
65+
// ----- Should emit note for these -----
66+
fn zero_iter_range() -> bool {
67+
for i in 0..0 {
68+
//~^ ERROR mismatched types
69+
return false;
70+
}
71+
}
72+
73+
fn zero_iter_array() -> bool {
74+
for i in [] {
75+
//~^ ERROR mismatched types
76+
return false;
77+
}
78+
}
79+
80+
fn zero_iter_array_ref() -> bool {
81+
for i in &[] {
82+
//~^ ERROR mismatched types
83+
return false;
84+
}
85+
}
86+
87+
// For the following cases the loop does iterate at
88+
// least once but we aren't currently smart enough
89+
// to not emit the note for them. We might add such
90+
// smarts in the future
91+
fn atlast_once_iter_array_var() -> bool {
92+
let x = [1, 2, 3];
93+
for i in x {
94+
//~^ ERROR mismatched types
95+
return false;
96+
}
97+
}
98+
99+
fn atleast_once_iter_vec() -> bool {
100+
for i in vec![1, 2, 3] {
101+
//~^ ERROR mismatched types
102+
return false;
103+
}
104+
}
105+
106+
fn atleast_once_iter_array_iter() -> bool {
107+
for i in [1, 2, 3].iter() {
108+
//~^ ERROR mismatched types
109+
return false;
110+
}
111+
}
112+
113+
fn atleast_once_iter_func_result() -> bool {
114+
for i in get_iter() {
115+
//~^ ERROR mismatched types
116+
return false;
117+
}
118+
}
119+
120+
121+
// Helper function
122+
fn get_iter() -> impl Iterator<Item=i32> {
123+
1..
124+
}
125+
126+
fn main() {}

0 commit comments

Comments
 (0)