Skip to content

Commit 3dec727

Browse files
committed
auto merge of #17869 : bkoropoff/rust/bound-all-the-upvars, r=nikomatsakis
This PR is based on #17784, which fixes closure soundness problems in borrowck. Only the last two commits are unique to this PR. My understanding of regionck is still evolving, so I'm not sure if this is the right approach. Feedback is appreciated. - In `link_reborrowed_region`, we account for the ability of upvars to change their mutability due to later processing. A map of recursive region links we may want to establish in the future is maintained, with the links being established when the mutability of the borrow is adjusted. - When asked to establish a region link for an upvar, we link it to the region of the closure body. This creates the necessary constraint to stop unsound reborrows from the closure environment. This partially (maybe completely) solves issue #17403. Remaining work: - This is only known to help with by-ref upvars. I have not looked at by-value upvars yet to see if they can cause problems. - The error diagnostics that result from failed region inference are pretty inscrutible.
2 parents 1600e0b + fdd69ac commit 3dec727

18 files changed

+526
-284
lines changed

src/librustc/metadata/tydecode.rs

+1
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ fn parse_bound_region(st: &mut PState, conv: conv_did) -> ty::BoundRegion {
277277
assert_eq!(next(st), '|');
278278
ty::BrFresh(id)
279279
}
280+
'e' => ty::BrEnv,
280281
_ => fail!("parse_bound_region: bad input")
281282
}
282283
}

src/librustc/metadata/tyencode.rs

+3
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ fn enc_bound_region(w: &mut SeekableMemWriter, cx: &ctxt, br: ty::BoundRegion) {
175175
ty::BrFresh(id) => {
176176
mywrite!(w, "f{}|", id);
177177
}
178+
ty::BrEnv => {
179+
mywrite!(w, "e|");
180+
}
178181
}
179182
}
180183

src/librustc/middle/astencode.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,8 @@ impl tr for ty::BoundRegion {
516516
fn tr(&self, dcx: &DecodeContext) -> ty::BoundRegion {
517517
match *self {
518518
ty::BrAnon(_) |
519-
ty::BrFresh(_) => *self,
519+
ty::BrFresh(_) |
520+
ty::BrEnv => *self,
520521
ty::BrNamed(id, ident) => ty::BrNamed(dcx.tr_def_id(id),
521522
ident),
522523
}

src/librustc/middle/borrowck/check_loans.rs

+24-23
Original file line numberDiff line numberDiff line change
@@ -775,21 +775,32 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
775775
}
776776

777777
// Otherwise, just a plain error.
778-
match opt_loan_path(&assignee_cmt) {
779-
Some(lp) => {
780-
self.bccx.span_err(
781-
assignment_span,
782-
format!("cannot assign to {} {} `{}`",
783-
assignee_cmt.mutbl.to_user_str(),
784-
self.bccx.cmt_to_string(&*assignee_cmt),
785-
self.bccx.loan_path_to_string(&*lp)).as_slice());
786-
}
787-
None => {
778+
match assignee_cmt.note {
779+
mc::NoteClosureEnv(upvar_id) => {
788780
self.bccx.span_err(
789781
assignment_span,
790-
format!("cannot assign to {} {}",
791-
assignee_cmt.mutbl.to_user_str(),
782+
format!("cannot assign to {}",
792783
self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
784+
self.bccx.span_note(
785+
self.tcx().map.span(upvar_id.closure_expr_id),
786+
"consider changing this closure to take self by mutable reference");
787+
}
788+
_ => match opt_loan_path(&assignee_cmt) {
789+
Some(lp) => {
790+
self.bccx.span_err(
791+
assignment_span,
792+
format!("cannot assign to {} {} `{}`",
793+
assignee_cmt.mutbl.to_user_str(),
794+
self.bccx.cmt_to_string(&*assignee_cmt),
795+
self.bccx.loan_path_to_string(&*lp)).as_slice());
796+
}
797+
None => {
798+
self.bccx.span_err(
799+
assignment_span,
800+
format!("cannot assign to {} {}",
801+
assignee_cmt.mutbl.to_user_str(),
802+
self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
803+
}
793804
}
794805
}
795806
return;
@@ -805,16 +816,12 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
805816
loop {
806817
debug!("mark_variable_as_used_mut(cmt={})", cmt.repr(this.tcx()));
807818
match cmt.cat.clone() {
808-
mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: id, .. }) |
819+
mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: id, .. }, .. }) |
809820
mc::cat_local(id) => {
810821
this.tcx().used_mut_nodes.borrow_mut().insert(id);
811822
return;
812823
}
813824

814-
mc::cat_upvar(..) => {
815-
return;
816-
}
817-
818825
mc::cat_rvalue(..) |
819826
mc::cat_static_item |
820827
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
@@ -854,12 +861,6 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
854861
check_for_aliasability_violation(this, span, b.clone());
855862
}
856863

857-
mc::cat_copied_upvar(mc::CopiedUpvar {
858-
kind: mc::Unboxed(ty::FnUnboxedClosureKind), ..}) => {
859-
// Prohibit writes to capture-by-move upvars in non-once closures
860-
check_for_aliasability_violation(this, span, guarantor.clone());
861-
}
862-
863864
_ => {}
864865
}
865866

src/librustc/middle/borrowck/gather_loans/gather_moves.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -133,19 +133,13 @@ fn check_and_get_illegal_move_origin(bccx: &BorrowckCtxt,
133133
mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
134134
mc::cat_deref(_, _, mc::Implicit(..)) |
135135
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
136-
mc::cat_upvar(..) | mc::cat_static_item => {
136+
mc::cat_static_item => {
137137
Some(cmt.clone())
138138
}
139139

140-
mc::cat_copied_upvar(mc::CopiedUpvar { kind: kind, .. }) => {
141-
match kind.onceness() {
142-
ast::Once => None,
143-
ast::Many => Some(cmt.clone())
144-
}
145-
}
146-
147140
mc::cat_rvalue(..) |
148-
mc::cat_local(..) => {
141+
mc::cat_local(..) |
142+
mc::cat_upvar(..) => {
149143
None
150144
}
151145

src/librustc/middle/borrowck/gather_loans/lifetime.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
6767

6868
match cmt.cat {
6969
mc::cat_rvalue(..) |
70-
mc::cat_copied_upvar(..) | // L-Local
7170
mc::cat_local(..) | // L-Local
7271
mc::cat_upvar(..) |
7372
mc::cat_deref(_, _, mc::BorrowedPtr(..)) | // L-Deref-Borrowed
@@ -165,8 +164,7 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
165164
mc::cat_rvalue(temp_scope) => {
166165
temp_scope
167166
}
168-
mc::cat_upvar(..) |
169-
mc::cat_copied_upvar(_) => {
167+
mc::cat_upvar(..) => {
170168
ty::ReScope(self.item_scope_id)
171169
}
172170
mc::cat_static_item => {

src/librustc/middle/borrowck/gather_loans/move_error.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,7 @@ fn report_cannot_move_out_of(bccx: &BorrowckCtxt, move_from: mc::cmt) {
115115
mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
116116
mc::cat_deref(_, _, mc::Implicit(..)) |
117117
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
118-
mc::cat_upvar(..) | mc::cat_static_item => {
119-
bccx.span_err(
120-
move_from.span,
121-
format!("cannot move out of {}",
122-
bccx.cmt_to_string(&*move_from)).as_slice());
123-
}
124-
125-
mc::cat_copied_upvar(mc::CopiedUpvar { kind: kind, .. })
126-
if kind.onceness() == ast::Many => {
118+
mc::cat_static_item => {
127119
bccx.span_err(
128120
move_from.span,
129121
format!("cannot move out of {}",

src/librustc/middle/borrowck/gather_loans/restrictions.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,9 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
7373
SafeIf(lp.clone(), vec![lp])
7474
}
7575

76-
mc::cat_upvar(upvar_id, _, _) => {
76+
mc::cat_upvar(mc::Upvar { id, .. }) => {
7777
// R-Variable, captured into closure
78-
let lp = Rc::new(LpUpvar(upvar_id));
79-
SafeIf(lp.clone(), vec![lp])
80-
}
81-
82-
mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id, .. }) => {
83-
// R-Variable, copied/moved into closure
84-
let lp = Rc::new(LpVar(upvar_id));
78+
let lp = Rc::new(LpUpvar(id));
8579
SafeIf(lp.clone(), vec![lp])
8680
}
8781

src/librustc/middle/borrowck/mod.rs

+30-24
Original file line numberDiff line numberDiff line change
@@ -359,21 +359,12 @@ pub fn opt_loan_path(cmt: &mc::cmt) -> Option<Rc<LoanPath>> {
359359
None
360360
}
361361

362-
mc::cat_copied_upvar(mc::CopiedUpvar { kind: kind, .. })
363-
if kind.onceness() == ast::Many => {
364-
None
365-
}
366-
367362
mc::cat_local(id) => {
368363
Some(Rc::new(LpVar(id)))
369364
}
370365

371-
mc::cat_upvar(ty::UpvarId {var_id: id, closure_expr_id: proc_id}, _, _) |
372-
mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: id,
373-
kind: _,
374-
capturing_proc: proc_id }) => {
375-
let upvar_id = ty::UpvarId{ var_id: id, closure_expr_id: proc_id };
376-
Some(Rc::new(LpUpvar(upvar_id)))
366+
mc::cat_upvar(mc::Upvar { id, .. }) => {
367+
Some(Rc::new(LpUpvar(id)))
377368
}
378369

379370
mc::cat_deref(ref cmt_base, _, pk) => {
@@ -630,17 +621,22 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
630621
pub fn bckerr_to_string(&self, err: &BckError) -> String {
631622
match err.code {
632623
err_mutbl => {
633-
let descr = match opt_loan_path(&err.cmt) {
634-
None => {
635-
format!("{} {}",
636-
err.cmt.mutbl.to_user_str(),
637-
self.cmt_to_string(&*err.cmt))
624+
let descr = match err.cmt.note {
625+
mc::NoteClosureEnv(_) => {
626+
self.cmt_to_string(&*err.cmt)
638627
}
639-
Some(lp) => {
640-
format!("{} {} `{}`",
641-
err.cmt.mutbl.to_user_str(),
642-
self.cmt_to_string(&*err.cmt),
643-
self.loan_path_to_string(&*lp))
628+
_ => match opt_loan_path(&err.cmt) {
629+
None => {
630+
format!("{} {}",
631+
err.cmt.mutbl.to_user_str(),
632+
self.cmt_to_string(&*err.cmt))
633+
}
634+
Some(lp) => {
635+
format!("{} {} `{}`",
636+
err.cmt.mutbl.to_user_str(),
637+
self.cmt_to_string(&*err.cmt),
638+
self.loan_path_to_string(&*lp))
639+
}
644640
}
645641
};
646642

@@ -732,8 +728,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
732728
}
733729
mc::AliasableClosure(id) => {
734730
self.tcx.sess.span_err(span,
735-
format!("{} in a free variable from an \
736-
immutable unboxed closure", prefix).as_slice());
731+
format!("{} in a captured outer \
732+
variable in an `Fn` closure", prefix).as_slice());
737733
span_note!(self.tcx.sess, self.tcx.map.span(id),
738734
"consider changing this closure to take self by mutable reference");
739735
}
@@ -760,7 +756,17 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
760756
pub fn note_and_explain_bckerr(&self, err: BckError) {
761757
let code = err.code;
762758
match code {
763-
err_mutbl(..) => { }
759+
err_mutbl(..) => {
760+
match err.cmt.note {
761+
mc::NoteClosureEnv(upvar_id) => {
762+
self.tcx.sess.span_note(
763+
self.tcx.map.span(upvar_id.closure_expr_id),
764+
"consider changing this closure to take \
765+
self by mutable reference");
766+
}
767+
_ => {}
768+
}
769+
}
764770

765771
err_out_of_scope(super_scope, sub_scope) => {
766772
note_and_explain_region(

src/librustc/middle/check_static.rs

-2
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,6 @@ impl euv::Delegate for GlobalChecker {
264264
mc::cat_interior(ref cmt, _) => cur = cmt,
265265

266266
mc::cat_rvalue(..) |
267-
mc::cat_copied_upvar(..) |
268267
mc::cat_upvar(..) |
269268
mc::cat_local(..) => break,
270269
}
@@ -299,7 +298,6 @@ impl euv::Delegate for GlobalChecker {
299298

300299
mc::cat_downcast(..) |
301300
mc::cat_discr(..) |
302-
mc::cat_copied_upvar(..) |
303301
mc::cat_upvar(..) |
304302
mc::cat_local(..) => unreachable!(),
305303
}

0 commit comments

Comments
 (0)