Skip to content

Commit 571da2c

Browse files
committed
refactor unsafety checking of places
1 parent df1c55a commit 571da2c

File tree

2 files changed

+87
-76
lines changed

2 files changed

+87
-76
lines changed

compiler/rustc_middle/src/mir/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1744,7 +1744,9 @@ impl<'tcx> Place<'tcx> {
17441744

17451745
/// Iterate over the projections in evaluation order, i.e., the first element is the base with
17461746
/// its projection and then subsequently more projections are added.
1747-
pub fn iter_projections(self) -> impl Iterator<Item=(PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
1747+
pub fn iter_projections(
1748+
self,
1749+
) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
17481750
self.projection.iter().enumerate().map(move |(i, proj)| {
17491751
let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
17501752
(base, proj)

compiler/rustc_mir/src/transform/check_unsafety.rs

+84-75
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
181181
self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
182182
}
183183

184+
// Check for borrows to packed fields.
185+
// `is_disaligned` already traverses the place to consider all projections after the last
186+
// `Deref`, so this only needs to be called once at the top level.
184187
if context.is_borrow() {
185188
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
186189
self.require_unsafe(
@@ -190,99 +193,105 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
190193
}
191194
}
192195

193-
for (i, _elem) in place.projection.iter().enumerate() {
194-
let proj_base = &place.projection[..i];
195-
if context.is_borrow() {
196-
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
196+
// Some checks below need the extra metainfo of the local declaration.
197+
let decl = &self.body.local_decls[place.local];
198+
199+
// Check the base local: it might be an unsafe-to-access static. We only check derefs of the
200+
// temporary holding the static pointer to avoid duplicate errors
201+
// <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
202+
if decl.internal && place.projection.first() == Some(&ProjectionElem::Deref) {
203+
// If the projection root is an artifical local that we introduced when
204+
// desugaring `static`, give a more specific error message
205+
// (avoid the general "raw pointer" clause below, that would only be confusing).
206+
if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
207+
if self.tcx.is_mutable_static(def_id) {
197208
self.require_unsafe(
198-
UnsafetyViolationKind::BorrowPacked,
199-
UnsafetyViolationDetails::BorrowOfPackedField,
209+
UnsafetyViolationKind::General,
210+
UnsafetyViolationDetails::UseOfMutableStatic,
200211
);
212+
return;
213+
} else if self.tcx.is_foreign_item(def_id) {
214+
self.require_unsafe(
215+
UnsafetyViolationKind::General,
216+
UnsafetyViolationDetails::UseOfExternStatic,
217+
);
218+
return;
201219
}
202220
}
203-
let source_info = self.source_info;
204-
if let [] = proj_base {
205-
let decl = &self.body.local_decls[place.local];
206-
if decl.internal {
207-
// If the projection root is an artifical local that we introduced when
208-
// desugaring `static`, give a more specific error message
209-
// (avoid the general "raw pointer" clause below, that would only be confusing).
210-
if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
211-
if self.tcx.is_mutable_static(def_id) {
212-
self.require_unsafe(
213-
UnsafetyViolationKind::General,
214-
UnsafetyViolationDetails::UseOfMutableStatic,
215-
);
216-
return;
217-
} else if self.tcx.is_foreign_item(def_id) {
218-
self.require_unsafe(
219-
UnsafetyViolationKind::General,
220-
UnsafetyViolationDetails::UseOfExternStatic,
221-
);
222-
return;
223-
}
224-
} else {
225-
// Internal locals are used in the `move_val_init` desugaring.
226-
// We want to check unsafety against the source info of the
227-
// desugaring, rather than the source info of the RHS.
228-
self.source_info = self.body.local_decls[place.local].source_info;
229-
}
221+
}
222+
223+
// Check for raw pointer `Deref`.
224+
for (base, proj) in place.iter_projections() {
225+
if proj == ProjectionElem::Deref {
226+
let source_info = self.source_info; // Backup source_info so we can restore it later.
227+
if base.projection.is_empty() && decl.internal {
228+
// Internal locals are used in the `move_val_init` desugaring.
229+
// We want to check unsafety against the source info of the
230+
// desugaring, rather than the source info of the RHS.
231+
self.source_info = self.body.local_decls[place.local].source_info;
230232
}
233+
let base_ty = base.ty(self.body, self.tcx).ty;
234+
if base_ty.is_unsafe_ptr() {
235+
self.require_unsafe(
236+
UnsafetyViolationKind::GeneralAndConstFn,
237+
UnsafetyViolationDetails::DerefOfRawPointer,
238+
)
239+
}
240+
self.source_info = source_info; // Restore backed-up source_info.
241+
}
242+
}
243+
244+
// Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
245+
// whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
246+
let mut saw_deref = false;
247+
for (base, proj) in place.iter_projections().rev() {
248+
if proj == ProjectionElem::Deref {
249+
saw_deref = true;
250+
continue;
231251
}
232-
let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
233-
match base_ty.kind() {
234-
ty::RawPtr(..) => self.require_unsafe(
235-
UnsafetyViolationKind::GeneralAndConstFn,
236-
UnsafetyViolationDetails::DerefOfRawPointer,
237-
),
238-
ty::Adt(adt, _) if adt.is_union() => {
239-
let assign_to_field = matches!(
252+
253+
let base_ty = base.ty(self.body, self.tcx).ty;
254+
if base_ty.ty_adt_def().map_or(false, |adt| adt.is_union()) {
255+
// If we did not hit a `Deref` yet and the overall place use is an assignment, the
256+
// rules are different.
257+
let assign_to_field = !saw_deref
258+
&& matches!(
240259
context,
241260
PlaceContext::MutatingUse(
242261
MutatingUseContext::Store
243262
| MutatingUseContext::Drop
244263
| MutatingUseContext::AsmOutput
245264
)
246265
);
247-
// If there is a `Deref` further along the projection chain, this is *not* an
248-
// assignment to a union field. In that case the union field is just read to
249-
// obtain the pointer/reference.
250-
let assign_to_field = assign_to_field
251-
&& !place.projection[i..]
252-
.iter()
253-
.any(|elem| matches!(elem, ProjectionElem::Deref));
254-
// If this is just an assignment, determine if the assigned type needs dropping.
255-
if assign_to_field {
256-
// We have to check the actual type of the assignment, as that determines if the
257-
// old value is being dropped.
258-
let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
259-
// To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
260-
let manually_drop = assigned_ty
261-
.ty_adt_def()
262-
.map_or(false, |adt_def| adt_def.is_manually_drop());
263-
let nodrop = manually_drop
264-
|| assigned_ty.is_copy_modulo_regions(
265-
self.tcx.at(self.source_info.span),
266-
self.param_env,
267-
);
268-
if !nodrop {
269-
self.require_unsafe(
270-
UnsafetyViolationKind::GeneralAndConstFn,
271-
UnsafetyViolationDetails::AssignToDroppingUnionField,
272-
);
273-
} else {
274-
// write to non-drop union field, safe
275-
}
276-
} else {
266+
// If this is just an assignment, determine if the assigned type needs dropping.
267+
if assign_to_field {
268+
// We have to check the actual type of the assignment, as that determines if the
269+
// old value is being dropped.
270+
let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
271+
// To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
272+
let manually_drop = assigned_ty
273+
.ty_adt_def()
274+
.map_or(false, |adt_def| adt_def.is_manually_drop());
275+
let nodrop = manually_drop
276+
|| assigned_ty.is_copy_modulo_regions(
277+
self.tcx.at(self.source_info.span),
278+
self.param_env,
279+
);
280+
if !nodrop {
277281
self.require_unsafe(
278282
UnsafetyViolationKind::GeneralAndConstFn,
279-
UnsafetyViolationDetails::AccessToUnionField,
280-
)
283+
UnsafetyViolationDetails::AssignToDroppingUnionField,
284+
);
285+
} else {
286+
// write to non-drop union field, safe
281287
}
288+
} else {
289+
self.require_unsafe(
290+
UnsafetyViolationKind::GeneralAndConstFn,
291+
UnsafetyViolationDetails::AccessToUnionField,
292+
)
282293
}
283-
_ => {}
284294
}
285-
self.source_info = source_info;
286295
}
287296
}
288297
}

0 commit comments

Comments
 (0)