Skip to content

Commit dedbb4e

Browse files
committed
Immutable unique closure upvars cannot be mutated.
1 parent c3459b0 commit dedbb4e

File tree

4 files changed

+64
-49
lines changed

4 files changed

+64
-49
lines changed

src/librustc/ich/impls_mir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> {
3131
lexical_scope,
3232
is_user_variable
3333
});
34-
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref });
34+
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref, mutability });
3535
impl_stable_hash_for!(struct mir::BasicBlockData<'tcx> { statements, terminator, is_cleanup });
3636
impl_stable_hash_for!(struct mir::UnsafetyViolation { source_info, description, kind });
3737
impl_stable_hash_for!(struct mir::UnsafetyCheckResult { violations, unsafe_blocks });

src/librustc/mir/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,9 @@ pub struct UpvarDecl {
554554
pub debug_name: Name,
555555

556556
/// If true, the capture is behind a reference.
557-
pub by_ref: bool
557+
pub by_ref: bool,
558+
559+
pub mutability: Mutability,
558560
}
559561

560562
///////////////////////////////////////////////////////////////////////////

src/librustc_mir/borrow_check.rs

+50-47
Original file line numberDiff line numberDiff line change
@@ -761,47 +761,34 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
761761
let move_data = self.move_data;
762762

763763
// determine if this path has a non-mut owner (and thus needs checking).
764-
let mut l = lvalue;
765-
loop {
766-
match *l {
767-
Lvalue::Projection(ref proj) => {
768-
l = &proj.base;
769-
continue;
770-
}
771-
Lvalue::Local(local) => {
772-
match self.mir.local_decls[local].mutability {
773-
Mutability::Not => break, // needs check
774-
Mutability::Mut => return,
775-
}
776-
}
777-
Lvalue::Static(ref static_) => {
778-
// mutation of non-mut static is always illegal,
779-
// independent of dataflow. However it will be catched by
780-
// `check_access_permissions()`, we call delay_span_bug here
781-
// to be sure that no case has been missed
782-
if !self.tcx.is_static_mut(static_.def_id) {
783-
let item_msg = match self.describe_lvalue(lvalue) {
784-
Some(name) => format!("immutable static item `{}`", name),
785-
None => "immutable static item".to_owned()
786-
};
787-
self.tcx.sess.delay_span_bug(span,
788-
&format!("cannot assign to {}, should have been caught by \
789-
`check_access_permissions()`", item_msg));
790-
}
791-
return;
792-
}
793-
}
764+
if let Ok(()) = self.is_mutable(lvalue, LocalMutationIsAllowed::No) {
765+
return;
794766
}
795767

796-
if let Some(mpi) = self.move_path_for_lvalue(lvalue) {
797-
for ii in &move_data.init_path_map[mpi] {
798-
if flow_state.ever_inits.curr_state.contains(ii) {
799-
let first_assign_span = self.move_data.inits[*ii].span;
800-
self.report_illegal_reassignment(
801-
context, (lvalue, span), first_assign_span);
802-
break;
768+
if let Err(_) = self.is_mutable(lvalue, LocalMutationIsAllowed::Yes) {
769+
return;
770+
}
771+
772+
match self.move_path_closest_to(lvalue) {
773+
Ok(mpi) => {
774+
for ii in &move_data.init_path_map[mpi] {
775+
if flow_state.ever_inits.curr_state.contains(ii) {
776+
let first_assign_span = self.move_data.inits[*ii].span;
777+
self.report_illegal_reassignment(
778+
context, (lvalue, span), first_assign_span);
779+
break;
780+
}
803781
}
804-
}
782+
},
783+
Err(NoMovePathFound::ReachedStatic) => {
784+
let item_msg = match self.describe_lvalue(lvalue) {
785+
Some(name) => format!("immutable static item `{}`", name),
786+
None => "immutable static item".to_owned()
787+
};
788+
self.tcx.sess.delay_span_bug(span,
789+
&format!("cannot assign to {}, should have been caught by \
790+
`check_access_permissions()`", item_msg));
791+
},
805792
}
806793
}
807794

@@ -1108,20 +1095,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
11081095
ProjectionElem::Deref => {
11091096
let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
11101097

1111-
// `Box<T>` owns its content, so mutable if its location is mutable
1112-
if base_ty.is_box() {
1113-
return self.is_mutable(&proj.base, LocalMutationIsAllowed::No);
1114-
}
1115-
1116-
// Otherwise we check the kind of deref to decide
1098+
// Check the kind of deref to decide
11171099
match base_ty.sty {
11181100
ty::TyRef(_, tnm) => {
11191101
match tnm.mutbl {
11201102
// Shared borrowed data is never mutable
11211103
hir::MutImmutable => Err(lvalue),
11221104
// Mutably borrowed data is mutable, but only if we have a
11231105
// unique path to the `&mut`
1124-
hir::MutMutable => self.is_unique(&proj.base),
1106+
hir::MutMutable => {
1107+
if self.is_upvar_field_projection(&proj.base).is_some() {
1108+
self.is_mutable(&proj.base, is_local_mutation_allowed)
1109+
} else {
1110+
self.is_unique(&proj.base)
1111+
}
1112+
},
11251113
}
11261114
},
11271115
ty::TyRawPtr(tnm) => {
@@ -1133,8 +1121,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
11331121
hir::MutMutable => Ok(()),
11341122
}
11351123
},
1124+
// `Box<T>` owns its content, so mutable if its location is mutable
1125+
_ if base_ty.is_box() =>
1126+
self.is_mutable(&proj.base, LocalMutationIsAllowed::No),
11361127
// Deref should only be for reference, pointers or boxes
1137-
_ => bug!("Deref of unexpected type: {:?}", base_ty)
1128+
_ => bug!("Deref of unexpected type: {:?}", base_ty),
11381129
}
11391130
},
11401131
// All other projections are owned by their base path, so mutable if
@@ -1143,8 +1134,20 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
11431134
ProjectionElem::Index(..) |
11441135
ProjectionElem::ConstantIndex{..} |
11451136
ProjectionElem::Subslice{..} |
1146-
ProjectionElem::Downcast(..) =>
1137+
ProjectionElem::Downcast(..) => {
1138+
let field_projection = self.is_upvar_field_projection(lvalue);
1139+
1140+
if let Some(field) = field_projection {
1141+
let decl = &self.mir.upvar_decls[field.index()];
1142+
1143+
return match decl.mutability {
1144+
Mutability::Mut => self.is_unique(&proj.base),
1145+
Mutability::Not => Err(lvalue),
1146+
};
1147+
}
1148+
11471149
self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
1150+
}
11481151
}
11491152
}
11501153
}

src/librustc_mir/build/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -443,10 +443,20 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
443443
let mut decl = UpvarDecl {
444444
debug_name: keywords::Invalid.name(),
445445
by_ref,
446+
mutability: Mutability::Not,
446447
};
447448
if let Some(hir::map::NodeBinding(pat)) = tcx.hir.find(var_id) {
448449
if let hir::PatKind::Binding(_, _, ref ident, _) = pat.node {
449450
decl.debug_name = ident.node;
451+
452+
let bm = *hir.tables.pat_binding_modes()
453+
.get(pat.hir_id)
454+
.expect("missing binding mode");
455+
if bm == ty::BindByValue(hir::MutMutable) {
456+
decl.mutability = Mutability::Mut;
457+
} else {
458+
decl.mutability = Mutability::Not;
459+
}
450460
}
451461
}
452462
decl

0 commit comments

Comments
 (0)