Skip to content

Commit 9094aab

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 1868a26 commit 9094aab

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
@@ -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

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
@@ -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

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) => {
@@ -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

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)