Call is_freeze less in unsafety-checking

This is to avoid cycles when calling `is_freeze` on an opaque type.
This commit is contained in:
Matthew Jasper 2019-12-28 15:51:44 +00:00
parent 7f41cf4cef
commit 4af0952961
2 changed files with 58 additions and 19 deletions

View File

@ -178,6 +178,16 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
}
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
// prevent
// * `&mut x.field`
// * `x.field = y;`
// * `&x.field` if `field`'s type has interior mutability
// because either of these would allow modifying the layout constrained field and
// insert values that violate the layout constraints.
if context.is_mutating_use() || context.is_borrow() {
self.check_mut_borrowing_layout_constrained_field(place, context.is_mutating_use());
}
for (i, elem) in place.projection.iter().enumerate() {
let proj_base = &place.projection[..i];
@ -198,24 +208,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
);
}
}
let is_borrow_of_interior_mut = context.is_borrow()
&& !Place::ty_from(place.local, proj_base, self.body, self.tcx).ty.is_freeze(
self.tcx,
self.param_env,
self.source_info.span,
);
// prevent
// * `&mut x.field`
// * `x.field = y;`
// * `&x.field` if `field`'s type has interior mutability
// because either of these would allow modifying the layout constrained field and
// insert values that violate the layout constraints.
if context.is_mutating_use() || is_borrow_of_interior_mut {
self.check_mut_borrowing_layout_constrained_field(place, context.is_mutating_use());
}
let old_source_info = self.source_info;
if let (local, []) = (&place.local, proj_base) {
let decl = &self.body.local_decls[*local];
if let [] = proj_base {
let decl = &self.body.local_decls[place.local];
if decl.internal {
if let LocalInfo::StaticRef { def_id, .. } = decl.local_info {
if self.tcx.is_mutable_static(def_id) {
@ -240,7 +235,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
// Internal locals are used in the `move_val_init` desugaring.
// We want to check unsafety against the source info of the
// desugaring, rather than the source info of the RHS.
self.source_info = self.body.local_decls[*local].source_info;
self.source_info = self.body.local_decls[place.local].source_info;
}
}
}
@ -396,6 +391,9 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
cursor = proj_base;
match elem {
// Modifications behind a dereference don't affect the value of
// the pointer.
ProjectionElem::Deref => return,
ProjectionElem::Field(..) => {
let ty =
Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty;
@ -409,7 +407,14 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
"mutating layout constrained fields cannot statically be \
checked for valid values",
)
} else {
// Check `is_freeze` as late as possible to avoid cycle errors
// with opaque types.
} else if !place.ty(self.body, self.tcx).ty.is_freeze(
self.tcx,
self.param_env,
self.source_info.span,
) {
(
"borrow of layout constrained field with interior \
mutability",
@ -417,6 +422,8 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
lose the constraints. Coupled with interior mutability, \
the field can be changed to invalid values",
)
} else {
continue;
};
self.require_unsafe(
description,

View File

@ -0,0 +1,32 @@
// Ensure that we don't get a cycle error from trying to determine whether an
// opaque type implements `Freeze` in safety checking, when it doesn't matter.
// check-pass
#![feature(rustc_attrs)]
struct AnyValue<T>(T);
// No need to check for `Freeze` here, there's no
// `rustc_layout_scalar_valid_range_start` involved.
fn not_restricted(c: bool) -> impl Sized {
if c {
let x = AnyValue(not_restricted(false));
&x.0;
}
2u32
}
#[rustc_layout_scalar_valid_range_start(1)]
struct NonZero<T>(T);
// No need to check for `Freeze` here, we're not borrowing the field.
fn not_field(c: bool) -> impl Sized {
if c {
let x = unsafe { NonZero(not_field(false)) };
&x;
}
5u32
}
fn main() {}