Skip to content

Commit

Permalink
Auto merge of #80865 - oliviacrain:proj_based, r=RalfJung
Browse files Browse the repository at this point in the history
Use PlaceRef projection abstractions more consistently in rustc_mir

PlaceRef contains abstractions for dealing with the `projections` array. This PR uses these abstractions more consistently within the `rustc_mir` crate.

See associated issue: #80647.

r? `@RalfJung`
  • Loading branch information
bors committed Jan 18, 2021
2 parents 93e0aed + 65b5e43 commit 0677d97
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 124 deletions.
16 changes: 6 additions & 10 deletions compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1616,28 +1616,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {

fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
let tcx = self.infcx.tcx;
match place.projection {
[] => StorageDeadOrDrop::LocalStorageDead,
[proj_base @ .., elem] => {
match place.last_projection() {
None => StorageDeadOrDrop::LocalStorageDead,
Some((place_base, elem)) => {
// FIXME(spastorino) make this iterate
let base_access = self.classify_drop_access_kind(PlaceRef {
local: place.local,
projection: proj_base,
});
let base_access = self.classify_drop_access_kind(place_base);
match elem {
ProjectionElem::Deref => match base_access {
StorageDeadOrDrop::LocalStorageDead
| StorageDeadOrDrop::BoxedStorageDead => {
assert!(
Place::ty_from(place.local, proj_base, self.body, tcx).ty.is_box(),
place_base.ty(self.body, tcx).ty.is_box(),
"Drop of value behind a reference or raw pointer"
);
StorageDeadOrDrop::BoxedStorageDead
}
StorageDeadOrDrop::Destructor(_) => base_access,
},
ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
let base_ty = Place::ty_from(place.local, proj_base, self.body, tcx).ty;
let base_ty = place_base.ty(self.body, tcx).ty;
match base_ty.kind() {
ty::Adt(def, _) if def.has_dtor(tcx) => {
// Report the outermost adt with a destructor
Expand All @@ -1652,7 +1649,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
_ => base_access,
}
}

ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Index(_) => base_access,
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
self.describe_field(PlaceRef { local, projection: proj_base }, field)
}
ProjectionElem::Downcast(_, variant_index) => {
let base_ty =
Place::ty_from(place.local, place.projection, self.body, self.infcx.tcx).ty;
let base_ty = place.ty(self.body, self.infcx.tcx).ty;
self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
}
ProjectionElem::Field(_, field_type) => {
Expand Down Expand Up @@ -473,7 +472,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {

// If we didn't find an overloaded deref or index, then assume it's a
// built in deref and check the type of the base.
let base_ty = Place::ty_from(deref_base.local, deref_base.projection, self.body, tcx).ty;
let base_ty = deref_base.ty(self.body, tcx).ty;
if base_ty.is_unsafe_ptr() {
BorrowedContentSource::DerefRawPointer
} else if base_ty.is_mutable_ptr() {
Expand Down
16 changes: 7 additions & 9 deletions compiler/rustc_mir/src/borrow_check/path_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,27 +147,25 @@ pub(crate) fn is_upvar_field_projection(
place_ref: PlaceRef<'tcx>,
body: &Body<'tcx>,
) -> Option<Field> {
let mut place_projection = place_ref.projection;
let mut place_ref = place_ref;
let mut by_ref = false;

if let [proj_base @ .., ProjectionElem::Deref] = place_projection {
place_projection = proj_base;
if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() {
place_ref = place_base;
by_ref = true;
}

match place_projection {
[base @ .., ProjectionElem::Field(field, _ty)] => {
let base_ty = Place::ty_from(place_ref.local, base, body, tcx).ty;

match place_ref.last_projection() {
Some((place_base, ProjectionElem::Field(field, _ty))) => {
let base_ty = place_base.ty(body, tcx).ty;
if (base_ty.is_closure() || base_ty.is_generator())
&& (!by_ref || upvars[field.index()].by_ref)
{
Some(*field)
Some(field)
} else {
None
}
}

_ => None,
}
}
10 changes: 3 additions & 7 deletions compiler/rustc_mir/src/dataflow/move_paths/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,14 +518,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {

// Check if we are assigning into a field of a union, if so, lookup the place
// of the union so it is marked as initialized again.
if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection {
if let ty::Adt(def, _) =
Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx)
.ty
.kind()
{
if let Some((place_base, ProjectionElem::Field(_, _))) = place.last_projection() {
if let ty::Adt(def, _) = place_base.ty(self.builder.body, self.builder.tcx).ty.kind() {
if def.is_union() {
place = PlaceRef { local: place.local, projection: proj_base }
place = place_base;
}
}
}
Expand Down
24 changes: 10 additions & 14 deletions compiler/rustc_mir/src/transform/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,10 @@ where

Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
// Special-case reborrows to be more like a copy of the reference.
if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty;
if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() {
let base_ty = place_base.ty(cx.body, cx.tcx).ty;
if let ty::Ref(..) = base_ty.kind() {
return in_place::<Q, _>(
cx,
in_local,
PlaceRef { local: place.local, projection: proj_base },
);
return in_place::<Q, _>(cx, in_local, place_base);
}
}

Expand Down Expand Up @@ -209,9 +205,9 @@ where
Q: Qualif,
F: FnMut(Local) -> bool,
{
let mut projection = place.projection;
while let &[ref proj_base @ .., proj_elem] = projection {
match proj_elem {
let mut place = place;
while let Some((place_base, elem)) = place.last_projection() {
match elem {
ProjectionElem::Index(index) if in_local(index) => return true,

ProjectionElem::Deref
Expand All @@ -222,16 +218,16 @@ where
| ProjectionElem::Index(_) => {}
}

let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx);
let proj_ty = base_ty.projection_ty(cx.tcx, proj_elem).ty;
let base_ty = place_base.ty(cx.body, cx.tcx);
let proj_ty = base_ty.projection_ty(cx.tcx, elem).ty;
if !Q::in_any_value_of_ty(cx, proj_ty) {
return false;
}

projection = proj_base;
place = place_base;
}

assert!(projection.is_empty());
assert!(place.projection.is_empty());
in_local(place.local)
}

Expand Down
39 changes: 19 additions & 20 deletions compiler/rustc_mir/src/transform/check_consts/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,27 +1007,26 @@ fn place_as_reborrow(
body: &Body<'tcx>,
place: Place<'tcx>,
) -> Option<&'a [PlaceElem<'tcx>]> {
place.projection.split_last().and_then(|(outermost, inner)| {
if outermost != &ProjectionElem::Deref {
return None;
}

// A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
// that points to the allocation for the static. Don't treat these as reborrows.
if body.local_decls[place.local].is_ref_to_static() {
return None;
}

// Ensure the type being derefed is a reference and not a raw pointer.
//
// This is sufficient to prevent an access to a `static mut` from being marked as a
// reborrow, even if the check above were to disappear.
let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty;
match inner_ty.kind() {
ty::Ref(..) => Some(inner),
_ => None,
match place.as_ref().last_projection() {
Some((place_base, ProjectionElem::Deref)) => {
// A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
// that points to the allocation for the static. Don't treat these as reborrows.
if body.local_decls[place_base.local].is_ref_to_static() {
None
} else {
// Ensure the type being derefed is a reference and not a raw pointer.
//
// This is sufficient to prevent an access to a `static mut` from being marked as a
// reborrow, even if the check above were to disappear.
let inner_ty = place_base.ty(body, tcx).ty;
match inner_ty.kind() {
ty::Ref(..) => Some(place_base.projection),
_ => None,
}
}
}
})
_ => None,
}
}

fn is_int_bool_or_char(ty: Ty<'_>) -> bool {
Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_mir/src/transform/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,17 +399,13 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
place: Place<'tcx>,
is_mut_use: bool,
) {
let mut cursor = place.projection.as_ref();
while let &[ref proj_base @ .., elem] = cursor {
cursor = proj_base;

for (place_base, elem) in place.iter_projections().rev() {
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;
let ty = place_base.ty(&self.body.local_decls, self.tcx).ty;
if let ty::Adt(def, _) = ty.kind() {
if self.tcx.layout_scalar_valid_range(def.did)
!= (Bound::Unbounded, Bound::Unbounded)
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_mir/src/transform/instcombine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,9 @@ impl OptimizationFinder<'b, 'tcx> {
impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
if let Rvalue::Ref(_, _, place) = rvalue {
if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } =
place.as_ref()
{
if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() {
// The dereferenced place must have type `&_`.
let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty;
let ty = place_base.ty(self.body, self.tcx).ty;
if let ty::Ref(_, _, Mutability::Not) = ty.kind() {
self.optimizations.and_stars.insert(location);
}
Expand Down
96 changes: 50 additions & 46 deletions compiler/rustc_mir/src/transform/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,43 +445,50 @@ impl<'tcx> Validator<'_, 'tcx> {
}

fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> {
match place {
PlaceRef { local, projection: [] } => self.validate_local(local),
PlaceRef { local, projection: [proj_base @ .., elem] } => {
match place.last_projection() {
None => self.validate_local(place.local),
Some((place_base, elem)) => {
// Validate topmost projection, then recurse.
match *elem {
match elem {
ProjectionElem::Deref => {
let mut promotable = false;
// This is a special treatment for cases like *&STATIC where STATIC is a
// global static variable.
// This pattern is generated only when global static variables are directly
// accessed and is qualified for promotion safely.
if let TempState::Defined { location, .. } = self.temps[local] {
let def_stmt =
self.body[location.block].statements.get(location.statement_index);
if let Some(Statement {
kind:
StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(c)))),
..
}) = def_stmt
// The `is_empty` predicate is introduced to exclude the case
// where the projection operations are [ .field, * ].
// The reason is because promotion will be illegal if field
// accesses precede the dereferencing.
// Discussion can be found at
// https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
// There may be opportunity for generalization, but this needs to be
// accounted for.
if place_base.projection.is_empty() {
// This is a special treatment for cases like *&STATIC where STATIC is a
// global static variable.
// This pattern is generated only when global static variables are directly
// accessed and is qualified for promotion safely.
if let TempState::Defined { location, .. } =
self.temps[place_base.local]
{
if let Some(did) = c.check_static_ptr(self.tcx) {
// Evaluating a promoted may not read statics except if it got
// promoted from a static (this is a CTFE check). So we
// can only promote static accesses inside statics.
if let Some(hir::ConstContext::Static(..)) = self.const_kind {
// The `is_empty` predicate is introduced to exclude the case
// where the projection operations are [ .field, * ].
// The reason is because promotion will be illegal if field
// accesses precede the dereferencing.
// Discussion can be found at
// https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
// There may be opportunity for generalization, but this needs to be
// accounted for.
if proj_base.is_empty()
&& !self.tcx.is_thread_local_static(did)
let def_stmt = self.body[location.block]
.statements
.get(location.statement_index);
if let Some(Statement {
kind:
StatementKind::Assign(box (
_,
Rvalue::Use(Operand::Constant(c)),
)),
..
}) = def_stmt
{
if let Some(did) = c.check_static_ptr(self.tcx) {
// Evaluating a promoted may not read statics except if it got
// promoted from a static (this is a CTFE check). So we
// can only promote static accesses inside statics.
if let Some(hir::ConstContext::Static(..)) = self.const_kind
{
promotable = true;
if !self.tcx.is_thread_local_static(did) {
promotable = true;
}
}
}
}
Expand All @@ -502,8 +509,7 @@ impl<'tcx> Validator<'_, 'tcx> {
}

ProjectionElem::Field(..) => {
let base_ty =
Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
let base_ty = place_base.ty(self.body, self.tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No promotion of union field accesses.
if def.is_union() {
Expand All @@ -513,7 +519,7 @@ impl<'tcx> Validator<'_, 'tcx> {
}
}

self.validate_place(PlaceRef { local: place.local, projection: proj_base })
self.validate_place(place_base)
}
}
}
Expand Down Expand Up @@ -660,13 +666,11 @@ impl<'tcx> Validator<'_, 'tcx> {
Rvalue::AddressOf(_, place) => {
// We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is
// no problem, only using it is.
if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection()
{
let base_ty = place_base.ty(self.body, self.tcx).ty;
if let ty::Ref(..) = base_ty.kind() {
return self.validate_place(PlaceRef {
local: place.local,
projection: proj_base,
});
return self.validate_place(place_base);
}
}
return Err(Unpromotable);
Expand All @@ -675,12 +679,12 @@ impl<'tcx> Validator<'_, 'tcx> {
Rvalue::Ref(_, kind, place) => {
// Special-case reborrows to be more like a copy of the reference.
let mut place_simplified = place.as_ref();
if let [proj_base @ .., ProjectionElem::Deref] = &place_simplified.projection {
let base_ty =
Place::ty_from(place_simplified.local, proj_base, self.body, self.tcx).ty;
if let Some((place_base, ProjectionElem::Deref)) =
place_simplified.last_projection()
{
let base_ty = place_base.ty(self.body, self.tcx).ty;
if let ty::Ref(..) = base_ty.kind() {
place_simplified =
PlaceRef { local: place_simplified.local, projection: proj_base };
place_simplified = place_base;
}
}

Expand Down
Loading

0 comments on commit 0677d97

Please sign in to comment.