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:50:36 -0400
committerNiko Matsakis <niko@alum.mit.edu>2017-03-30 07:56:58 -0400
commitd448329849bb2cef355cabfdc6cb4d8b5b76b80e (patch)
tree06e11b25dc40e561829c423ae226b9637ecc29a6 /src
parentf83706454fd00c2a3b7873b50223b68ca2cbd0d0 (diff)
downloadrust-d448329849bb2cef355cabfdc6cb4d8b5b76b80e.tar.gz
rust-d448329849bb2cef355cabfdc6cb4d8b5b76b80e.zip
fix handling of blocks with `CoerceMany`
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/coercion.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs137
2 files changed, 51 insertions, 88 deletions
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index a737b82700e..8a9db674c6e 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -926,7 +926,7 @@ pub struct CoerceMany<'gcx, 'tcx, 'exprs, E>
 
 /// The type of a `CoerceMany` that is storing up the expressions into
 /// a buffer. We use this in `check/mod.rs` for things like `break`.
-pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'static, hir::Expr>;
+pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'gcx, P<hir::Expr>>;
 
 #[derive(Clone)] // (*)
 enum Expressions<'gcx, 'exprs, E>
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 539f16ec670..413d09117af 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -86,6 +86,7 @@ use dep_graph::DepNode;
 use fmt_macros::{Parser, Piece, Position};
 use hir::def::{Def, CtorKind};
 use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
+use rustc_back::slice::ref_slice;
 use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin, TypeTrace};
 use rustc::infer::type_variable::{self, TypeVariableOrigin};
 use rustc::ty::subst::{Kind, Subst, Substs};
@@ -4108,102 +4109,64 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             replace(&mut *fcx_ps, unsafety_state)
         };
 
-        let mut ty = if blk.targeted_by_break {
-            let unified = self.next_ty_var(TypeVariableOrigin::TypeInference(blk.span));
-            let coerce_to = expected.only_has_type(self).unwrap_or(unified);
-            let ctxt = BreakableCtxt {
-                unified: unified,
-                coerce_to: coerce_to,
-                break_exprs: vec![],
-                may_break: false,
+        // In some cases, blocks have just one exit, but other blocks
+        // can be targeted by multiple breaks. This cannot happen in
+        // normal Rust syntax today, but it can happen when we desugar
+        // a `do catch { ... }` expression.
+        //
+        // Example 1:
+        //
+        //    'a: { if true { break 'a Err(()); } Ok(()) }
+        //
+        // Here we would wind up with two coercions, one from
+        // `Err(())` and the other from the tail expression
+        // `Ok(())`. If the tail expression is omitted, that's a
+        // "forced unit" -- unless the block diverges, in which
+        // case we can ignore the tail expression (e.g., `'a: {
+        // break 'a 22; }` would not force the type of the block
+        // to be `()`).
+        let tail_expr = blk.expr.as_ref();
+        let coerce_to_ty = expected.coercion_target_type(self, blk.span);
+        let coerce = if blk.targeted_by_break {
+            CoerceMany::new(coerce_to_ty)
+        } else {
+            let tail_expr: &[P<hir::Expr>] = match tail_expr {
+                Some(e) => ref_slice(e),
+                None => &[],
             };
+            CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
+        };
 
-            let (mut ctxt, (e_ty, cause)) = self.with_breakable_ctxt(blk.id, ctxt, || {
-                for s in &blk.stmts {
-                    self.check_stmt(s);
-                }
-                let coerce_to = {
-                    let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
-                    enclosing_breakables.find_breakable(blk.id).coerce_to
-                };
-                let e_ty;
-                let cause;
-                match blk.expr {
-                    Some(ref e) => {
-                        e_ty = self.check_expr_with_hint(e, coerce_to);
-                        cause = self.misc(e.span);
-                    },
-                    None => {
-                        e_ty = if self.diverges.get().always() {
-                            self.tcx.types.never
-                        } else {
-                            self.tcx.mk_nil()
-                        };
-                        cause = self.misc(blk.span);
-                    }
-                };
-
-                (e_ty, cause)
-            });
-
-        if let ExpectHasType(ety) = expected {
-            if let Some(ref e) = blk.expr {
-                let result = if !ctxt.may_break {
-                    self.try_coerce(e, e_ty, ctxt.coerce_to)
-                } else {
-                    self.try_find_coercion_lub(&cause, || ctxt.break_exprs.iter().cloned(),
-                                               ctxt.unified, e, e_ty)
-                };
-                match result {
-                    Ok(ty) => ctxt.unified = ty,
-                    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);
-            };
+        let ctxt = BreakableCtxt {
+            coerce: Some(coerce),
+            may_break: false,
+        };
 
-            ctxt.unified
-        } else {
+        let (ctxt, ()) = self.with_breakable_ctxt(blk.id, ctxt, || {
             for s in &blk.stmts {
                 self.check_stmt(s);
             }
 
-            let mut ty = match blk.expr {
-                Some(ref e) => self.check_expr_with_expectation(e, expected),
-                None => if self.diverges.get().always() {
-                    self.tcx.types.never
-                } else {
-                    self.tcx.mk_nil()
-                },
-            };
+            // check the tail expression **without** holding the
+            // `enclosing_breakables` lock below.
+            let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, 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;
-                }
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            let mut ctxt = enclosing_breakables.find_breakable(blk.id);
+            let mut coerce = ctxt.coerce.as_mut().unwrap();
+            if let Some(tail_expr_ty) = tail_expr_ty {
+                let tail_expr = tail_expr.unwrap();
+                coerce.coerce(self,
+                              &self.misc(tail_expr.span),
+                              tail_expr,
+                              tail_expr_ty,
+                              self.diverges.get()); // TODO
+            } else if !self.diverges.get().always() {
+                coerce.coerce_forced_unit(self, &self.misc(blk.span));
             }
-            ty
-        };
+        });
+
+        let mut ty = ctxt.coerce.unwrap().complete(self);
 
         if self.has_errors.get() || ty.references_error() {
             ty = self.tcx.types.err