about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-08-06 15:09:59 +0000
committerbors <bors@rust-lang.org>2022-08-06 15:09:59 +0000
commit5c54be35c63a883d5114cdbc50e07194e5cece0f (patch)
treee53022b20d73d8044fb1e2df52f79f67ed027e53
parent76b04847400effd127ced322d5627c0c1aec2998 (diff)
parent811b0368aab849fb53f66e7ce7bec7a56e4c2f8a (diff)
downloadrust-5c54be35c63a883d5114cdbc50e07194e5cece0f.tar.gz
rust-5c54be35c63a883d5114cdbc50e07194e5cece0f.zip
Auto merge of #100195 - matthiaskrgr:rollup-ovzyyb0, r=matthiaskrgr
Rollup of 4 pull requests

Successful merges:

 - #100094 (Detect type mismatch due to loop that might never iterate)
 - #100132 (Use (actually) dummy place for let-else divergence)
 - #100167 (Recover `require`, `include` instead of `use` in item)
 - #100193 (Remove more Clean trait implementations)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs5
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_typeck/src/check/coercion.rs49
-rw-r--r--src/librustdoc/clean/mod.rs90
-rw-r--r--src/test/ui/did_you_mean/use_instead_of_import.fixed8
-rw-r--r--src/test/ui/did_you_mean/use_instead_of_import.rs8
-rw-r--r--src/test/ui/did_you_mean/use_instead_of_import.stderr16
-rw-r--r--src/test/ui/for-loop-while/break-while-condition.stderr8
-rw-r--r--src/test/ui/let-else/issue-100103.rs15
-rw-r--r--src/test/ui/typeck/issue-98982.rs9
-rw-r--r--src/test/ui/typeck/issue-98982.stderr24
12 files changed, 182 insertions, 53 deletions
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 58b1564cc5d..cefb5f36b6a 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -2334,7 +2334,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // This place is not really used because this destination place
         // should never be used to take values at the end of the failure
         // block.
-        let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() };
+        let dummy_place = self.temp(self.tcx.types.never, else_block.span);
         let failure_block;
         unpack!(
             failure_block = self.ast_block(
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index fb92ce41252..72c23776d33 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -271,7 +271,10 @@ impl<'a> Parser<'a> {
             // MACRO_RULES ITEM
             self.parse_item_macro_rules(vis, has_bang)?
         } else if self.isnt_macro_invocation()
-            && (self.token.is_ident_named(sym::import) || self.token.is_ident_named(sym::using))
+            && (self.token.is_ident_named(sym::import)
+                || self.token.is_ident_named(sym::using)
+                || self.token.is_ident_named(sym::include)
+                || self.token.is_ident_named(sym::require))
         {
             return self.recover_import_as_use();
         } else if self.isnt_macro_invocation() && vis.kind.is_pub() {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 791160ff694..f81a69c1cce 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1170,6 +1170,7 @@ symbols! {
         repr_packed,
         repr_simd,
         repr_transparent,
+        require,
         residual,
         result,
         rhs,
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs
index 2ed5f569b4f..0b63b4a63c9 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -38,10 +38,12 @@
 use crate::astconv::AstConv;
 use crate::check::FnCtxt;
 use rustc_errors::{
-    struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::Expr;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{Coercion, InferOk, InferResult};
 use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt};
@@ -87,6 +89,19 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
 
 type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
 
+struct CollectRetsVisitor<'tcx> {
+    ret_exprs: Vec<&'tcx hir::Expr<'tcx>>,
+}
+
+impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        if let hir::ExprKind::Ret(_) = expr.kind {
+            self.ret_exprs.push(expr);
+        }
+        intravisit::walk_expr(self, expr);
+    }
+}
+
 /// Coercing a mutable reference to an immutable works, while
 /// coercing `&T` to `&mut T` should be forbidden.
 fn coerce_mutbls<'tcx>(
@@ -1481,6 +1496,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
 
                 let mut err;
                 let mut unsized_return = false;
+                let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
                 match *cause.code() {
                     ObligationCauseCode::ReturnNoExpression => {
                         err = struct_span_err!(
@@ -1506,6 +1522,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                         if !fcx.tcx.features().unsized_locals {
                             unsized_return = self.is_return_ty_unsized(fcx, blk_id);
                         }
+                        if let Some(expression) = expression
+                            && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind {
+                              intravisit::walk_block(& mut visitor, loop_blk);
+                        }
                     }
                     ObligationCauseCode::ReturnValue(id) => {
                         err = self.report_return_mismatched_types(
@@ -1551,12 +1571,39 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                     );
                 }
 
+                if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
+                    self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
+                }
                 err.emit_unless(unsized_return);
 
                 self.final_ty = Some(fcx.tcx.ty_error());
             }
         }
     }
+    fn note_unreachable_loop_return<'a>(
+        &self,
+        err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>,
+        expr: &hir::Expr<'tcx>,
+        ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
+    ) {
+        let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;};
+        let mut span: MultiSpan = vec![loop_span].into();
+        span.push_span_label(loop_span, "this might have zero elements to iterate on".to_string());
+        for ret_expr in ret_exprs {
+            span.push_span_label(
+                ret_expr.span,
+                "if the loop doesn't execute, this value would never get returned".to_string(),
+            );
+        }
+        err.span_note(
+            span,
+            "the function expects a value to always be returned, but loops might run zero times",
+        );
+        err.help(
+            "return a value for the case when the loop has zero elements to iterate on, or \
+           consider changing the return type to account for that possibility",
+        );
+    }
 
     fn report_return_mismatched_types<'a>(
         &self,
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2368a1f1c7e..5fb9747a944 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -126,40 +126,40 @@ impl<'tcx> Clean<'tcx, Item> for DocModule<'tcx> {
     }
 }
 
-impl<'tcx> Clean<'tcx, Option<GenericBound>> for hir::GenericBound<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<GenericBound> {
-        Some(match *self {
-            hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)),
-            hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => {
-                let def_id = cx.tcx.require_lang_item(lang_item, Some(span));
-
-                let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder();
-
-                let generic_args = generic_args.clean(cx);
-                let GenericArgs::AngleBracketed { bindings, .. } = generic_args
-                else {
-                    bug!("clean: parenthesized `GenericBound::LangItemTrait`");
-                };
+fn clean_generic_bound<'tcx>(
+    bound: &hir::GenericBound<'tcx>,
+    cx: &mut DocContext<'tcx>,
+) -> Option<GenericBound> {
+    Some(match *bound {
+        hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)),
+        hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => {
+            let def_id = cx.tcx.require_lang_item(lang_item, Some(span));
+
+            let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder();
+
+            let generic_args = generic_args.clean(cx);
+            let GenericArgs::AngleBracketed { bindings, .. } = generic_args
+            else {
+                bug!("clean: parenthesized `GenericBound::LangItemTrait`");
+            };
 
-                let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings);
-                GenericBound::TraitBound(
-                    PolyTrait { trait_, generic_params: vec![] },
-                    hir::TraitBoundModifier::None,
-                )
+            let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings);
+            GenericBound::TraitBound(
+                PolyTrait { trait_, generic_params: vec![] },
+                hir::TraitBoundModifier::None,
+            )
+        }
+        hir::GenericBound::Trait(ref t, modifier) => {
+            // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op.
+            if modifier == hir::TraitBoundModifier::MaybeConst
+                && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap())
+            {
+                return None;
             }
-            hir::GenericBound::Trait(ref t, modifier) => {
-                // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op.
-                if modifier == hir::TraitBoundModifier::MaybeConst
-                    && cx.tcx.lang_items().destruct_trait()
-                        == Some(t.trait_ref.trait_def_id().unwrap())
-                {
-                    return None;
-                }
 
-                GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier)
-            }
-        })
-    }
+            GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier)
+        }
+    })
 }
 
 pub(crate) fn clean_trait_ref_with_bindings<'tcx>(
@@ -207,12 +207,6 @@ fn clean_poly_trait_ref_with_bindings<'tcx>(
     )
 }
 
-impl<'tcx> Clean<'tcx, GenericBound> for ty::PolyTraitRef<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericBound {
-        clean_poly_trait_ref_with_bindings(cx, *self, &[])
-    }
-}
-
 fn clean_lifetime<'tcx>(lifetime: hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime {
     let def = cx.tcx.named_region(lifetime.hir_id);
     if let Some(
@@ -294,14 +288,14 @@ impl<'tcx> Clean<'tcx, Option<WherePredicate>> for hir::WherePredicate<'tcx> {
                     .collect();
                 WherePredicate::BoundPredicate {
                     ty: clean_ty(wbp.bounded_ty, cx),
-                    bounds: wbp.bounds.iter().filter_map(|x| x.clean(cx)).collect(),
+                    bounds: wbp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
                     bound_params,
                 }
             }
 
             hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate {
                 lifetime: clean_lifetime(wrp.lifetime, cx),
-                bounds: wrp.bounds.iter().filter_map(|x| x.clean(cx)).collect(),
+                bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
             },
 
             hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate {
@@ -349,7 +343,7 @@ fn clean_poly_trait_predicate<'tcx>(
     let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref);
     Some(WherePredicate::BoundPredicate {
         ty: clean_middle_ty(poly_trait_ref.skip_binder().self_ty(), cx, None),
-        bounds: vec![poly_trait_ref.clean(cx)],
+        bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, &[])],
         bound_params: Vec::new(),
     })
 }
@@ -531,7 +525,7 @@ fn clean_generic_param<'tcx>(
                     .bounds_for_param(did)
                     .filter(|bp| bp.origin != PredicateOrigin::WhereClause)
                     .flat_map(|bp| bp.bounds)
-                    .filter_map(|x| x.clean(cx))
+                    .filter_map(|x| clean_generic_bound(x, cx))
                     .collect()
             } else {
                 Vec::new()
@@ -1041,7 +1035,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
             }
             hir::TraitItemKind::Type(bounds, Some(default)) => {
                 let generics = enter_impl_trait(cx, |cx| trait_item.generics.clean(cx));
-                let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
+                let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
                 let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, default), cx, None);
                 AssocTypeItem(
                     Box::new(Typedef {
@@ -1054,7 +1048,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
             }
             hir::TraitItemKind::Type(bounds, None) => {
                 let generics = enter_impl_trait(cx, |cx| trait_item.generics.clean(cx));
-                let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
+                let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
                 TyAssocTypeItem(Box::new(generics), bounds)
             }
         };
@@ -1507,7 +1501,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
         TyKind::OpaqueDef(item_id, _) => {
             let item = cx.tcx.hir().item(item_id);
             if let hir::ItemKind::OpaqueTy(ref ty) = item.kind {
-                ImplTrait(ty.bounds.iter().filter_map(|x| x.clean(cx)).collect())
+                ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect())
             } else {
                 unreachable!()
             }
@@ -1911,7 +1905,7 @@ fn clean_maybe_renamed_item<'tcx>(
                 kind: ConstantKind::Local { body: body_id, def_id },
             }),
             ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {
-                bounds: ty.bounds.iter().filter_map(|x| x.clean(cx)).collect(),
+                bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
                 generics: ty.generics.clean(cx),
             }),
             ItemKind::TyAlias(hir_ty, generics) => {
@@ -1929,7 +1923,7 @@ fn clean_maybe_renamed_item<'tcx>(
             }),
             ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias {
                 generics: generics.clean(cx),
-                bounds: bounds.iter().filter_map(|x| x.clean(cx)).collect(),
+                bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
             }),
             ItemKind::Union(ref variant_data, generics) => UnionItem(Union {
                 generics: generics.clean(cx),
@@ -1961,7 +1955,7 @@ fn clean_maybe_renamed_item<'tcx>(
                     def_id,
                     items,
                     generics: generics.clean(cx),
-                    bounds: bounds.iter().filter_map(|x| x.clean(cx)).collect(),
+                    bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
                 })
             }
             ItemKind::ExternCrate(orig_name) => {
@@ -2241,7 +2235,7 @@ fn clean_type_binding<'tcx>(
                 TypeBindingKind::Equality { term: clean_hir_term(term, cx) }
             }
             hir::TypeBindingKind::Constraint { bounds } => TypeBindingKind::Constraint {
-                bounds: bounds.iter().filter_map(|b| b.clean(cx)).collect(),
+                bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(),
             },
         },
     }
diff --git a/src/test/ui/did_you_mean/use_instead_of_import.fixed b/src/test/ui/did_you_mean/use_instead_of_import.fixed
index 87d453e1565..a8aae76f4fc 100644
--- a/src/test/ui/did_you_mean/use_instead_of_import.fixed
+++ b/src/test/ui/did_you_mean/use_instead_of_import.fixed
@@ -6,10 +6,18 @@ use std::{
     rc::Rc,
 };
 
+use std::time::Duration;
+//~^ ERROR expected item, found `require`
+
+use std::time::Instant;
+//~^ ERROR expected item, found `include`
+
 pub use std::io;
 //~^ ERROR expected item, found `using`
 
 fn main() {
     let x = Rc::new(1);
     let _ = write!(io::stdout(), "{:?}", x);
+    let _ = Duration::new(5, 0);
+    let _ = Instant::now();
 }
diff --git a/src/test/ui/did_you_mean/use_instead_of_import.rs b/src/test/ui/did_you_mean/use_instead_of_import.rs
index 59e83732328..2db7c240752 100644
--- a/src/test/ui/did_you_mean/use_instead_of_import.rs
+++ b/src/test/ui/did_you_mean/use_instead_of_import.rs
@@ -6,10 +6,18 @@ import std::{
     rc::Rc,
 };
 
+require std::time::Duration;
+//~^ ERROR expected item, found `require`
+
+include std::time::Instant;
+//~^ ERROR expected item, found `include`
+
 pub using std::io;
 //~^ ERROR expected item, found `using`
 
 fn main() {
     let x = Rc::new(1);
     let _ = write!(io::stdout(), "{:?}", x);
+    let _ = Duration::new(5, 0);
+    let _ = Instant::now();
 }
diff --git a/src/test/ui/did_you_mean/use_instead_of_import.stderr b/src/test/ui/did_you_mean/use_instead_of_import.stderr
index b22954af80f..2aac8f68c5e 100644
--- a/src/test/ui/did_you_mean/use_instead_of_import.stderr
+++ b/src/test/ui/did_you_mean/use_instead_of_import.stderr
@@ -4,11 +4,23 @@ error: expected item, found `import`
 LL | import std::{
    | ^^^^^^ help: items are imported using the `use` keyword
 
+error: expected item, found `require`
+  --> $DIR/use_instead_of_import.rs:9:1
+   |
+LL | require std::time::Duration;
+   | ^^^^^^^ help: items are imported using the `use` keyword
+
+error: expected item, found `include`
+  --> $DIR/use_instead_of_import.rs:12:1
+   |
+LL | include std::time::Instant;
+   | ^^^^^^^ help: items are imported using the `use` keyword
+
 error: expected item, found `using`
-  --> $DIR/use_instead_of_import.rs:9:5
+  --> $DIR/use_instead_of_import.rs:15:5
    |
 LL | pub using std::io;
    |     ^^^^^ help: items are imported using the `use` keyword
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/for-loop-while/break-while-condition.stderr b/src/test/ui/for-loop-while/break-while-condition.stderr
index 6960c4fd867..e79f6a75fde 100644
--- a/src/test/ui/for-loop-while/break-while-condition.stderr
+++ b/src/test/ui/for-loop-while/break-while-condition.stderr
@@ -31,6 +31,14 @@ LL | |             }
    |
    = note:   expected type `!`
            found unit type `()`
+note: the function expects a value to always be returned, but loops might run zero times
+  --> $DIR/break-while-condition.rs:24:13
+   |
+LL |             while false {
+   |             ^^^^^^^^^^^ this might have zero elements to iterate on
+LL |                 return
+   |                 ------ if the loop doesn't execute, this value would never get returned
+   = help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/let-else/issue-100103.rs b/src/test/ui/let-else/issue-100103.rs
new file mode 100644
index 00000000000..e393deab764
--- /dev/null
+++ b/src/test/ui/let-else/issue-100103.rs
@@ -0,0 +1,15 @@
+// edition:2021
+// check-pass
+
+#![feature(try_blocks)]
+#![feature(let_else)]
+
+fn main() {
+    let _: Result<i32, i32> = try {
+        let Some(x) = Some(0) else {
+            Err(1)?
+        };
+
+        x
+    };
+}
diff --git a/src/test/ui/typeck/issue-98982.rs b/src/test/ui/typeck/issue-98982.rs
new file mode 100644
index 00000000000..2553824bbfe
--- /dev/null
+++ b/src/test/ui/typeck/issue-98982.rs
@@ -0,0 +1,9 @@
+fn foo() -> i32 {
+    for i in 0..0 {
+    //~^ ERROR: mismatched types [E0308]
+        return i;
+    }
+    //~| help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
+}
+
+fn main() {}
diff --git a/src/test/ui/typeck/issue-98982.stderr b/src/test/ui/typeck/issue-98982.stderr
new file mode 100644
index 00000000000..3c9806ac965
--- /dev/null
+++ b/src/test/ui/typeck/issue-98982.stderr
@@ -0,0 +1,24 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-98982.rs:2:5
+   |
+LL |   fn foo() -> i32 {
+   |               --- expected `i32` because of return type
+LL | /     for i in 0..0 {
+LL | |
+LL | |         return i;
+LL | |     }
+   | |_____^ expected `i32`, found `()`
+   |
+note: the function expects a value to always be returned, but loops might run zero times
+  --> $DIR/issue-98982.rs:2:5
+   |
+LL |     for i in 0..0 {
+   |     ^^^^^^^^^^^^^ this might have zero elements to iterate on
+LL |
+LL |         return i;
+   |         -------- if the loop doesn't execute, this value would never get returned
+   = help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.