Skip to content

Commit d7d0057

Browse files
committed
Fix soundness bug in treatment of closure upvars by regionck
- Unify the representations of `cat_upvar` and `cat_copied_upvar` - In `link_reborrowed_region`, 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 kind of the borrow is adjusted. - When categorizing upvars, add an explicit deref that represents the closure environment pointer for closures that do not take the environment by value. The region for the implicit pointer is an anonymous free region type introduced for this purpose. This creates the necessary constraint to prevent unsound reborrows from the environment. - Add a note to categorizations to make it easier to tell when extra dereferences have been inserted by an upvar without having to perform deep pattern matching. - Adjust borrowck to deal with the changes. Where `cat_upvar` and `cat_copied_upvar` were previously treated differently, they are now both treated roughly like local variables within the closure body, as the explicit derefs now ensure proper behavior. However, error diagnostics had to be changed to explicitly look through the extra dereferences to avoid producing confusing messages about references not present in the source code. Closes issue #17403. Remaining work: - The error diagnostics that result from failed region inference are pretty inscrutible and should be improved. Code like the following is now rejected: let mut x = 0u; let f = || &mut x; let y = f(); let z = f(); // multiple mutable references to the same location This also breaks code that uses a similar construction even if it does not go on to violate aliasability semantics. Such code will need to be reworked in some way, such as by using a capture-by-value closure type. [breaking-change]
1 parent 8096fee commit d7d0057

File tree

15 files changed

+443
-266
lines changed

15 files changed

+443
-266
lines changed

src/librustc/metadata/tydecode.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 24 additions & 23 deletions
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

Lines changed: 3 additions & 9 deletions
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

Lines changed: 1 addition & 3 deletions
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

Lines changed: 1 addition & 9 deletions
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

Lines changed: 2 additions & 8 deletions
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

Lines changed: 30 additions & 24 deletions
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

Lines changed: 0 additions & 2 deletions
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)