about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2017-03-24 11:48:44 -0400
committerNiko Matsakis <niko@alum.mit.edu>2017-03-30 07:55:29 -0400
commitf83706454fd00c2a3b7873b50223b68ca2cbd0d0 (patch)
tree6f84bed551903fe2500fed322e07c8055c4bcb3d /src
parentbad79484fb99d40f48e3b84776699526a37513e0 (diff)
downloadrust-f83706454fd00c2a3b7873b50223b68ca2cbd0d0.tar.gz
rust-f83706454fd00c2a3b7873b50223b68ca2cbd0d0.zip
coercion now depends on whether the expression diverges
Before I was checking this in `demand_coerce` but that's not really the
right place. The right place is to move that into the coercion routine
itself.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/_match.rs2
-rw-r--r--src/librustc_typeck/check/cast.rs10
-rw-r--r--src/librustc_typeck/check/coercion.rs46
-rw-r--r--src/librustc_typeck/check/demand.rs16
-rw-r--r--src/librustc_typeck/check/mod.rs18
5 files changed, 55 insertions, 37 deletions
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 5ca8f2c01c5..e83b786b9a8 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -504,7 +504,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     arm_span: arm.body.span,
                     source: match_src
                 });
-                coercion.coerce(self, &cause, &arm.body, arm_ty);
+                coercion.coerce(self, &cause, &arm.body, arm_ty, self.diverges.get());
             }
         }
 
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 441d427fe49..ea0aad007dd 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -38,7 +38,7 @@
 //! expression, `e as U2` is not necessarily so (in fact it will only be valid if
 //! `U1` coerces to `U2`).
 
-use super::FnCtxt;
+use super::{Diverges, FnCtxt};
 
 use lint;
 use hir::def_id::DefId;
@@ -376,7 +376,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
             (None, Some(t_cast)) => {
                 if let ty::TyFnDef(.., f) = self.expr_ty.sty {
                     // Attempt a coercion to a fn pointer type.
-                    let res = fcx.try_coerce(self.expr, self.expr_ty, fcx.tcx.mk_fn_ptr(f));
+                    let res = fcx.try_coerce(self.expr,
+                                             self.expr_ty,
+                                             Diverges::Maybe, // TODO
+                                             fcx.tcx.mk_fn_ptr(f));
                     if !res.is_ok() {
                         return Err(CastError::NonScalar);
                     }
@@ -542,7 +545,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
     }
 
     fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> bool {
-        fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty).is_ok()
+        // TODO
+        fcx.try_coerce(self.expr, self.expr_ty, Diverges::Maybe, self.cast_ty).is_ok()
     }
 }
 
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index eba58df781f..a737b82700e 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -60,7 +60,7 @@
 //! sort of a minor point so I've opted to leave it for later---after all
 //! we may want to adjust precisely when coercions occur.
 
-use check::FnCtxt;
+use check::{Diverges, FnCtxt};
 
 use rustc::hir;
 use rustc::hir::def_id::DefId;
@@ -156,7 +156,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         })
     }
 
-    fn coerce<E>(&self, exprs: &[E], a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
+    fn coerce<E>(&self,
+                 exprs: &[E],
+                 a: Ty<'tcx>,
+                 b: Ty<'tcx>)
+                 -> CoerceResult<'tcx>
         where E: AsCoercionSite
     {
         let a = self.shallow_resolve(a);
@@ -689,11 +693,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn try_coerce(&self,
                       expr: &hir::Expr,
                       expr_ty: Ty<'tcx>,
+                      expr_diverges: Diverges,
                       target: Ty<'tcx>)
                       -> RelateResult<'tcx, Ty<'tcx>> {
         let source = self.resolve_type_vars_with_obligations(expr_ty);
         debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
 
+        // Special-ish case: we can coerce any type `T` into the `!`
+        // type, but only if the source expression diverges.
+        if target.is_never() && expr_diverges.always() {
+            debug!("permit coercion to `!` because expr diverges");
+            return Ok(target);
+        }
+
         let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
         let coerce = Coerce::new(self, cause);
         self.commit_if_ok(|_| {
@@ -721,15 +733,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                 exprs: &[E],
                                 prev_ty: Ty<'tcx>,
                                 new: &hir::Expr,
-                                new_ty: Ty<'tcx>)
+                                new_ty: Ty<'tcx>,
+                                new_diverges: Diverges)
                                 -> RelateResult<'tcx, Ty<'tcx>>
         where E: AsCoercionSite
     {
-
         let prev_ty = self.resolve_type_vars_with_obligations(prev_ty);
         let new_ty = self.resolve_type_vars_with_obligations(new_ty);
         debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty);
 
+        // Special-ish case: we can coerce any type `T` into the `!`
+        // type, but only if the source expression diverges.
+        if prev_ty.is_never() && new_diverges.always() {
+            debug!("permit coercion to `!` because expr diverges");
+            return Ok(prev_ty);
+        }
+
         let trace = TypeTrace::types(cause, true, prev_ty, new_ty);
 
         // Special-case that coercion alone cannot handle:
@@ -982,9 +1001,10 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
                       fcx: &FnCtxt<'a, 'gcx, 'tcx>,
                       cause: &ObligationCause<'tcx>,
                       expression: &'gcx hir::Expr,
-                      expression_ty: Ty<'tcx>)
+                      expression_ty: Ty<'tcx>,
+                      expression_diverges: Diverges)
     {
-        self.coerce_inner(fcx, cause, Some(expression), expression_ty)
+        self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges)
     }
 
     /// Indicates that one of the inputs is a "forced unit". This
@@ -1002,7 +1022,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
         self.coerce_inner(fcx,
                           cause,
                           None,
-                          fcx.tcx.mk_nil())
+                          fcx.tcx.mk_nil(),
+                          Diverges::Maybe)
     }
 
     /// The inner coercion "engine". If `expression` is `None`, this
@@ -1012,7 +1033,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
                         fcx: &FnCtxt<'a, 'gcx, 'tcx>,
                         cause: &ObligationCause<'tcx>,
                         expression: Option<&'gcx hir::Expr>,
-                        mut expression_ty: Ty<'tcx>)
+                        mut expression_ty: Ty<'tcx>,
+                        expression_diverges: Diverges)
     {
         // Incorporate whatever type inference information we have
         // until now; in principle we might also want to process
@@ -1035,7 +1057,7 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
             if self.pushed == 0 {
                 // Special-case the first expression we are coercing.
                 // To be honest, I'm not entirely sure why we do this.
-                fcx.try_coerce(expression, expression_ty, self.expected_ty)
+                fcx.try_coerce(expression, expression_ty, expression_diverges, self.expected_ty)
             } else {
                 match self.expressions {
                     Expressions::Dynamic(ref exprs) =>
@@ -1043,13 +1065,15 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
                                                   exprs,
                                                   self.merged_ty(),
                                                   expression,
-                                                  expression_ty),
+                                                  expression_ty,
+                                                  expression_diverges),
                     Expressions::UpFront(ref coercion_sites) =>
                         fcx.try_find_coercion_lub(cause,
                                                   &coercion_sites[0..self.pushed],
                                                   self.merged_ty(),
                                                   expression,
-                                                  expression_ty),
+                                                  expression_ty,
+                                                  expression_diverges),
                 }
             }
         } else {
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 25d689b3c2c..e922c7447ff 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -77,21 +77,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                          expected: Ty<'tcx>) {
         let expected = self.resolve_type_vars_with_obligations(expected);
 
-        // If we are "assigning" to a `!` location, then we can permit
-        // any type to be assigned there, so long as we are in
-        // dead-code. This applies to e.g. `fn foo() -> ! { return; 22
-        // }` but also `fn foo() { let x: ! = { return; 22 }; }`.
-        //
-        // You might imagine that we could just *always* bail if we
-        // are in dead-code, but we don't want to do that, because
-        // that leaves a lot of type variables unconstrained. See
-        // e.g. #39808 and #39984.
-        let in_dead_code = self.diverges.get().always();
-        if expected.is_never() && in_dead_code {
-            return;
-        }
-
-        if let Err(e) = self.try_coerce(expr, checked_ty, expected) {
+        if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
             let cause = self.misc(expr.span);
             let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
             let mode = probe::Mode::MethodCall;
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 4b7d9a0fd3a..539f16ec670 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -366,7 +366,7 @@ impl UnsafetyState {
 /// as diverging), with some manual adjustments for control-flow
 /// primitives (approximating a CFG).
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
-enum Diverges {
+pub enum Diverges {
     /// Potentially unknown, some cases converge,
     /// others require a CFG to determine them.
     Maybe,
@@ -2833,7 +2833,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     .coerce(self,
                             &self.misc(return_expr.span),
                             return_expr,
-                            return_expr_ty);
+                            return_expr_ty,
+                            self.diverges.get());
     }
 
 
@@ -2864,13 +2865,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
 
         let if_cause = self.cause(sp, ObligationCauseCode::IfExpression);
-        coerce.coerce(self, &if_cause, then_expr, then_ty);
+        coerce.coerce(self, &if_cause, then_expr, then_ty, then_diverges);
 
         if let Some(else_expr) = opt_else_expr {
             let else_ty = self.check_expr_with_expectation(else_expr, expected);
             let else_diverges = self.diverges.get();
 
-            coerce.coerce(self, &if_cause, else_expr, else_ty);
+            coerce.coerce(self, &if_cause, else_expr, else_ty, else_diverges);
 
             // We won't diverge unless both branches do (or the condition does).
             self.diverges.set(cond_diverges | then_diverges & else_diverges);
@@ -3553,7 +3554,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
           }
           hir::ExprBreak(destination, ref expr_opt) => {
               if let Some(target_id) = destination.target_id.opt_id() {
-                  let (e_ty, cause);
+                  let (e_ty, e_diverges, cause);
                   if let Some(ref e) = *expr_opt {
                       // If this is a break with a value, we need to type-check
                       // the expression. Get an expected type from the loop context.
@@ -3572,11 +3573,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
                       // Recurse without `enclosing_breakables` borrowed.
                       e_ty = self.check_expr_with_hint(e, coerce_to);
+                      e_diverges = self.diverges.get();
                       cause = self.misc(e.span);
                   } else {
                       // Otherwise, this is a break *without* a value. That's
                       // always legal, and is equivalent to `break ()`.
                       e_ty = tcx.mk_nil();
+                      e_diverges = Diverges::Maybe;
                       cause = self.misc(expr.span);
                   }
 
@@ -3587,7 +3590,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                   let ctxt = enclosing_breakables.find_breakable(target_id);
                   if let Some(ref mut coerce) = ctxt.coerce {
                       if let Some(ref e) = *expr_opt {
-                          coerce.coerce(self, &cause, e, e_ty);
+                          coerce.coerce(self, &cause, e, e_ty, e_diverges);
                       } else {
                           assert!(e_ty.is_nil());
                           coerce.coerce_forced_unit(self, &cause);
@@ -3769,10 +3772,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                   let coerce_to = uty.unwrap_or_else(
                       || self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span)));
                   let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
+                  assert_eq!(self.diverges.get(), Diverges::Maybe);
                   for e in args {
                       let e_ty = self.check_expr_with_hint(e, coerce_to);
                       let cause = self.misc(e.span);
-                      coerce.coerce(self, &cause, e, e_ty);
+                      coerce.coerce(self, &cause, e, e_ty, self.diverges.get());
                   }
                   coerce.complete(self)
               } else {