Skip to content

Commit 34f56c4

Browse files
committed
mir-borrowck: Add permission check for WriteKind::Mutate
1 parent 503d25c commit 34f56c4

File tree

1 file changed

+131
-30
lines changed

1 file changed

+131
-30
lines changed

src/librustc_mir/borrow_check.rs

+131-30
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
271271
self.access_lvalue(context,
272272
(output, span),
273273
(Deep, Read(ReadKind::Copy)),
274+
LocalMutationIsAllowed::No,
274275
flow_state);
275276
self.check_if_path_is_moved(context, InitializationRequiringAction::Use,
276277
(output, span), flow_state);
@@ -300,7 +301,9 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
300301
StatementKind::StorageDead(local) => {
301302
self.access_lvalue(ContextKind::StorageDead.new(location),
302303
(&Lvalue::Local(local), span),
303-
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)), flow_state);
304+
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
305+
LocalMutationIsAllowed::Yes,
306+
flow_state);
304307
}
305308
}
306309
}
@@ -322,6 +325,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
322325
self.access_lvalue(ContextKind::Drop.new(loc),
323326
(drop_lvalue, span),
324327
(Deep, Write(WriteKind::StorageDeadOrDrop)),
328+
LocalMutationIsAllowed::Yes,
325329
flow_state);
326330
}
327331
TerminatorKind::DropAndReplace { location: ref drop_lvalue,
@@ -391,6 +395,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
391395
ContextKind::StorageDead.new(loc),
392396
(&root_lvalue, self.mir.source_info(borrow.location).span),
393397
(Deep, Write(WriteKind::StorageDeadOrDrop)),
398+
LocalMutationIsAllowed::Yes,
394399
flow_state
395400
);
396401
}
@@ -399,6 +404,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
399404
ContextKind::StorageDead.new(loc),
400405
(&root_lvalue, self.mir.source_info(borrow.location).span),
401406
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
407+
LocalMutationIsAllowed::Yes,
402408
flow_state
403409
);
404410
}
@@ -445,6 +451,8 @@ enum ShallowOrDeep {
445451
Deep,
446452
}
447453

454+
/// Kind of access to a value: read or write
455+
/// (For informational purposes only)
448456
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
449457
enum ReadOrWrite {
450458
/// From the RFC: "A *read* means that the existing data may be
@@ -457,12 +465,16 @@ enum ReadOrWrite {
457465
Write(WriteKind),
458466
}
459467

468+
/// Kind of read access to a value
469+
/// (For informational purposes only)
460470
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
461471
enum ReadKind {
462472
Borrow(BorrowKind),
463473
Copy,
464474
}
465475

476+
/// Kind of write access to a value
477+
/// (For informational purposes only)
466478
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
467479
enum WriteKind {
468480
StorageDeadOrDrop,
@@ -471,6 +483,20 @@ enum WriteKind {
471483
Move,
472484
}
473485

486+
/// When checking permissions for an lvalue access, this flag is used to indicate that an immutable
487+
/// local lvalue can be mutated.
488+
///
489+
/// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications:
490+
/// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`
491+
/// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and
492+
/// `is_declared_mutable()`
493+
/// - Take flow state into consideration in `is_assignable()` for local variables
494+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
495+
enum LocalMutationIsAllowed {
496+
Yes,
497+
No
498+
}
499+
474500
#[derive(Copy, Clone)]
475501
enum InitializationRequiringAction {
476502
Update,
@@ -510,6 +536,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
510536
context: Context,
511537
lvalue_span: (&Lvalue<'tcx>, Span),
512538
kind: (ShallowOrDeep, ReadOrWrite),
539+
is_local_mutation_allowed: LocalMutationIsAllowed,
513540
flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
514541
let (sd, rw) = kind;
515542

@@ -526,9 +553,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
526553
}
527554

528555
// Check permissions
529-
self.check_access_permissions(lvalue_span, rw);
556+
let mut error_reported = self.check_access_permissions(lvalue_span,
557+
rw,
558+
is_local_mutation_allowed);
530559

531-
let mut error_reported = false;
532560
self.each_borrow_involving_path(
533561
context, (sd, lvalue_span.0), flow_state, |this, _index, borrow, common_prefix| {
534562
match (rw, borrow.kind) {
@@ -614,7 +642,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
614642
}
615643
}
616644

617-
self.access_lvalue(context, lvalue_span, (kind, Write(WriteKind::Mutate)), flow_state);
645+
self.access_lvalue(context,
646+
lvalue_span,
647+
(kind, Write(WriteKind::Mutate)),
648+
LocalMutationIsAllowed::Yes,
649+
flow_state);
618650

619651
// check for reassignments to immutable local variables
620652
self.check_if_reassignment_to_immutable_state(context, lvalue_span, flow_state);
@@ -632,7 +664,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
632664
BorrowKind::Unique |
633665
BorrowKind::Mut => (Deep, Write(WriteKind::MutableBorrow(bk))),
634666
};
635-
self.access_lvalue(context, (lvalue, span), access_kind, flow_state);
667+
self.access_lvalue(context,
668+
(lvalue, span),
669+
access_kind,
670+
LocalMutationIsAllowed::No,
671+
flow_state);
636672
self.check_if_path_is_moved(context, InitializationRequiringAction::Borrow,
637673
(lvalue, span), flow_state);
638674
}
@@ -651,8 +687,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
651687
Rvalue::Discriminant(..) => ArtificialField::Discriminant,
652688
_ => unreachable!(),
653689
};
654-
self.access_lvalue(
655-
context, (lvalue, span), (Shallow(Some(af)), Read(ReadKind::Copy)), flow_state);
690+
self.access_lvalue(context,
691+
(lvalue, span),
692+
(Shallow(Some(af)), Read(ReadKind::Copy)),
693+
LocalMutationIsAllowed::No,
694+
flow_state);
656695
self.check_if_path_is_moved(context, InitializationRequiringAction::Use,
657696
(lvalue, span), flow_state);
658697
}
@@ -690,6 +729,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
690729
self.access_lvalue(context,
691730
(lvalue, span),
692731
(Deep, Read(ReadKind::Copy)),
732+
LocalMutationIsAllowed::No,
693733
flow_state);
694734

695735
// Finally, check if path was already moved.
@@ -701,6 +741,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
701741
self.access_lvalue(context,
702742
(lvalue, span),
703743
(Deep, Write(WriteKind::Move)),
744+
LocalMutationIsAllowed::Yes,
704745
flow_state);
705746

706747
// Finally, check if path was already moved.
@@ -735,9 +776,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
735776
}
736777
Lvalue::Static(ref static_) => {
737778
// mutation of non-mut static is always illegal,
738-
// independent of dataflow.
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
739782
if !self.tcx.is_static_mut(static_.def_id) {
740-
self.report_assignment_to_static(context, (lvalue, span));
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));
741790
}
742791
return;
743792
}
@@ -949,41 +998,101 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
949998
}
950999

9511000
/// Check the permissions for the given lvalue and read or write kind
952-
fn check_access_permissions(&self, (lvalue, span): (&Lvalue<'tcx>, Span), kind: ReadOrWrite) {
1001+
///
1002+
/// Returns true if an error is reported, false otherwise.
1003+
fn check_access_permissions(&self,
1004+
(lvalue, span): (&Lvalue<'tcx>, Span),
1005+
kind: ReadOrWrite,
1006+
is_local_mutation_allowed: LocalMutationIsAllowed)
1007+
-> bool {
1008+
debug!("check_access_permissions({:?}, {:?}, {:?})",
1009+
lvalue, kind, is_local_mutation_allowed);
1010+
let mut error_reported = false;
9531011
match kind {
9541012
Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => {
9551013
if let Err(_lvalue_err) = self.is_unique(lvalue) {
956-
span_bug!(span, "&unique borrow for `{}` should not fail",
957-
self.describe_lvalue(lvalue));
1014+
span_bug!(span, "&unique borrow for {:?} should not fail", lvalue);
9581015
}
9591016
},
9601017
Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => {
961-
if let Err(lvalue_err) = self.is_mutable(lvalue) {
1018+
if let Err(lvalue_err) = self.is_mutable(lvalue, is_local_mutation_allowed) {
1019+
error_reported = true;
1020+
1021+
let item_msg = match self.describe_lvalue(lvalue) {
1022+
Some(name) => format!("immutable item `{}`", name),
1023+
None => "immutable item".to_owned()
1024+
};
1025+
9621026
let mut err = self.tcx.cannot_borrow_path_as_mutable(span,
963-
&format!("immutable item `{}`",
964-
self.describe_lvalue(lvalue)),
1027+
&item_msg,
9651028
Origin::Mir);
9661029
err.span_label(span, "cannot borrow as mutable");
9671030

9681031
if lvalue != lvalue_err {
969-
err.note(&format!("Value not mutable causing this error: `{}`",
970-
self.describe_lvalue(lvalue_err)));
1032+
if let Some(name) = self.describe_lvalue(lvalue_err) {
1033+
err.note(&format!("Value not mutable causing this error: `{}`", name));
1034+
}
1035+
}
1036+
1037+
err.emit();
1038+
}
1039+
},
1040+
Write(WriteKind::Mutate) => {
1041+
if let Err(lvalue_err) = self.is_mutable(lvalue, is_local_mutation_allowed) {
1042+
error_reported = true;
1043+
1044+
let item_msg = match self.describe_lvalue(lvalue) {
1045+
Some(name) => format!("immutable item `{}`", name),
1046+
None => "immutable item".to_owned()
1047+
};
1048+
1049+
let mut err = self.tcx.cannot_assign(span,
1050+
&item_msg,
1051+
Origin::Mir);
1052+
err.span_label(span, "cannot mutate");
1053+
1054+
if lvalue != lvalue_err {
1055+
if let Some(name) = self.describe_lvalue(lvalue_err) {
1056+
err.note(&format!("Value not mutable causing this error: `{}`", name));
1057+
}
9711058
}
9721059

9731060
err.emit();
9741061
}
9751062
},
976-
_ => {}// Access authorized
1063+
Write(WriteKind::Move) |
1064+
Write(WriteKind::StorageDeadOrDrop) |
1065+
Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => {
1066+
if let Err(_lvalue_err) = self.is_mutable(lvalue, is_local_mutation_allowed) {
1067+
self.tcx.sess.delay_span_bug(span,
1068+
&format!("Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
1069+
lvalue,
1070+
kind));
1071+
}
1072+
},
1073+
Read(ReadKind::Borrow(BorrowKind::Unique)) |
1074+
Read(ReadKind::Borrow(BorrowKind::Mut)) |
1075+
Read(ReadKind::Borrow(BorrowKind::Shared)) |
1076+
Read(ReadKind::Copy) => {} // Access authorized
9771077
}
1078+
1079+
error_reported
9781080
}
9791081

9801082
/// Can this value be written or borrowed mutably
981-
fn is_mutable<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> Result<(), &'d Lvalue<'tcx>> {
1083+
fn is_mutable<'d>(&self,
1084+
lvalue: &'d Lvalue<'tcx>,
1085+
is_local_mutation_allowed: LocalMutationIsAllowed)
1086+
-> Result<(), &'d Lvalue<'tcx>> {
9821087
match *lvalue {
9831088
Lvalue::Local(local) => {
9841089
let local = &self.mir.local_decls[local];
9851090
match local.mutability {
986-
Mutability::Not => Err(lvalue),
1091+
Mutability::Not =>
1092+
match is_local_mutation_allowed {
1093+
LocalMutationIsAllowed::Yes => Ok(()),
1094+
LocalMutationIsAllowed::No => Err(lvalue),
1095+
},
9871096
Mutability::Mut => Ok(())
9881097
}
9891098
},
@@ -1001,7 +1110,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10011110

10021111
// `Box<T>` owns its content, so mutable if its location is mutable
10031112
if base_ty.is_box() {
1004-
return self.is_mutable(&proj.base);
1113+
return self.is_mutable(&proj.base, LocalMutationIsAllowed::No);
10051114
}
10061115

10071116
// Otherwise we check the kind of deref to decide
@@ -1035,7 +1144,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10351144
ProjectionElem::ConstantIndex{..} |
10361145
ProjectionElem::Subslice{..} |
10371146
ProjectionElem::Downcast(..) =>
1038-
self.is_mutable(&proj.base)
1147+
self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
10391148
}
10401149
}
10411150
}
@@ -1604,14 +1713,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16041713
}
16051714
err.emit();
16061715
}
1607-
1608-
fn report_assignment_to_static(&mut self,
1609-
_context: Context,
1610-
(lvalue, span): (&Lvalue<'tcx>, Span)) {
1611-
let mut err = self.tcx.cannot_assign_static(
1612-
span, &self.describe_lvalue(lvalue), Origin::Mir);
1613-
err.emit();
1614-
}
16151716
}
16161717

16171718
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {

0 commit comments

Comments
 (0)