3636//! ```
3737
3838use crate :: FnCtxt ;
39+ use hir:: { Expr , ExprField , ExprKind , LangItem , QPath } ;
40+ use rustc_ast:: { LitKind , UnOp } ;
3941use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , Diag , MultiSpan } ;
4042use rustc_hir as hir;
4143use rustc_hir:: def_id:: DefId ;
4244use rustc_hir:: intravisit:: { self , Visitor } ;
43- use rustc_hir:: Expr ;
4445use rustc_hir_analysis:: astconv:: AstConv ;
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 , TypeAndMut } ;
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 ;
@@ -1694,6 +1696,23 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16941696 let hir:: ExprKind :: Loop ( _, _, _, loop_span) = expr. kind else {
16951697 return ;
16961698 } ;
1699+
1700+ let hir = tcx. hir ( ) ;
1701+ let parent_node = tcx. hir_node ( hir. get_parent_item ( expr. hir_id ) . into ( ) ) ;
1702+ let parent_block = if let Some ( body_id) = parent_node. body_id ( )
1703+ && let hir:: ExprKind :: Block ( block, _) = hir. body ( body_id) . value . kind
1704+ {
1705+ Some ( block)
1706+ } else {
1707+ None
1708+ } ;
1709+
1710+ if let Some ( block) = parent_block
1711+ && Self :: loop_iterates_atleast_once ( block)
1712+ {
1713+ return ;
1714+ }
1715+
16971716 let mut span: MultiSpan = vec ! [ loop_span] . into ( ) ;
16981717 span. push_span_label ( loop_span, "this might have zero elements to iterate on" ) ;
16991718 const MAXITER : usize = 3 ;
@@ -1714,15 +1733,11 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17141733 ret_exprs. len( ) - MAXITER
17151734 ) ) ;
17161735 }
1717- let hir = tcx. hir ( ) ;
1718- let item = hir. get_parent_item ( expr. hir_id ) ;
17191736 let ret_msg = "return a value for the case when the loop has zero elements to iterate on" ;
17201737 let ret_ty_msg =
17211738 "otherwise consider changing the return type to account for that possibility" ;
1722- let node = tcx. hir_node ( item. into ( ) ) ;
1723- if let Some ( body_id) = node. body_id ( )
1724- && let Some ( sig) = node. fn_sig ( )
1725- && let hir:: ExprKind :: Block ( block, _) = hir. body ( body_id) . value . kind
1739+ if let Some ( block) = parent_block
1740+ && let Some ( sig) = parent_node. fn_sig ( )
17261741 && !ty. is_never ( )
17271742 {
17281743 let indentation = if let None = block. expr
@@ -1787,6 +1802,97 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
17871802 }
17881803 }
17891804
1805+ /// Returns `true` if the given `block` is a loop
1806+ /// and it will definitely iterate at least once
1807+ fn loop_iterates_atleast_once ( block : & hir:: Block < ' tcx > ) -> bool {
1808+ // Check if `block` is a for loop and extract
1809+ // the expr it iterate over as `iter_target`
1810+ let Some ( Expr {
1811+ kind :
1812+ ExprKind :: DropTemps ( Expr {
1813+ kind :
1814+ ExprKind :: Match (
1815+ Expr {
1816+ kind :
1817+ ExprKind :: Call (
1818+ Expr {
1819+ kind :
1820+ ExprKind :: Path (
1821+ QPath :: LangItem ( LangItem :: IntoIterIntoIter , ..) ,
1822+ ..,
1823+ ) ,
1824+ ..
1825+ } ,
1826+ [ Expr { kind : iter_target, .. } ] ,
1827+ ) ,
1828+ ..
1829+ } ,
1830+ ..,
1831+ ) ,
1832+ ..
1833+ } ) ,
1834+ ..
1835+ } ) = block. expr
1836+ else {
1837+ return false ; // Block is not a for loop
1838+ } ;
1839+
1840+ // Peel away any ref if present
1841+ let iter_target = match iter_target {
1842+ ExprKind :: AddrOf ( .., deref) => deref. kind ,
1843+ _ => * iter_target,
1844+ } ;
1845+
1846+ // Computes value of a literal expr
1847+ // Takes into account any enclosing neg unary expr if present
1848+ fn get_literal < ' a > ( expr_kind : & ExprKind < ' a > ) -> Option < i128 > {
1849+ match expr_kind {
1850+ ExprKind :: Lit ( Spanned { node : LitKind :: Int ( lit, ..) , .. } ) => {
1851+ i128:: try_from ( lit. get ( ) ) . ok ( )
1852+ }
1853+ ExprKind :: Unary (
1854+ UnOp :: Neg ,
1855+ Expr {
1856+ kind : ExprKind :: Lit ( Spanned { node : LitKind :: Int ( lit, ..) , .. } ) , ..
1857+ } ,
1858+ ) => i128:: try_from ( lit. get ( ) ) . map ( |v| -1 * v) . ok ( ) ,
1859+ _ => None ,
1860+ }
1861+ }
1862+
1863+ // Check if `iter_target` will be iterated over at least once.
1864+ // We support only exclusive, inclusive and infinite range
1865+ // literals and array literals
1866+ match iter_target {
1867+ // Exclusive range
1868+ ExprKind :: Struct (
1869+ QPath :: LangItem ( LangItem :: Range , ..) ,
1870+ [
1871+ ExprField { expr : Expr { kind : start, .. } , .. } ,
1872+ ExprField { expr : Expr { kind : end, .. } , .. } ,
1873+ ] ,
1874+ ..,
1875+ ) => match ( get_literal ( start) , get_literal ( end) ) {
1876+ ( Some ( start) , Some ( end) ) => end > start,
1877+ _ => false ,
1878+ } ,
1879+ // Inclusive range
1880+ ExprKind :: Call (
1881+ Expr {
1882+ kind : ExprKind :: Path ( QPath :: LangItem ( LangItem :: RangeInclusiveNew , ..) ) , ..
1883+ } ,
1884+ [ Expr { kind : start, .. } , Expr { kind : end, .. } ] ,
1885+ ) => match ( get_literal ( start) , get_literal ( end) ) {
1886+ ( Some ( start) , Some ( end) ) => end >= start,
1887+ _ => false ,
1888+ } ,
1889+ // Infinite range
1890+ ExprKind :: Struct ( QPath :: LangItem ( LangItem :: RangeFrom , ..) , ..) => true ,
1891+ ExprKind :: Array ( items) => items. len ( ) > 0 ,
1892+ _ => false ,
1893+ }
1894+ }
1895+
17901896 fn report_return_mismatched_types < ' a > (
17911897 & self ,
17921898 cause : & ObligationCause < ' tcx > ,
0 commit comments