3636//! ```
3737
3838use crate :: FnCtxt ;
39+ use rustc_ast:: { LitKind , UnOp } ;
3940use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , Diag , MultiSpan } ;
4041use rustc_hir as hir;
4142use rustc_hir:: def_id:: DefId ;
4243use rustc_hir:: intravisit:: { self , Visitor } ;
43- use rustc_hir:: Expr ;
44+ use rustc_hir:: { Expr , ExprField , ExprKind , LangItem , QPath } ;
4445use rustc_hir_analysis:: hir_ty_lowering:: HirTyLowerer ;
4546use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
4647use rustc_infer:: infer:: { Coercion , DefineOpaqueTypes , InferOk , InferResult } ;
@@ -57,6 +58,7 @@ use rustc_middle::ty::relate::RelateResult;
5758use rustc_middle:: ty:: visit:: TypeVisitableExt ;
5859use rustc_middle:: ty:: { self , GenericArgsRef , Ty , TyCtxt } ;
5960use rustc_session:: parse:: feature_err;
61+ use rustc_span:: source_map:: Spanned ;
6062use rustc_span:: symbol:: sym;
6163use rustc_span:: DesugaringKind ;
6264use 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 > ,
0 commit comments