refactor unsafety checking of places
This commit is contained in:
parent
df1c55a474
commit
571da2c62d
2 changed files with 87 additions and 76 deletions
|
@ -1744,7 +1744,9 @@ impl<'tcx> Place<'tcx> {
|
||||||
|
|
||||||
/// Iterate over the projections in evaluation order, i.e., the first element is the base with
|
/// Iterate over the projections in evaluation order, i.e., the first element is the base with
|
||||||
/// its projection and then subsequently more projections are added.
|
/// its projection and then subsequently more projections are added.
|
||||||
pub fn iter_projections(self) -> impl Iterator<Item=(PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
|
pub fn iter_projections(
|
||||||
|
self,
|
||||||
|
) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
|
||||||
self.projection.iter().enumerate().map(move |(i, proj)| {
|
self.projection.iter().enumerate().map(move |(i, proj)| {
|
||||||
let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
|
let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
|
||||||
(base, proj)
|
(base, proj)
|
||||||
|
|
|
@ -181,6 +181,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||||
self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
|
self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for borrows to packed fields.
|
||||||
|
// `is_disaligned` already traverses the place to consider all projections after the last
|
||||||
|
// `Deref`, so this only needs to be called once at the top level.
|
||||||
if context.is_borrow() {
|
if context.is_borrow() {
|
||||||
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
|
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
|
||||||
self.require_unsafe(
|
self.require_unsafe(
|
||||||
|
@ -190,20 +193,13 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, _elem) in place.projection.iter().enumerate() {
|
// Some checks below need the extra metainfo of the local declaration.
|
||||||
let proj_base = &place.projection[..i];
|
|
||||||
if context.is_borrow() {
|
|
||||||
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
|
|
||||||
self.require_unsafe(
|
|
||||||
UnsafetyViolationKind::BorrowPacked,
|
|
||||||
UnsafetyViolationDetails::BorrowOfPackedField,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let source_info = self.source_info;
|
|
||||||
if let [] = proj_base {
|
|
||||||
let decl = &self.body.local_decls[place.local];
|
let decl = &self.body.local_decls[place.local];
|
||||||
if decl.internal {
|
|
||||||
|
// Check the base local: it might be an unsafe-to-access static. We only check derefs of the
|
||||||
|
// temporary holding the static pointer to avoid duplicate errors
|
||||||
|
// <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
|
||||||
|
if decl.internal && place.projection.first() == Some(&ProjectionElem::Deref) {
|
||||||
// If the projection root is an artifical local that we introduced when
|
// If the projection root is an artifical local that we introduced when
|
||||||
// desugaring `static`, give a more specific error message
|
// desugaring `static`, give a more specific error message
|
||||||
// (avoid the general "raw pointer" clause below, that would only be confusing).
|
// (avoid the general "raw pointer" clause below, that would only be confusing).
|
||||||
|
@ -221,22 +217,45 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for raw pointer `Deref`.
|
||||||
|
for (base, proj) in place.iter_projections() {
|
||||||
|
if proj == ProjectionElem::Deref {
|
||||||
|
let source_info = self.source_info; // Backup source_info so we can restore it later.
|
||||||
|
if base.projection.is_empty() && decl.internal {
|
||||||
// Internal locals are used in the `move_val_init` desugaring.
|
// Internal locals are used in the `move_val_init` desugaring.
|
||||||
// We want to check unsafety against the source info of the
|
// We want to check unsafety against the source info of the
|
||||||
// desugaring, rather than the source info of the RHS.
|
// desugaring, rather than the source info of the RHS.
|
||||||
self.source_info = self.body.local_decls[place.local].source_info;
|
self.source_info = self.body.local_decls[place.local].source_info;
|
||||||
}
|
}
|
||||||
}
|
let base_ty = base.ty(self.body, self.tcx).ty;
|
||||||
}
|
if base_ty.is_unsafe_ptr() {
|
||||||
let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
|
self.require_unsafe(
|
||||||
match base_ty.kind() {
|
|
||||||
ty::RawPtr(..) => self.require_unsafe(
|
|
||||||
UnsafetyViolationKind::GeneralAndConstFn,
|
UnsafetyViolationKind::GeneralAndConstFn,
|
||||||
UnsafetyViolationDetails::DerefOfRawPointer,
|
UnsafetyViolationDetails::DerefOfRawPointer,
|
||||||
),
|
)
|
||||||
ty::Adt(adt, _) if adt.is_union() => {
|
}
|
||||||
let assign_to_field = matches!(
|
self.source_info = source_info; // Restore backed-up source_info.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
|
||||||
|
// whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
|
||||||
|
let mut saw_deref = false;
|
||||||
|
for (base, proj) in place.iter_projections().rev() {
|
||||||
|
if proj == ProjectionElem::Deref {
|
||||||
|
saw_deref = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let base_ty = base.ty(self.body, self.tcx).ty;
|
||||||
|
if base_ty.ty_adt_def().map_or(false, |adt| adt.is_union()) {
|
||||||
|
// If we did not hit a `Deref` yet and the overall place use is an assignment, the
|
||||||
|
// rules are different.
|
||||||
|
let assign_to_field = !saw_deref
|
||||||
|
&& matches!(
|
||||||
context,
|
context,
|
||||||
PlaceContext::MutatingUse(
|
PlaceContext::MutatingUse(
|
||||||
MutatingUseContext::Store
|
MutatingUseContext::Store
|
||||||
|
@ -244,13 +263,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||||
| MutatingUseContext::AsmOutput
|
| MutatingUseContext::AsmOutput
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// If there is a `Deref` further along the projection chain, this is *not* an
|
|
||||||
// assignment to a union field. In that case the union field is just read to
|
|
||||||
// obtain the pointer/reference.
|
|
||||||
let assign_to_field = assign_to_field
|
|
||||||
&& !place.projection[i..]
|
|
||||||
.iter()
|
|
||||||
.any(|elem| matches!(elem, ProjectionElem::Deref));
|
|
||||||
// If this is just an assignment, determine if the assigned type needs dropping.
|
// If this is just an assignment, determine if the assigned type needs dropping.
|
||||||
if assign_to_field {
|
if assign_to_field {
|
||||||
// We have to check the actual type of the assignment, as that determines if the
|
// We have to check the actual type of the assignment, as that determines if the
|
||||||
|
@ -280,9 +292,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
self.source_info = source_info;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue