Skip to content

Commit 37e08a5

Browse files
committed
mir-borrowck: Check access permissions in access_lvalue()
1 parent 0bb77bd commit 37e08a5

File tree

1 file changed

+154
-2
lines changed

1 file changed

+154
-2
lines changed

src/librustc_mir/borrow_check.rs

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
//! This query borrow-checks the MIR to (further) ensure it is not broken.
1212
13+
use rustc::hir;
1314
use rustc::hir::def_id::{DefId};
1415
use rustc::infer::{InferCtxt};
1516
use rustc::ty::{self, TyCtxt, ParamEnv};
@@ -447,9 +448,12 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
447448
lvalue_span: (&Lvalue<'tcx>, Span),
448449
kind: (ShallowOrDeep, ReadOrWrite),
449450
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
450-
// FIXME: also need to check permissions (e.g. reject mut
451-
// borrow of immutable ref, moves through non-`Box`-ref)
451+
452452
let (sd, rw) = kind;
453+
454+
// Check permissions
455+
self.check_access_permissions(lvalue_span, rw);
456+
453457
self.each_borrow_involving_path(
454458
context, (sd, lvalue_span.0), flow_state, |this, _index, borrow, common_prefix| {
455459
match (rw, borrow.kind) {
@@ -861,6 +865,154 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
861865
}
862866
}
863867
}
868+
869+
/// Check the permissions for the given lvalue and read or write kind
870+
fn check_access_permissions(&self, (lvalue, span): (&Lvalue<'tcx>, Span), kind: ReadOrWrite) {
871+
match kind {
872+
Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => {
873+
if let Err(_lvalue_err) = self.is_unique(lvalue) {
874+
span_bug!(span, "&unique borrow for `{}` should not fail",
875+
self.describe_lvalue(lvalue));
876+
}
877+
},
878+
Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => {
879+
if let Err(lvalue_err) = self.is_mutable(lvalue) {
880+
let mut err = self.tcx.cannot_borrow_path_as_mutable(span,
881+
&format!("immutable item `{}`",
882+
self.describe_lvalue(lvalue)),
883+
Origin::Mir);
884+
err.span_label(span, "cannot borrow as mutable");
885+
886+
if lvalue != lvalue_err {
887+
err.note(&format!("Value not mutable causing this error: `{}`",
888+
self.describe_lvalue(lvalue_err)));
889+
}
890+
891+
err.emit();
892+
}
893+
},
894+
_ => {}// Access authorized
895+
}
896+
}
897+
898+
/// Can this value be written or borrowed mutably
899+
fn is_mutable<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> Result<(), &'d Lvalue<'tcx>> {
900+
match *lvalue {
901+
Lvalue::Local(local) => {
902+
let local = &self.mir.local_decls[local];
903+
match local.mutability {
904+
Mutability::Not => Err(lvalue),
905+
Mutability::Mut => Ok(())
906+
}
907+
},
908+
Lvalue::Static(ref static_) => {
909+
if !self.tcx.is_static_mut(static_.def_id) {
910+
Err(lvalue)
911+
} else {
912+
Ok(())
913+
}
914+
},
915+
Lvalue::Projection(ref proj) => {
916+
match proj.elem {
917+
ProjectionElem::Deref => {
918+
let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
919+
920+
// `Box<T>` owns its content, so mutable if its location is mutable
921+
if base_ty.is_box() {
922+
return self.is_mutable(&proj.base);
923+
}
924+
925+
// Otherwise we check the kind of deref to decide
926+
match base_ty.sty {
927+
ty::TyRef(_, tnm) => {
928+
match tnm.mutbl {
929+
// Shared borrowed data is never mutable
930+
hir::MutImmutable => Err(lvalue),
931+
// Mutably borrowed data is mutable, but only if we have a
932+
// unique path to the `&mut`
933+
hir::MutMutable => self.is_unique(&proj.base),
934+
}
935+
},
936+
ty::TyRawPtr(tnm) => {
937+
match tnm.mutbl {
938+
// `*const` raw pointers are not mutable
939+
hir::MutImmutable => Err(lvalue),
940+
// `*mut` raw pointers are always mutable, regardless of context
941+
// The users have to check by themselve.
942+
hir::MutMutable => Ok(()),
943+
}
944+
},
945+
// Deref should only be for reference, pointers or boxes
946+
_ => bug!("Deref of unexpected type: {:?}", base_ty)
947+
}
948+
},
949+
// All other projections are owned by their base path, so mutable if
950+
// base path is mutable
951+
ProjectionElem::Field(..) |
952+
ProjectionElem::Index(..) |
953+
ProjectionElem::ConstantIndex{..} |
954+
ProjectionElem::Subslice{..} |
955+
ProjectionElem::Downcast(..) =>
956+
self.is_mutable(&proj.base)
957+
}
958+
}
959+
}
960+
}
961+
962+
/// Does this lvalue have a unique path
963+
fn is_unique<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> Result<(), &'d Lvalue<'tcx>> {
964+
match *lvalue {
965+
Lvalue::Local(..) => {
966+
// Local variables are unique
967+
Ok(())
968+
},
969+
Lvalue::Static(..) => {
970+
// Static variables are not
971+
Err(lvalue)
972+
},
973+
Lvalue::Projection(ref proj) => {
974+
match proj.elem {
975+
ProjectionElem::Deref => {
976+
let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
977+
978+
// `Box<T>` referent is unique if box is a unique spot
979+
if base_ty.is_box() {
980+
return self.is_unique(&proj.base);
981+
}
982+
983+
// Otherwise we check the kind of deref to decide
984+
match base_ty.sty {
985+
ty::TyRef(_, tnm) => {
986+
match tnm.mutbl {
987+
// lvalue represent an aliased location
988+
hir::MutImmutable => Err(lvalue),
989+
// `&mut T` is as unique as the context in which it is found
990+
hir::MutMutable => self.is_unique(&proj.base),
991+
}
992+
},
993+
ty::TyRawPtr(tnm) => {
994+
match tnm.mutbl {
995+
// `*mut` can be aliased, but we leave it to user
996+
hir::MutMutable => Ok(()),
997+
// `*const` is treated the same as `*mut`
998+
hir::MutImmutable => Ok(()),
999+
}
1000+
},
1001+
// Deref should only be for reference, pointers or boxes
1002+
_ => bug!("Deref of unexpected type: {:?}", base_ty)
1003+
}
1004+
},
1005+
// Other projections are unique if the base is unique
1006+
ProjectionElem::Field(..) |
1007+
ProjectionElem::Index(..) |
1008+
ProjectionElem::ConstantIndex{..} |
1009+
ProjectionElem::Subslice{..} |
1010+
ProjectionElem::Downcast(..) =>
1011+
self.is_unique(&proj.base)
1012+
}
1013+
}
1014+
}
1015+
}
8641016
}
8651017

8661018
#[derive(Copy, Clone, PartialEq, Eq, Debug)]

0 commit comments

Comments
 (0)