about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCameron Steffen <cam.steffen94@gmail.com>2021-07-30 11:30:12 -0500
committerCameron Steffen <cam.steffen94@gmail.com>2021-08-30 20:18:42 -0500
commit2f4e86b9ef457ff7b465e73e3aaabfcaa1d9d8e1 (patch)
tree0a3ddf2ab5a4753d2b99b64742019c5fde1b5c7f
parentcb4439a3150308490a00773aa4f2a5ef23586a42 (diff)
downloadrust-2f4e86b9ef457ff7b465e73e3aaabfcaa1d9d8e1.tar.gz
rust-2f4e86b9ef457ff7b465e73e3aaabfcaa1d9d8e1.zip
Enforce diverging let...else
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs5
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs9
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs21
4 files changed, 33 insertions, 5 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 299dcf5f17a..d54933841fd 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -781,6 +781,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     );
                 }
             }
+            ObligationCauseCode::LetElse => {
+                err.help("try adding a diverging expression, such as `return` or `panic!(..)`");
+                err.help("...or use `match` instead of `let...else`");
+            }
             _ => (),
         }
     }
@@ -2592,6 +2596,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
             }
             IfExpression { .. } => Error0308("`if` and `else` have incompatible types"),
             IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"),
+            LetElse => Error0308("`else` clause of `let...else` does not diverge"),
             MainFunctionType => Error0580("`main` function has wrong type"),
             StartFunctionType => Error0308("`#[start]` function has wrong type"),
             IntrinsicType => Error0308("intrinsic has wrong type"),
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 676cb7fe41d..74edb17fe32 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -305,6 +305,9 @@ pub enum ObligationCauseCode<'tcx> {
     /// Intrinsic has wrong type
     IntrinsicType,
 
+    /// A let else block does not diverge
+    LetElse,
+
     /// Method receiver
     MethodReceiver,
 
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 40841a6e32d..db3432b0142 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1928,7 +1928,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             | ObligationCauseCode::OpaqueType
             | ObligationCauseCode::MiscObligation
             | ObligationCauseCode::WellFormed(..)
-            | ObligationCauseCode::MatchImpl(..) => {}
+            | ObligationCauseCode::MatchImpl(..)
+            | ObligationCauseCode::ReturnType
+            | ObligationCauseCode::ReturnValue(_)
+            | ObligationCauseCode::BlockTailExpression(_)
+            | ObligationCauseCode::LetElse => {}
             ObligationCauseCode::SliceOrArrayElem => {
                 err.note("slice and array elements must have `Sized` type");
             }
@@ -2338,9 +2342,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     predicate
                 ));
             }
-            ObligationCauseCode::ReturnType
-            | ObligationCauseCode::ReturnValue(_)
-            | ObligationCauseCode::BlockTailExpression(_) => (),
             ObligationCauseCode::TrivialBound => {
                 err.help("see issue #48214");
                 if tcx.sess.opts.unstable_features.is_nightly_build() {
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 51c646e500c..10f5b000aca 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -849,7 +849,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
 
         if let Some(else_expr) = opt_else_expr {
-            let else_ty = self.check_expr_with_expectation(else_expr, expected);
+            let else_ty = if sp.desugaring_kind() == Some(DesugaringKind::LetElse) {
+                // todo introduce `check_expr_with_expectation(.., Expectation::LetElse)`
+                //   for errors that point to the offending expression rather than the entire block.
+                //   We could use `check_expr_eq_type(.., tcx.types.never)`, but then there is no
+                //   way to detect that the expected type originated from let-else and provide
+                //   a customized error.
+                let else_ty = self.check_expr(else_expr);
+                let cause = self.cause(else_expr.span, ObligationCauseCode::LetElse);
+
+                if let Some(mut err) =
+                    self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
+                {
+                    err.emit();
+                    self.tcx.ty_error()
+                } else {
+                    else_ty
+                }
+            } else {
+                self.check_expr_with_expectation(else_expr, expected)
+            };
             let else_diverges = self.diverges.get();
 
             let opt_suggest_box_span =