]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_borrowck/borrowck/mod.rs
Merge remote-tracking branch 'origin/master' into gen
[rust.git] / src / librustc_borrowck / borrowck / mod.rs
index 8c79534d209ef8549dffb4c120cc6fc8e6785ad8..80ca16d23f52209c3a0a58cc3dfa88a4a2b7cfcf 100644 (file)
@@ -36,7 +36,7 @@
 use rustc::middle::free_region::RegionRelations;
 use rustc::ty::{self, TyCtxt};
 use rustc::ty::maps::Providers;
-
+use rustc::util::nodemap::FxHashMap;
 use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
 
 use std::fmt;
@@ -100,9 +100,8 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) {
     let body_id = tcx.hir.body_owned_by(owner_id);
     let tables = tcx.typeck_tables_of(owner_def_id);
     let region_maps = tcx.region_maps(owner_def_id);
-    let bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
-
-    let body = bccx.tcx.hir.body(body_id);
+    let body = tcx.hir.body(body_id);
+    let bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id, body };
 
     // Eventually, borrowck will always read the MIR, but at the
     // moment we do not. So, for now, we always force MIR to be
@@ -139,10 +138,9 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
 {
     // Check the body of fn items.
     let tcx = this.tcx;
-    let body = tcx.hir.body(body_id);
     let id_range = {
         let mut visitor = intravisit::IdRangeComputingVisitor::new(&tcx.hir);
-        visitor.visit_body(body);
+        visitor.visit_body(this.body);
         visitor.result()
     };
     let (all_loans, move_data) =
@@ -163,7 +161,7 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
     let mut loan_dfcx =
         DataFlowContext::new(this.tcx,
                              "borrowck",
-                             Some(body),
+                             Some(this.body),
                              cfg,
                              LoanDataFlowOperator,
                              id_range,
@@ -174,13 +172,13 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
                            loan.kill_scope.node_id(), loan_idx);
     }
     loan_dfcx.add_kills_from_flow_exits(cfg);
-    loan_dfcx.propagate(cfg, body);
+    loan_dfcx.propagate(cfg, this.body);
 
     let flowed_moves = move_data::FlowedMoveData::new(move_data,
                                                       this,
                                                       cfg,
                                                       id_range,
-                                                      body);
+                                                      this.body);
 
     Some(AnalysisData { all_loans,
                         loans: loan_dfcx,
@@ -199,7 +197,8 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
     let owner_def_id = tcx.hir.local_def_id(owner_id);
     let tables = tcx.typeck_tables_of(owner_def_id);
     let region_maps = tcx.region_maps(owner_def_id);
-    let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
+    let body = tcx.hir.body(body_id);
+    let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id, body };
 
     let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
     (bccx, dataflow_data.unwrap())
@@ -218,6 +217,8 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
     region_maps: Rc<RegionMaps>,
 
     owner_def_id: DefId,
+
+    body: &'tcx hir::Body,
 }
 
 impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> {
@@ -349,7 +350,7 @@ fn closure_to_block(closure_id: DefIndex,
     let closure_id = tcx.hir.def_index_to_node_id(closure_id);
     match tcx.hir.get(closure_id) {
         hir_map::NodeExpr(expr) => match expr.node {
-            hir::ExprClosure(.., body_id, _) => {
+            hir::ExprClosure(.., body_id, _, _) => {
                 body_id.node_id
             }
             _ => {
@@ -549,9 +550,7 @@ pub fn report(&self, err: BckError<'tcx>) {
             _ => { }
         }
 
-        let mut db = self.bckerr_to_diag(&err);
-        self.note_and_explain_bckerr(&mut db, err);
-        db.emit();
+        self.report_bckerr(&err);
     }
 
     pub fn report_use_of_moved_value(&self,
@@ -652,7 +651,7 @@ pub fn report_use_of_moved_value(&self,
 
             move_data::Captured =>
                 (match self.tcx.hir.expect_expr(the_move.id).node {
-                    hir::ExprClosure(.., fn_decl_span) => fn_decl_span,
+                    hir::ExprClosure(.., fn_decl_span, _) => fn_decl_span,
                     ref r => bug!("Captured({}) maps to non-closure: {:?}",
                                   the_move.id, r),
                 }, " (into closure)"),
@@ -735,8 +734,8 @@ pub fn span_err_with_code<S: Into<MultiSpan>>(&self, s: S, msg: &str, code: &str
         self.tcx.sess.span_err_with_code(s, msg, code);
     }
 
-    fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
-        let span = err.span.clone();
+    fn report_bckerr(&self, err: &BckError<'tcx>) {
+        let error_span = err.span.clone();
 
         match err.code {
             err_mutbl => {
@@ -760,12 +759,16 @@ fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
                     }
                 };
 
-                match err.cause {
+                let mut db = match err.cause {
                     MutabilityViolation => {
-                        struct_span_err!(self.tcx.sess, span, E0594, "cannot assign to {}", descr)
+                        struct_span_err!(self.tcx.sess,
+                                         error_span,
+                                         E0594,
+                                         "cannot assign to {}",
+                                         descr)
                     }
                     BorrowViolation(euv::ClosureCapture(_)) => {
-                        struct_span_err!(self.tcx.sess, span, E0595,
+                        struct_span_err!(self.tcx.sess, error_span, E0595,
                                          "closure cannot assign to {}", descr)
                     }
                     BorrowViolation(euv::OverloadedOperator) |
@@ -775,30 +778,228 @@ fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
                     BorrowViolation(euv::AutoUnsafe) |
                     BorrowViolation(euv::ForLoop) |
                     BorrowViolation(euv::MatchDiscriminant) => {
-                        struct_span_err!(self.tcx.sess, span, E0596,
+                        struct_span_err!(self.tcx.sess, error_span, E0596,
                                          "cannot borrow {} as mutable", descr)
                     }
                     BorrowViolation(euv::ClosureInvocation) => {
                         span_bug!(err.span,
                             "err_mutbl with a closure invocation");
                     }
-                }
+                };
+
+                self.note_and_explain_mutbl_error(&mut db, &err, &error_span);
+                self.note_immutability_blame(&mut db, err.cmt.immutability_blame());
+                db.emit();
             }
-            err_out_of_scope(..) => {
+            err_out_of_scope(super_scope, sub_scope, cause) => {
                 let msg = match opt_loan_path(&err.cmt) {
                     None => "borrowed value".to_string(),
                     Some(lp) => {
                         format!("`{}`", self.loan_path_to_string(&lp))
                     }
                 };
-                struct_span_err!(self.tcx.sess, span, E0597, "{} does not live long enough", msg)
+
+                // When you have a borrow that lives across a yield,
+                // that reference winds up captured in the generator
+                // type. Regionck then constraints it to live as long
+                // as the generator itself. If that borrow is borrowing
+                // data owned by the generator, this winds up resulting in
+                // an `err_out_of_scope` error:
+                //
+                // ```
+                // {
+                //     let g = || {
+                //         let a = &3; // this borrow is forced to ... -+
+                //         yield ();          //                        |
+                //         println!("{}", a); //                        |
+                //     };                     //                        |
+                // } <----------------------... live until here --------+
+                // ```
+                //
+                // To detect this case, we look for cases where the
+                // `super_scope` (lifetime of the value) is within the
+                // body, but the `sub_scope` is not.
+                debug!("err_out_of_scope: self.body.is_generator = {:?}",
+                       self.body.is_generator);
+                let maybe_borrow_across_yield = if self.body.is_generator {
+                    let body_extent = region::CodeExtent::Misc(self.body.id().node_id);
+                    debug!("err_out_of_scope: body_extent = {:?}", body_extent);
+                    debug!("err_out_of_scope: super_scope = {:?}", super_scope);
+                    debug!("err_out_of_scope: sub_scope = {:?}", sub_scope);
+                    match (super_scope, sub_scope) {
+                        (&ty::RegionKind::ReScope(value_extent),
+                         &ty::RegionKind::ReScope(loan_extent)) => {
+                            if {
+                                // value_extent <= body_extent &&
+                                self.region_maps.is_subscope_of(value_extent, body_extent) &&
+                                    // body_extent <= loan_extent
+                                    self.region_maps.is_subscope_of(body_extent, loan_extent)
+                            } {
+                                // We now know that this is a case
+                                // that fits the bill described above:
+                                // a borrow of something whose scope
+                                // is within the generator, but the
+                                // borrow is for a scope outside the
+                                // generator.
+                                //
+                                // Now look within the scope of the of
+                                // the value being borrowed (in the
+                                // example above, that would be the
+                                // block remainder that starts with
+                                // `let a`) for a yield. We can cite
+                                // that for the user.
+                                self.tcx.yield_in_extent(value_extent, &mut FxHashMap())
+                            } else {
+                                None
+                            }
+                        }
+                        _ => None,
+                    }
+                } else {
+                    None
+                };
+
+                if let Some(yield_span) = maybe_borrow_across_yield {
+                    debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span);
+                    struct_span_err!(self.tcx.sess,
+                                     error_span,
+                                     E0626,
+                                     "borrow may still be in use when generator yields")
+                        .span_label(yield_span, "possible yield occurs here")
+                        .emit();
+                    return;
+                }
+
+                let mut db = struct_span_err!(self.tcx.sess,
+                                              error_span,
+                                              E0597,
+                                              "{} does not live long enough",
+                                              msg);
+
+                let (value_kind, value_msg) = match err.cmt.cat {
+                    mc::Categorization::Rvalue(..) =>
+                        ("temporary value", "temporary value created here"),
+                    _ =>
+                        ("borrowed value", "borrow occurs here")
+                };
+
+                let is_closure = match cause {
+                    euv::ClosureCapture(s) => {
+                        // The primary span starts out as the closure creation point.
+                        // Change the primary span here to highlight the use of the variable
+                        // in the closure, because it seems more natural. Highlight
+                        // closure creation point as a secondary span.
+                        match db.span.primary_span() {
+                            Some(primary) => {
+                                db.span = MultiSpan::from_span(s);
+                                db.span_label(primary, "capture occurs here");
+                                db.span_label(s, "does not live long enough");
+                                true
+                            }
+                            None => false
+                        }
+                    }
+                    _ => {
+                        db.span_label(error_span, "does not live long enough");
+                        false
+                    }
+                };
+
+                let sub_span = self.region_end_span(sub_scope);
+                let super_span = self.region_end_span(super_scope);
+
+                match (sub_span, super_span) {
+                    (Some(s1), Some(s2)) if s1 == s2 => {
+                        if !is_closure {
+                            db.span = MultiSpan::from_span(s1);
+                            db.span_label(error_span, value_msg);
+                            let msg = match opt_loan_path(&err.cmt) {
+                                None => value_kind.to_string(),
+                                Some(lp) => {
+                                    format!("`{}`", self.loan_path_to_string(&lp))
+                                }
+                            };
+                            db.span_label(s1,
+                                          format!("{} dropped here while still borrowed", msg));
+                        } else {
+                            db.span_label(s1, format!("{} dropped before borrower", value_kind));
+                        }
+                        db.note("values in a scope are dropped in the opposite order \
+                                they are created");
+                    }
+                    (Some(s1), Some(s2)) if !is_closure => {
+                        db.span = MultiSpan::from_span(s2);
+                        db.span_label(error_span, value_msg);
+                        let msg = match opt_loan_path(&err.cmt) {
+                            None => value_kind.to_string(),
+                            Some(lp) => {
+                                format!("`{}`", self.loan_path_to_string(&lp))
+                            }
+                        };
+                        db.span_label(s2, format!("{} dropped here while still borrowed", msg));
+                        db.span_label(s1, format!("{} needs to live until here", value_kind));
+                    }
+                    _ => {
+                        match sub_span {
+                            Some(s) => {
+                                db.span_label(s, format!("{} needs to live until here",
+                                                          value_kind));
+                            }
+                            None => {
+                                self.tcx.note_and_explain_region(
+                                    &mut db,
+                                    "borrowed value must be valid for ",
+                                    sub_scope,
+                                    "...");
+                            }
+                        }
+                        match super_span {
+                            Some(s) => {
+                                db.span_label(s, format!("{} only lives until here", value_kind));
+                            }
+                            None => {
+                                self.tcx.note_and_explain_region(
+                                    &mut db,
+                                    "...but borrowed value is only valid for ",
+                                    super_scope,
+                                    "");
+                            }
+                        }
+                    }
+                }
+
+                if let Some(_) = statement_scope_span(self.tcx, super_scope) {
+                    db.note("consider using a `let` binding to increase its lifetime");
+                }
+
+                db.emit();
             }
-            err_borrowed_pointer_too_short(..) => {
+            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
                 let descr = self.cmt_to_path_or_string(&err.cmt);
-                struct_span_err!(self.tcx.sess, span, E0598,
-                                 "lifetime of {} is too short to guarantee \
-                                  its contents can be safely reborrowed",
-                                 descr)
+                let mut db = struct_span_err!(self.tcx.sess, error_span, E0598,
+                                              "lifetime of {} is too short to guarantee \
+                                               its contents can be safely reborrowed",
+                                              descr);
+
+                let descr = match opt_loan_path(&err.cmt) {
+                    Some(lp) => {
+                        format!("`{}`", self.loan_path_to_string(&lp))
+                    }
+                    None => self.cmt_to_string(&err.cmt),
+                };
+                self.tcx.note_and_explain_region(
+                    &mut db,
+                    &format!("{} would have to be valid for ",
+                            descr),
+                    loan_scope,
+                    "...");
+                self.tcx.note_and_explain_region(
+                    &mut db,
+                    &format!("...but {} is only valid for ", descr),
+                    ptr_scope,
+                    "");
+
+                db.emit();
             }
         }
     }
@@ -1061,133 +1262,6 @@ fn region_end_span(&self, region: ty::Region<'tcx>) -> Option<Span> {
         }
     }
 
-    fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) {
-        let error_span = err.span.clone();
-        match err.code {
-            err_mutbl => {
-                self.note_and_explain_mutbl_error(db, &err, &error_span);
-                self.note_immutability_blame(db, err.cmt.immutability_blame());
-            }
-            err_out_of_scope(super_scope, sub_scope, cause) => {
-                let (value_kind, value_msg) = match err.cmt.cat {
-                    mc::Categorization::Rvalue(..) =>
-                        ("temporary value", "temporary value created here"),
-                    _ =>
-                        ("borrowed value", "borrow occurs here")
-                };
-
-                let is_closure = match cause {
-                    euv::ClosureCapture(s) => {
-                        // The primary span starts out as the closure creation point.
-                        // Change the primary span here to highlight the use of the variable
-                        // in the closure, because it seems more natural. Highlight
-                        // closure creation point as a secondary span.
-                        match db.span.primary_span() {
-                            Some(primary) => {
-                                db.span = MultiSpan::from_span(s);
-                                db.span_label(primary, "capture occurs here");
-                                db.span_label(s, "does not live long enough");
-                                true
-                            }
-                            None => false
-                        }
-                    }
-                    _ => {
-                        db.span_label(error_span, "does not live long enough");
-                        false
-                    }
-                };
-
-                let sub_span = self.region_end_span(sub_scope);
-                let super_span = self.region_end_span(super_scope);
-
-                match (sub_span, super_span) {
-                    (Some(s1), Some(s2)) if s1 == s2 => {
-                        if !is_closure {
-                            db.span = MultiSpan::from_span(s1);
-                            db.span_label(error_span, value_msg);
-                            let msg = match opt_loan_path(&err.cmt) {
-                                None => value_kind.to_string(),
-                                Some(lp) => {
-                                    format!("`{}`", self.loan_path_to_string(&lp))
-                                }
-                            };
-                            db.span_label(s1,
-                                          format!("{} dropped here while still borrowed", msg));
-                        } else {
-                            db.span_label(s1, format!("{} dropped before borrower", value_kind));
-                        }
-                        db.note("values in a scope are dropped in the opposite order \
-                                they are created");
-                    }
-                    (Some(s1), Some(s2)) if !is_closure => {
-                        db.span = MultiSpan::from_span(s2);
-                        db.span_label(error_span, value_msg);
-                        let msg = match opt_loan_path(&err.cmt) {
-                            None => value_kind.to_string(),
-                            Some(lp) => {
-                                format!("`{}`", self.loan_path_to_string(&lp))
-                            }
-                        };
-                        db.span_label(s2, format!("{} dropped here while still borrowed", msg));
-                        db.span_label(s1, format!("{} needs to live until here", value_kind));
-                    }
-                    _ => {
-                        match sub_span {
-                            Some(s) => {
-                                db.span_label(s, format!("{} needs to live until here",
-                                                          value_kind));
-                            }
-                            None => {
-                                self.tcx.note_and_explain_region(
-                                    db,
-                                    "borrowed value must be valid for ",
-                                    sub_scope,
-                                    "...");
-                            }
-                        }
-                        match super_span {
-                            Some(s) => {
-                                db.span_label(s, format!("{} only lives until here", value_kind));
-                            }
-                            None => {
-                                self.tcx.note_and_explain_region(
-                                    db,
-                                    "...but borrowed value is only valid for ",
-                                    super_scope,
-                                    "");
-                            }
-                        }
-                    }
-                }
-
-                if let Some(_) = statement_scope_span(self.tcx, super_scope) {
-                    db.note("consider using a `let` binding to increase its lifetime");
-                }
-            }
-
-            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
-                let descr = match opt_loan_path(&err.cmt) {
-                    Some(lp) => {
-                        format!("`{}`", self.loan_path_to_string(&lp))
-                    }
-                    None => self.cmt_to_string(&err.cmt),
-                };
-                self.tcx.note_and_explain_region(
-                    db,
-                    &format!("{} would have to be valid for ",
-                            descr),
-                    loan_scope,
-                    "...");
-                self.tcx.note_and_explain_region(
-                    db,
-                    &format!("...but {} is only valid for ", descr),
-                    ptr_scope,
-                    "");
-            }
-        }
-    }
-
     fn note_and_explain_mutbl_error(&self, db: &mut DiagnosticBuilder, err: &BckError<'tcx>,
                                     error_span: &Span) {
         match err.cmt.note {