about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2017-03-17 11:48:18 -0400
committerNiko Matsakis <niko@alum.mit.edu>2017-03-30 07:55:29 -0400
commit1ae620bbebadbe4362a28b37bcecd41cfed66cea (patch)
tree9acf3cbd13242401e66ce902d37510540c40599d /src
parent4967f1ae57120b02639f3d1493e8f0c9e168b0dc (diff)
downloadrust-1ae620bbebadbe4362a28b37bcecd41cfed66cea.tar.gz
rust-1ae620bbebadbe4362a28b37bcecd41cfed66cea.zip
do not eagerly convert `!` to a diverging variable
Instead, wait until coercion time.  This has some small effects on a few
tests (one less temporary, generally better errors when trying to call
methods or otherwise "force" the type).
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/coercion.rs26
-rw-r--r--src/librustc_typeck/check/mod.rs79
-rw-r--r--src/test/compile-fail/index-bot.rs2
-rw-r--r--src/test/compile-fail/issue-13847.rs2
-rw-r--r--src/test/compile-fail/issue-15207.rs2
-rw-r--r--src/test/compile-fail/issue-15965.rs2
-rw-r--r--src/test/compile-fail/issue-17373.rs2
-rw-r--r--src/test/compile-fail/issue-18532.rs3
8 files changed, 71 insertions, 47 deletions
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 40ae169a94e..a08faf2610e 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -169,7 +169,23 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         }
 
         if a.is_never() {
-            return success(Adjust::NeverToAny, b, vec![]);
+            // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
+            // type variable, we want `?T` to fallback to `!` if not
+            // otherwise constrained. An example where this arises:
+            //
+            //     let _: Option<?T> = Some({ return; });
+            //
+            // here, we would coerce from `!` to `?T`.
+            let b = self.shallow_resolve(b);
+            return if self.shallow_resolve(b).is_ty_var() {
+                // micro-optimization: no need for this if `b` is
+                // already resolved in some way.
+                let diverging_ty = self.next_diverging_ty_var(
+                    TypeVariableOrigin::AdjustmentType(self.cause.span));
+                self.unify_and(&b, &diverging_ty, Adjust::NeverToAny)
+            } else {
+                success(Adjust::NeverToAny, b, vec![])
+            };
         }
 
         // Consider coercing the subtype to a DST
@@ -687,11 +703,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             let adjustment = self.register_infer_ok_obligations(ok);
             if !adjustment.is_identity() {
                 debug!("Success, coerced with {:?}", adjustment);
-                match self.tables.borrow().adjustments.get(&expr.id) {
-                    None |
-                    Some(&Adjustment { kind: Adjust::NeverToAny, .. }) => (),
-                    _ => bug!("expr already has an adjustment on it!"),
-                };
+                if self.tables.borrow().adjustments.get(&expr.id).is_some() {
+                    bug!("expr already has an adjustment on it!");
+                }
                 self.write_adjustment(expr.id, adjustment);
             }
             Ok(adjustment.target)
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index e186daf9f40..ae0ed81ac77 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -2664,7 +2664,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn check_expr_has_type(&self,
                                expr: &'gcx hir::Expr,
                                expected: Ty<'tcx>) -> Ty<'tcx> {
-        let ty = self.check_expr_with_hint(expr, expected);
+        let mut ty = self.check_expr_with_hint(expr, expected);
+
+        // While we don't allow *arbitrary* coercions here, we *do* allow
+        // coercions from ! to `expected`.
+        if ty.is_never() {
+            assert!(!self.tables.borrow().adjustments.contains_key(&expr.id),
+                    "expression with never type wound up being adjusted");
+            let adj_ty = self.next_diverging_ty_var(
+                TypeVariableOrigin::AdjustmentType(expr.span));
+            self.write_adjustment(expr.id, adjustment::Adjustment {
+                kind: adjustment::Adjust::NeverToAny,
+                target: adj_ty
+            });
+            ty = adj_ty;
+        }
+
         self.demand_suptype(expr.span, expected, ty);
         ty
     }
@@ -3370,18 +3385,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         debug!("type of {} is...", self.tcx.hir.node_to_string(expr.id));
         debug!("... {:?}, expected is {:?}", ty, expected);
 
-        // Add adjustments to !-expressions
-        if ty.is_never() {
-            if let Some(hir::map::NodeExpr(node_expr)) = self.tcx.hir.find(expr.id) {
-                let adj_ty = self.next_diverging_ty_var(
-                    TypeVariableOrigin::AdjustmentType(node_expr.span));
-                self.write_adjustment(expr.id, adjustment::Adjustment {
-                    kind: adjustment::Adjust::NeverToAny,
-                    target: adj_ty
-                });
-                return adj_ty;
-            }
-        }
         ty
     }
 
@@ -4072,7 +4075,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn check_block_no_value(&self, blk: &'gcx hir::Block)  {
         let unit = self.tcx.mk_nil();
         let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
-        self.demand_suptype(blk.span, unit, ty);
+
+        // if the block produces a `!` value, that can always be
+        // (effectively) coerced to unit.
+        if !ty.is_never() {
+            self.demand_suptype(blk.span, unit, ty);
+        }
     }
 
     fn check_block_with_expected(&self,
@@ -4111,7 +4119,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     },
                     None => {
                         e_ty = if self.diverges.get().always() {
-                            self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span))
+                            self.tcx.types.never
                         } else {
                             self.tcx.mk_nil()
                         };
@@ -4135,6 +4143,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     Err(err) =>
                         self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(),
                 }
+            } else if self.diverges.get().always() {
+                // No tail expression and the body diverges; ignore
+                // the expected type, and keep `!` as the type of the
+                // block.
             } else {
                 self.check_block_no_expr(blk, self.tcx.mk_nil(), e_ty);
             };
@@ -4147,33 +4159,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
             let mut ty = match blk.expr {
                 Some(ref e) => self.check_expr_with_expectation(e, expected),
-                None => self.tcx.mk_nil()
+                None => if self.diverges.get().always() {
+                    self.tcx.types.never
+                } else {
+                    self.tcx.mk_nil()
+                },
             };
 
-            if self.diverges.get().always() {
-                if let ExpectHasType(ety) = expected {
-                    // Avoid forcing a type (only `!` for now) in unreachable code.
-                    // FIXME(aburka) do we need this special case? and should it be is_uninhabited?
-                    if !ety.is_never() {
-                        if let Some(ref e) = blk.expr {
-                            // Coerce the tail expression to the right type.
-                            self.demand_coerce(e, ty, ety);
-                        }
-                    }
-                }
-
-                ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
-            } else if let ExpectHasType(ety) = expected {
+            if let ExpectHasType(ety) = expected {
                 if let Some(ref e) = blk.expr {
                     // Coerce the tail expression to the right type.
                     self.demand_coerce(e, ty, ety);
+
+                    // We already applied the type (and potentially errored),
+                    // use the expected type to avoid further errors out.
+                    ty = ety;
+                } else if self.diverges.get().always() {
+                    // No tail expression and the body diverges; ignore
+                    // the expected type, and keep `!` as the type of the
+                    // block.
                 } else {
                     self.check_block_no_expr(blk, ty, ety);
-                }
 
-                // We already applied the type (and potentially errored),
-                // use the expected type to avoid further errors out.
-                ty = ety;
+                    // We already applied the type (and potentially errored),
+                    // use the expected type to avoid further errors out.
+                    ty = ety;
+                }
             }
             ty
         };
diff --git a/src/test/compile-fail/index-bot.rs b/src/test/compile-fail/index-bot.rs
index 70c362303ae..05b04723300 100644
--- a/src/test/compile-fail/index-bot.rs
+++ b/src/test/compile-fail/index-bot.rs
@@ -9,5 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    (return)[0]; //~ ERROR the type of this value must be known in this context
+    (return)[0]; //~ ERROR cannot index a value of type `!`
 }
diff --git a/src/test/compile-fail/issue-13847.rs b/src/test/compile-fail/issue-13847.rs
index aa823d9a70e..0314f109a7c 100644
--- a/src/test/compile-fail/issue-13847.rs
+++ b/src/test/compile-fail/issue-13847.rs
@@ -9,5 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    return.is_failure //~ ERROR the type of this value must be known in this context
+    return.is_failure //~ ERROR no field `is_failure` on type `!`
 }
diff --git a/src/test/compile-fail/issue-15207.rs b/src/test/compile-fail/issue-15207.rs
index 61877775269..70da8cf4169 100644
--- a/src/test/compile-fail/issue-15207.rs
+++ b/src/test/compile-fail/issue-15207.rs
@@ -10,7 +10,7 @@
 
 fn main() {
     loop {
-        break.push(1) //~ ERROR the type of this value must be known in this context
+        break.push(1) //~ ERROR no method named `push` found for type `!`
         ;
     }
 }
diff --git a/src/test/compile-fail/issue-15965.rs b/src/test/compile-fail/issue-15965.rs
index 08b896f387b..d5d597c190e 100644
--- a/src/test/compile-fail/issue-15965.rs
+++ b/src/test/compile-fail/issue-15965.rs
@@ -11,7 +11,7 @@
 fn main() {
     return
         { return () }
-//~^ ERROR the type of this value must be known in this context
+//~^ ERROR expected function, found `!`
     ()
     ;
 }
diff --git a/src/test/compile-fail/issue-17373.rs b/src/test/compile-fail/issue-17373.rs
index 6895893adc4..f6e6a8a0852 100644
--- a/src/test/compile-fail/issue-17373.rs
+++ b/src/test/compile-fail/issue-17373.rs
@@ -9,6 +9,6 @@
 // except according to those terms.
 
 fn main() {
-    *return //~ ERROR the type of this value must be known in this context
+    *return //~ ERROR type `!` cannot be dereferenced
     ;
 }
diff --git a/src/test/compile-fail/issue-18532.rs b/src/test/compile-fail/issue-18532.rs
index 94eab97c42a..2be5fdcac4e 100644
--- a/src/test/compile-fail/issue-18532.rs
+++ b/src/test/compile-fail/issue-18532.rs
@@ -13,6 +13,5 @@
 // into it.
 
 fn main() {
-    (return)((),());
-    //~^ ERROR the type of this value must be known
+    (return)((),()); //~ ERROR expected function, found `!`
 }