Skip to content

Commit 583ae91

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 126f224 + 29d160f commit 583ae91

18 files changed

+526
-284
lines changed

src/librustc/metadata/tydecode.rs

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

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
@@ -72,15 +72,9 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
7272
SafeIf(lp.clone(), vec![lp])
7373
}
7474

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

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) => {
@@ -629,17 +620,22 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
629620
pub fn bckerr_to_string(&self, err: &BckError) -> String {
630621
match err.code {
631622
err_mutbl => {
632-
let descr = match opt_loan_path(&err.cmt) {
633-
None => {
634-
format!("{} {}",
635-
err.cmt.mutbl.to_user_str(),
636-
self.cmt_to_string(&*err.cmt))
623+
let descr = match err.cmt.note {
624+
mc::NoteClosureEnv(_) => {
625+
self.cmt_to_string(&*err.cmt)
637626
}
638-
Some(lp) => {
639-
format!("{} {} `{}`",
640-
err.cmt.mutbl.to_user_str(),
641-
self.cmt_to_string(&*err.cmt),
642-
self.loan_path_to_string(&*lp))
627+
_ => match opt_loan_path(&err.cmt) {
628+
None => {
629+
format!("{} {}",
630+
err.cmt.mutbl.to_user_str(),
631+
self.cmt_to_string(&*err.cmt))
632+
}
633+
Some(lp) => {
634+
format!("{} {} `{}`",
635+
err.cmt.mutbl.to_user_str(),
636+
self.cmt_to_string(&*err.cmt),
637+
self.loan_path_to_string(&*lp))
638+
}
643639
}
644640
};
645641

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

764770
err_out_of_scope(super_scope, sub_scope) => {
765771
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)