diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index f8da075e4bdc2..91a171c310783 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -522,6 +522,16 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } pub fn report(&self, err: BckError<'tcx>) { + // Catch and handle some particular cases. + match (&err.code, &err.cause) { + (&err_out_of_scope(ty::ReScope(_), ty::ReStatic), &euv::ClosureCapture(span)) | + (&err_out_of_scope(ty::ReScope(_), ty::ReFree(..)), &euv::ClosureCapture(span)) => { + return self.report_out_of_scope_escaping_closure_capture(&err, span); + } + _ => { } + } + + // General fallback. self.span_err( err.span, &self.bckerr_to_string(&err)); @@ -795,16 +805,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { format!("{} does not live long enough", msg) } err_borrowed_pointer_too_short(..) => { - let descr = match opt_loan_path(&err.cmt) { - Some(lp) => { - format!("`{}`", self.loan_path_to_string(&*lp)) - } - None => self.cmt_to_string(&*err.cmt), - }; - + let descr = self.cmt_to_path_or_string(&err.cmt); format!("lifetime of {} is too short to guarantee \ - its contents can be safely reborrowed", - descr) + its contents can be safely reborrowed", + descr) } } } @@ -886,6 +890,39 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } } + fn report_out_of_scope_escaping_closure_capture(&self, + err: &BckError<'tcx>, + capture_span: Span) + { + let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt); + + span_err!( + self.tcx.sess, err.span, E0371, + "closure may outlive the current function, \ + but it borrows {}, \ + which is owned by the current function", + cmt_path_or_string); + + self.tcx.sess.span_note( + capture_span, + &format!("{} is borrowed here", + cmt_path_or_string)); + + let suggestion = + match self.tcx.sess.codemap().span_to_snippet(err.span) { + Ok(string) => format!("move {}", string.lines().next().unwrap()), + Err(_) => format!("move || ") + }; + + self.tcx.sess.span_suggestion( + err.span, + &format!("to force the closure to take ownership of {} \ + (and any other referenced variables), \ + use the `move` keyword, as shown:", + cmt_path_or_string), + suggestion); + } + pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) { let code = err.code; match code { @@ -1033,6 +1070,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String { cmt.descriptive_string(self.tcx) } + + pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String { + match opt_loan_path(cmt) { + Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)), + None => self.cmt_to_string(cmt), + } + } } fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool { diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs new file mode 100644 index 0000000000000..7381bd0793dc7 --- /dev/null +++ b/src/librustc_borrowck/diagnostics.rs @@ -0,0 +1,17 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_snake_case)] + +register_diagnostics! { + E0371 // closure may outlive current fn, but it borrows {}, which is owned by current fn +} + +__build_diagnostic_array! { DIAGNOSTICS } diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index 54feed930a80d..647ea3555ba91 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -40,6 +40,10 @@ pub use borrowck::check_crate; pub use borrowck::build_borrowck_dataflow_data_for_fn; pub use borrowck::FnPartsWithCFG; +// NB: This module needs to be declared first so diagnostics are +// registered before they are used. +pub mod diagnostics; + mod borrowck; pub mod graphviz;