Refactor and add PlaceContext::AscribeUserTy.

This commit refactors `PlaceContext` to split it into four different
smaller enums based on if the context represents a mutating use,
non-mutating use, maybe-mutating use or a non-use (this is based on the
recommendation from @oli-obk on Zulip[1]).

This commit then introduces a `PlaceContext::AscribeUserTy` variant.
`StatementKind::AscribeUserTy` is now correctly mapped to
`PlaceContext::AscribeUserTy` instead of `PlaceContext::Validate`.
`PlaceContext::AscribeUserTy` can also now be correctly categorized as a
non-use which fixes an issue with constant promotion in statics after a
cast introduces a `AscribeUserTy` statement.

[1]: https://rust-lang.zulipchat.com/#narrow/stream/122657-wg-nll/subject/.2355288.20cast.20fails.20to.20promote.20to.20'static/near/136536949
This commit is contained in:
David Wood 2018-10-26 13:22:45 +02:00
parent 8a0d8c5ab5
commit 6208bd8e03
No known key found for this signature in database
GPG Key ID: 01760B4F9F53F154
13 changed files with 308 additions and 195 deletions

View File

@ -364,33 +364,54 @@ fn super_statement(&mut self,
self.visit_assign(block, place, rvalue, location);
}
StatementKind::FakeRead(_, ref $($mutability)* place) => {
self.visit_place(place,
PlaceContext::Inspect,
location);
self.visit_place(
place,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect),
location
);
}
StatementKind::EndRegion(_) => {}
StatementKind::Validate(_, ref $($mutability)* places) => {
for operand in places {
self.visit_place(& $($mutability)* operand.place,
PlaceContext::Validate, location);
self.visit_place(
& $($mutability)* operand.place,
PlaceContext::NonUse(NonUseContext::Validate),
location
);
self.visit_ty(& $($mutability)* operand.ty,
TyContext::Location(location));
}
}
StatementKind::SetDiscriminant{ ref $($mutability)* place, .. } => {
self.visit_place(place, PlaceContext::Store, location);
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Store),
location
);
}
StatementKind::StorageLive(ref $($mutability)* local) => {
self.visit_local(local, PlaceContext::StorageLive, location);
self.visit_local(
local,
PlaceContext::NonUse(NonUseContext::StorageLive),
location
);
}
StatementKind::StorageDead(ref $($mutability)* local) => {
self.visit_local(local, PlaceContext::StorageDead, location);
self.visit_local(
local,
PlaceContext::NonUse(NonUseContext::StorageDead),
location
);
}
StatementKind::InlineAsm { ref $($mutability)* outputs,
ref $($mutability)* inputs,
asm: _ } => {
for output in & $($mutability)* outputs[..] {
self.visit_place(output, PlaceContext::AsmOutput, location);
self.visit_place(
output,
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
location
);
}
for input in & $($mutability)* inputs[..] {
self.visit_operand(input, location);
@ -412,7 +433,11 @@ fn super_assign(&mut self,
place: &$($mutability)* Place<'tcx>,
rvalue: &$($mutability)* Rvalue<'tcx>,
location: Location) {
self.visit_place(place, PlaceContext::Store, location);
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Store),
location
);
self.visit_rvalue(rvalue, location);
}
@ -459,7 +484,11 @@ fn super_terminator_kind(&mut self,
TerminatorKind::Drop { ref $($mutability)* location,
target,
unwind } => {
self.visit_place(location, PlaceContext::Drop, source_location);
self.visit_place(
location,
PlaceContext::MutatingUse(MutatingUseContext::Drop),
source_location
);
self.visit_branch(block, target);
unwind.map(|t| self.visit_branch(block, t));
}
@ -468,7 +497,11 @@ fn super_terminator_kind(&mut self,
ref $($mutability)* value,
target,
unwind } => {
self.visit_place(location, PlaceContext::Drop, source_location);
self.visit_place(
location,
PlaceContext::MutatingUse(MutatingUseContext::Drop),
source_location
);
self.visit_operand(value, source_location);
self.visit_branch(block, target);
unwind.map(|t| self.visit_branch(block, t));
@ -484,7 +517,11 @@ fn super_terminator_kind(&mut self,
self.visit_operand(arg, source_location);
}
if let Some((ref $($mutability)* destination, target)) = *destination {
self.visit_place(destination, PlaceContext::Call, source_location);
self.visit_place(
destination,
PlaceContext::MutatingUse(MutatingUseContext::Call),
source_location
);
self.visit_branch(block, target);
}
cleanup.map(|t| self.visit_branch(block, t));
@ -552,14 +589,28 @@ fn super_rvalue(&mut self,
Rvalue::Ref(ref $($mutability)* r, bk, ref $($mutability)* path) => {
self.visit_region(r, location);
self.visit_place(path, PlaceContext::Borrow {
region: *r,
kind: bk
}, location);
let ctx = match bk {
BorrowKind::Shared => PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow(*r)
),
BorrowKind::Shallow => PlaceContext::NonMutatingUse(
NonMutatingUseContext::ShallowBorrow(*r)
),
BorrowKind::Unique => PlaceContext::NonMutatingUse(
NonMutatingUseContext::UniqueBorrow(*r)
),
BorrowKind::Mut { .. } =>
PlaceContext::MutatingUse(MutatingUseContext::Borrow(*r)),
};
self.visit_place(path, ctx, location);
}
Rvalue::Len(ref $($mutability)* path) => {
self.visit_place(path, PlaceContext::Inspect, location);
self.visit_place(
path,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect),
location
);
}
Rvalue::Cast(_cast_kind,
@ -584,7 +635,11 @@ fn super_rvalue(&mut self,
}
Rvalue::Discriminant(ref $($mutability)* place) => {
self.visit_place(place, PlaceContext::Inspect, location);
self.visit_place(
place,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect),
location
);
}
Rvalue::NullaryOp(_op, ref $($mutability)* ty) => {
@ -632,10 +687,18 @@ fn super_operand(&mut self,
location: Location) {
match *operand {
Operand::Copy(ref $($mutability)* place) => {
self.visit_place(place, PlaceContext::Copy, location);
self.visit_place(
place,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
location
);
}
Operand::Move(ref $($mutability)* place) => {
self.visit_place(place, PlaceContext::Move, location);
self.visit_place(
place,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move),
location
);
}
Operand::Constant(ref $($mutability)* constant) => {
self.visit_constant(constant, location);
@ -648,7 +711,11 @@ fn super_ascribe_user_ty(&mut self,
_variance: & $($mutability)* ty::Variance,
user_ty: & $($mutability)* UserTypeProjection<'tcx>,
location: Location) {
self.visit_place(place, PlaceContext::Validate, location);
self.visit_place(
place,
PlaceContext::NonUse(NonUseContext::AscribeUserTy),
location
);
self.visit_user_type_projection(user_ty);
}
@ -693,9 +760,9 @@ fn super_projection(&mut self,
ref $($mutability)* elem,
} = *proj;
let context = if context.is_mutating_use() {
PlaceContext::Projection(Mutability::Mut)
PlaceContext::MutatingUse(MutatingUseContext::Projection)
} else {
PlaceContext::Projection(Mutability::Not)
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
self.visit_place(base, context, location);
self.visit_projection_elem(elem, location);
@ -713,7 +780,11 @@ fn super_projection_elem(&mut self,
self.visit_ty(ty, TyContext::Location(location));
}
ProjectionElem::Index(ref $($mutability)* local) => {
self.visit_local(local, PlaceContext::Copy, location);
self.visit_local(
local,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
location
);
}
ProjectionElem::ConstantIndex { offset: _,
min_length: _,
@ -896,125 +967,146 @@ pub enum TyContext {
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PlaceContext<'tcx> {
// Appears as LHS of an assignment
Store,
// Can often be treated as a Store, but needs to be separate because
// ASM is allowed to read outputs as well, so a Store-AsmOutput sequence
// cannot be simplified the way a Store-Store can be.
AsmOutput,
// Dest of a call
Call,
// Being dropped
Drop,
// Being inspected in some way, like loading a len
pub enum NonMutatingUseContext<'tcx> {
/// Being inspected in some way, like loading a len.
Inspect,
// Being borrowed
Borrow { region: Region<'tcx>, kind: BorrowKind },
// Used as base for another place, e.g. `x` in `x.y`.
//
// The `Mutability` argument specifies whether the projection is being performed in order to
// (potentially) mutate the place. For example, the projection `x.y` is marked as a mutation
// in these cases:
//
// x.y = ...;
// f(&mut x.y);
//
// But not in these cases:
//
// z = x.y;
// f(&x.y);
Projection(Mutability),
// Consumed as part of an operand
/// Consumed as part of an operand.
Copy,
/// Consumed as part of an operand.
Move,
/// Shared borrow.
SharedBorrow(Region<'tcx>),
/// Shallow borrow.
ShallowBorrow(Region<'tcx>),
/// Unique borrow.
UniqueBorrow(Region<'tcx>),
/// Used as base for another place, e.g. `x` in `x.y`. Will not mutate the place.
/// For example, the projection `x.y` is not marked as a mutation in these cases:
///
/// z = x.y;
/// f(&x.y);
///
Projection,
}
// Starting and ending a storage live range
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MutatingUseContext<'tcx> {
/// Appears as LHS of an assignment.
Store,
/// Can often be treated as a `Store`, but needs to be separate because
/// ASM is allowed to read outputs as well, so a `Store`-`AsmOutput` sequence
/// cannot be simplified the way a `Store`-`Store` can be.
AsmOutput,
/// Destination of a call.
Call,
/// Being dropped.
Drop,
/// Mutable borrow.
Borrow(Region<'tcx>),
/// Used as base for another place, e.g. `x` in `x.y`. Could potentially mutate the place.
/// For example, the projection `x.y` is marked as a mutation in these cases:
///
/// x.y = ...;
/// f(&mut x.y);
///
Projection,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NonUseContext {
/// Starting a storage live range.
StorageLive,
/// Ending a storage live range.
StorageDead,
// Validation command
/// User type annotation assertions for NLL.
AscribeUserTy,
/// Validation command.
Validate,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PlaceContext<'tcx> {
NonMutatingUse(NonMutatingUseContext<'tcx>),
MutatingUse(MutatingUseContext<'tcx>),
NonUse(NonUseContext),
}
impl<'tcx> PlaceContext<'tcx> {
/// Returns true if this place context represents a drop.
/// Returns `true` if this place context represents a drop.
pub fn is_drop(&self) -> bool {
match *self {
PlaceContext::Drop => true,
PlaceContext::MutatingUse(MutatingUseContext::Drop) => true,
_ => false,
}
}
/// Returns true if this place context represents a storage live or storage dead marker.
/// Returns `true` if this place context represents a borrow.
pub fn is_borrow(&self) -> bool {
match *self {
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(..)) |
PlaceContext::MutatingUse(MutatingUseContext::Borrow(..)) => true,
_ => false,
}
}
/// Returns `true` if this place context represents a storage live or storage dead marker.
pub fn is_storage_marker(&self) -> bool {
match *self {
PlaceContext::StorageLive | PlaceContext::StorageDead => true,
PlaceContext::NonUse(NonUseContext::StorageLive) |
PlaceContext::NonUse(NonUseContext::StorageDead) => true,
_ => false,
}
}
/// Returns true if this place context represents a storage live marker.
/// Returns `true` if this place context represents a storage live marker.
pub fn is_storage_live_marker(&self) -> bool {
match *self {
PlaceContext::StorageLive => true,
PlaceContext::NonUse(NonUseContext::StorageLive) => true,
_ => false,
}
}
/// Returns true if this place context represents a storage dead marker.
/// Returns `true` if this place context represents a storage dead marker.
pub fn is_storage_dead_marker(&self) -> bool {
match *self {
PlaceContext::StorageDead => true,
PlaceContext::NonUse(NonUseContext::StorageDead) => true,
_ => false,
}
}
/// Returns true if this place context represents a use that potentially changes the value.
/// Returns `true` if this place context represents a use that potentially changes the value.
pub fn is_mutating_use(&self) -> bool {
match *self {
PlaceContext::Store | PlaceContext::AsmOutput | PlaceContext::Call |
PlaceContext::Borrow { kind: BorrowKind::Mut { .. }, .. } |
PlaceContext::Projection(Mutability::Mut) |
PlaceContext::Drop => true,
PlaceContext::Inspect |
PlaceContext::Borrow { kind: BorrowKind::Shared, .. } |
PlaceContext::Borrow { kind: BorrowKind::Shallow, .. } |
PlaceContext::Borrow { kind: BorrowKind::Unique, .. } |
PlaceContext::Projection(Mutability::Not) |
PlaceContext::Copy | PlaceContext::Move |
PlaceContext::StorageLive | PlaceContext::StorageDead |
PlaceContext::Validate => false,
PlaceContext::MutatingUse(..) => true,
_ => false,
}
}
/// Returns true if this place context represents a use that does not change the value.
/// Returns `true` if this place context represents a use that does not change the value.
pub fn is_nonmutating_use(&self) -> bool {
match *self {
PlaceContext::Inspect |
PlaceContext::Borrow { kind: BorrowKind::Shared, .. } |
PlaceContext::Borrow { kind: BorrowKind::Shallow, .. } |
PlaceContext::Borrow { kind: BorrowKind::Unique, .. } |
PlaceContext::Projection(Mutability::Not) |
PlaceContext::Copy | PlaceContext::Move => true,
PlaceContext::Borrow { kind: BorrowKind::Mut { .. }, .. } | PlaceContext::Store |
PlaceContext::AsmOutput |
PlaceContext::Call | PlaceContext::Projection(Mutability::Mut) |
PlaceContext::Drop | PlaceContext::StorageLive | PlaceContext::StorageDead |
PlaceContext::Validate => false,
PlaceContext::NonMutatingUse(..) => true,
_ => false,
}
}
/// Returns `true` if this place context represents a use.
pub fn is_use(&self) -> bool {
self.is_mutating_use() || self.is_nonmutating_use()
match *self {
PlaceContext::NonUse(..) => false,
_ => true,
}
}
/// Returns `true` if this place context represents an assignment statement.
pub fn is_place_assignment(&self) -> bool {
match *self {
PlaceContext::MutatingUse(MutatingUseContext::Store) |
PlaceContext::MutatingUse(MutatingUseContext::Call) |
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) => true,
_ => false,
}
}
}

View File

@ -15,7 +15,7 @@
use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, PlaceContext};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::traversal;
use rustc::ty;
use rustc::ty::layout::LayoutOf;
@ -116,7 +116,11 @@ fn visit_assign(&mut self,
self.not_ssa(index);
}
} else {
self.visit_place(place, PlaceContext::Store, location);
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Store),
location
);
}
self.visit_rvalue(rvalue, location);
@ -142,7 +146,11 @@ fn visit_terminator_kind(&mut self,
// is not guaranteed to be statically dominated by the
// definition of x, so x must always be in an alloca.
if let mir::Operand::Move(ref place) = args[0] {
self.visit_place(place, PlaceContext::Drop, location);
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Drop),
location
);
}
}
}
@ -160,7 +168,8 @@ fn visit_place(&mut self,
if let mir::Place::Projection(ref proj) = *place {
// Allow uses of projections that are ZSTs or from scalar fields.
let is_consume = match context {
PlaceContext::Copy | PlaceContext::Move => true,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
_ => false
};
if is_consume {
@ -190,7 +199,11 @@ fn visit_place(&mut self,
// A deref projection only reads the pointer, never needs the place.
if let mir::ProjectionElem::Deref = proj.elem {
return self.visit_place(&proj.base, PlaceContext::Copy, location);
return self.visit_place(
&proj.base,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
location
);
}
}
@ -202,16 +215,14 @@ fn visit_local(&mut self,
context: PlaceContext<'tcx>,
location: Location) {
match context {
PlaceContext::Call => {
PlaceContext::MutatingUse(MutatingUseContext::Call) => {
self.assign(local, location);
}
PlaceContext::StorageLive |
PlaceContext::StorageDead |
PlaceContext::Validate => {}
PlaceContext::NonUse(_) => {}
PlaceContext::Copy |
PlaceContext::Move => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => {
// Reads from uninitialized variables (e.g. in dead code, after
// optimizations) require locals to be in (uninitialized) memory.
// NB: there can be uninitialized reads of a local visited after
@ -227,15 +238,19 @@ fn visit_local(&mut self,
}
}
PlaceContext::Inspect |
PlaceContext::Store |
PlaceContext::AsmOutput |
PlaceContext::Borrow { .. } |
PlaceContext::Projection(..) => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
PlaceContext::MutatingUse(MutatingUseContext::Store) |
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
PlaceContext::MutatingUse(MutatingUseContext::Borrow(..)) |
PlaceContext::MutatingUse(MutatingUseContext::Projection) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
self.not_ssa(local);
}
PlaceContext::Drop => {
PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx);
let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx));

View File

@ -12,7 +12,9 @@
use dataflow::indexes::BorrowIndex;
use dataflow::move_paths::MoveData;
use rustc::mir::traversal;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::visit::{
PlaceContext, Visitor, NonUseContext, MutatingUseContext, NonMutatingUseContext
};
use rustc::mir::{self, Location, Mir, Place, Local};
use rustc::ty::{Region, TyCtxt};
use rustc::util::nodemap::{FxHashMap, FxHashSet};
@ -116,7 +118,7 @@ fn build(
impl<'tcx> Visitor<'tcx> for HasStorageDead {
fn visit_local(&mut self, local: &Local, ctx: PlaceContext<'tcx>, _: Location) {
if ctx == PlaceContext::StorageDead {
if ctx == PlaceContext::NonUse(NonUseContext::StorageDead) {
self.0.insert(*local);
}
}
@ -266,7 +268,9 @@ fn visit_place(
// Watch out: the use of TMP in the borrow itself
// doesn't count as an activation. =)
if borrow_data.reserve_location == location && context == PlaceContext::Store {
if borrow_data.reserve_location == location &&
context == PlaceContext::MutatingUse(MutatingUseContext::Store)
{
return;
}
@ -287,10 +291,9 @@ fn visit_place(
borrow_data.activation_location = match context {
// The use of TMP in a shared borrow does not
// count as an actual activation.
PlaceContext::Borrow { kind: mir::BorrowKind::Shared, .. }
| PlaceContext::Borrow { kind: mir::BorrowKind::Shallow, .. } => {
TwoPhaseActivation::NotActivated
}
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(..)) =>
TwoPhaseActivation::NotActivated,
_ => {
// Double check: This borrow is indeed a two-phase borrow (that is,
// we are 'transitioning' from `NotActivated` to `ActivatedAt`) and

View File

@ -35,7 +35,7 @@
use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
use rustc::mir::tcx::PlaceTy;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::*;
use rustc::traits::query::type_op;
use rustc::traits::query::type_op::custom::CustomTypeOp;
@ -472,9 +472,9 @@ fn sanitize_place(
}
Place::Projection(ref proj) => {
let base_context = if context.is_mutating_use() {
PlaceContext::Projection(Mutability::Mut)
PlaceContext::MutatingUse(MutatingUseContext::Projection)
} else {
PlaceContext::Projection(Mutability::Not)
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
let base_ty = self.sanitize_place(&proj.base, location, base_context);
if let PlaceTy::Ty { ty } = base_ty {
@ -488,7 +488,7 @@ fn sanitize_place(
self.sanitize_projection(base_ty, &proj.elem, place, location)
}
};
if let PlaceContext::Copy = context {
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
let tcx = self.tcx();
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().copy_trait().unwrap(),

View File

@ -14,7 +14,6 @@
use rustc_data_structures::fx::FxHashSet;
use borrow_check::MirBorrowckCtxt;
use util::collect_writes::is_place_assignment;
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
/// Walks the MIR looking for assignments to a set of locals, as part of the unused mutable
@ -46,7 +45,7 @@ fn visit_local(
return;
}
if is_place_assignment(&place_context) {
if place_context.is_place_assignment() {
// Propagate the Local assigned at this Location as a used mutable local variable
for moi in &self.mbcx.move_data.loc_map[location] {
let mpi = &self.mbcx.move_data.moves[*moi].path;

View File

@ -19,7 +19,7 @@
use rustc::hir::def_id::DefId;
use rustc::lint::builtin::{SAFE_EXTERN_STATICS, SAFE_PACKED_BORROWS, UNUSED_UNSAFE};
use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext};
use syntax::ast;
use syntax::symbol::Symbol;
@ -152,7 +152,7 @@ fn visit_place(&mut self,
place: &Place<'tcx>,
context: PlaceContext<'tcx>,
location: Location) {
if let PlaceContext::Borrow { .. } = context {
if context.is_borrow() {
if util::is_disaligned(self.tcx, self.mir, self.param_env, place) {
let source_info = self.source_info;
let lint_root =
@ -193,9 +193,11 @@ fn visit_place(&mut self,
}
ty::Adt(adt, _) => {
if adt.is_union() {
if context == PlaceContext::Store ||
context == PlaceContext::AsmOutput ||
context == PlaceContext::Drop
if context == PlaceContext::MutatingUse(MutatingUseContext::Store) ||
context == PlaceContext::MutatingUse(MutatingUseContext::Drop) ||
context == PlaceContext::MutatingUse(
MutatingUseContext::AsmOutput
)
{
let elem_ty = match elem {
&ProjectionElem::Field(_, ty) => ty,

View File

@ -16,7 +16,7 @@
use rustc::mir::{Constant, Location, Place, Mir, Operand, Rvalue, Local};
use rustc::mir::{NullOp, UnOp, StatementKind, Statement, BasicBlock, LocalKind};
use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
use rustc::mir::visit::{Visitor, PlaceContext};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::interpret::{
ConstEvalErr, EvalErrorKind, Scalar, GlobalId, EvalResult,
};
@ -533,17 +533,18 @@ fn visit_local(
// Constants must have at most one write
// FIXME(oli-obk): we could be more powerful here, if the multiple writes
// only occur in independent execution paths
Store => if self.found_assignment[local] {
MutatingUse(MutatingUseContext::Store) => if self.found_assignment[local] {
self.can_const_prop[local] = false;
} else {
self.found_assignment[local] = true
},
// Reading constants is allowed an arbitrary number of times
Copy | Move |
StorageDead | StorageLive |
Validate |
Projection(_) |
Inspect => {},
NonMutatingUse(NonMutatingUseContext::Copy) |
NonMutatingUse(NonMutatingUseContext::Move) |
NonMutatingUse(NonMutatingUseContext::Inspect) |
NonMutatingUse(NonMutatingUseContext::Projection) |
MutatingUse(MutatingUseContext::Projection) |
NonUse(_) => {},
_ => self.can_const_prop[local] = false,
}
}

View File

@ -23,7 +23,7 @@
//! move analysis runs after promotion on broken MIR.
use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor};
use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
use rustc::ty::TyCtxt;
use syntax_pos::Span;
@ -97,11 +97,11 @@ fn visit_local(&mut self,
// Ignore drops, if the temp gets promoted,
// then it's constant and thus drop is noop.
// Storage live ranges are also irrelevant.
if context.is_drop() || context.is_storage_marker() {
// Non-uses are also irrelevent.
if context.is_drop() || !context.is_use() {
debug!(
"visit_local: context.is_drop={:?} context.is_storage_marker={:?}",
context.is_drop(), context.is_storage_marker(),
"visit_local: context.is_drop={:?} context.is_use={:?}",
context.is_drop(), context.is_use(),
);
return;
}
@ -110,9 +110,9 @@ fn visit_local(&mut self,
debug!("visit_local: temp={:?}", temp);
if *temp == TempState::Undefined {
match context {
PlaceContext::Store |
PlaceContext::AsmOutput |
PlaceContext::Call => {
PlaceContext::MutatingUse(MutatingUseContext::Store) |
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
PlaceContext::MutatingUse(MutatingUseContext::Call) => {
*temp = TempState::Defined {
location,
uses: 0
@ -124,10 +124,7 @@ fn visit_local(&mut self,
} else if let TempState::Defined { ref mut uses, .. } = *temp {
// We always allow borrows, even mutable ones, as we need
// to promote mutable borrows of some ZSTs e.g. `&mut []`.
let allowed_use = match context {
PlaceContext::Borrow {..} => true,
_ => context.is_nonmutating_use()
};
let allowed_use = context.is_borrow() || context.is_nonmutating_use();
debug!("visit_local: allowed_use={:?}", allowed_use);
if allowed_use {
*uses += 1;

View File

@ -26,7 +26,7 @@
use rustc::ty::query::Providers;
use rustc::mir::*;
use rustc::mir::traversal::ReversePostorder;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
use rustc::middle::lang_items;
use rustc_target::spec::abi::Abi;
use syntax::ast::LitKind;
@ -271,7 +271,11 @@ fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
// This must be an explicit assignment.
_ => {
// Catch more errors in the destination.
self.visit_place(dest, PlaceContext::Store, location);
self.visit_place(
dest,
PlaceContext::MutatingUse(MutatingUseContext::Store),
location
);
self.statement_like();
}
}
@ -610,10 +614,17 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
}
if is_reborrow {
self.super_place(place, PlaceContext::Borrow {
region,
kind
}, location);
let ctx = match kind {
BorrowKind::Shared =>
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(region)),
BorrowKind::Shallow =>
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(region)),
BorrowKind::Unique =>
PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(region)),
BorrowKind::Mut { .. } =>
PlaceContext::MutatingUse(MutatingUseContext::Borrow(region)),
};
self.super_place(place, ctx, location);
} else {
self.super_rvalue(rvalue, location);
}

View File

@ -331,8 +331,10 @@ struct DeclMarker {
impl<'tcx> Visitor<'tcx> for DeclMarker {
fn visit_local(&mut self, local: &Local, ctx: PlaceContext<'tcx>, _: Location) {
// ignore these altogether, they get removed along with their otherwise unused decls.
if ctx != PlaceContext::StorageLive && ctx != PlaceContext::StorageDead {
// Ignore storage markers altogether, they get removed along with their otherwise unused
// decls.
// FIXME: Extend this to all non-uses.
if !ctx.is_storage_marker() {
self.locals.insert(*local);
}
}

View File

@ -39,7 +39,7 @@
use rustc::ty;
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc::mir::visit::{Visitor, PlaceContext};
use rustc::mir::visit::{Visitor, PlaceContext, NonUseContext};
use transform::{MirPass, MirSource};
use util::patch::MirPatch;
use rustc_data_structures::indexed_vec::{IndexVec};
@ -316,8 +316,8 @@ fn visit_local(&mut self,
location: Location) {
let local_use = &mut self.locals_use[*local];
match context {
PlaceContext::StorageLive => local_use.alive = Some(location),
PlaceContext::StorageDead => local_use.dead = Some(location),
PlaceContext::NonUse(NonUseContext::StorageLive) => local_use.alive = Some(location),
PlaceContext::NonUse(NonUseContext::StorageDead) => local_use.dead = Some(location),
_ => {
local_use.use_count += 1;
if local_use.first_use.is_none() {

View File

@ -43,24 +43,8 @@ fn visit_local(&mut self,
return;
}
if is_place_assignment(&place_context) {
if place_context.is_place_assignment() {
self.locations.push(location);
}
}
}
/// Returns true if this place context represents an assignment statement
crate fn is_place_assignment(place_context: &PlaceContext) -> bool {
match *place_context {
PlaceContext::Store | PlaceContext::Call | PlaceContext::AsmOutput => true,
PlaceContext::Drop
| PlaceContext::Inspect
| PlaceContext::Borrow { .. }
| PlaceContext::Projection(..)
| PlaceContext::Copy
| PlaceContext::Move
| PlaceContext::StorageLive
| PlaceContext::StorageDead
| PlaceContext::Validate => false,
}
}

View File

@ -33,7 +33,9 @@
//! generator yield points, all pre-existing references are invalidated, so this
//! doesn't matter).
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::visit::{
PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext, NonUseContext,
};
use rustc::mir::Local;
use rustc::mir::*;
use rustc::ty::{item_path, TyCtxt};
@ -161,10 +163,10 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>) -> Option<DefUse> {
///////////////////////////////////////////////////////////////////////////
// DEFS
PlaceContext::Store |
PlaceContext::MutatingUse(MutatingUseContext::Store) |
// This is potentially both a def and a use...
PlaceContext::AsmOutput |
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
// We let Call define the result in both the success and
// unwind cases. This is not really correct, however it
@ -172,12 +174,12 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>) -> Option<DefUse> {
// generate MIR. To do things properly, we would apply
// the def in call only to the input from the success
// path and not the unwind path. -nmatsakis
PlaceContext::Call |
PlaceContext::MutatingUse(MutatingUseContext::Call) |
// Storage live and storage dead aren't proper defines, but we can ignore
// values that come before them.
PlaceContext::StorageLive |
PlaceContext::StorageDead => Some(DefUse::Def),
PlaceContext::NonUse(NonUseContext::StorageLive) |
PlaceContext::NonUse(NonUseContext::StorageDead) => Some(DefUse::Def),
///////////////////////////////////////////////////////////////////////////
// REGULAR USES
@ -186,18 +188,23 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>) -> Option<DefUse> {
// purposes of NLL, these are special in that **all** the
// lifetimes appearing in the variable must be live for each regular use.
PlaceContext::Projection(..) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) |
PlaceContext::MutatingUse(MutatingUseContext::Projection) |
// Borrows only consider their local used at the point of the borrow.
// This won't affect the results since we use this analysis for generators
// and we only care about the result at suspension points. Borrows cannot
// cross suspension points so this behavior is unproblematic.
PlaceContext::Borrow { .. } |
PlaceContext::MutatingUse(MutatingUseContext::Borrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(..)) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(..)) |
PlaceContext::Inspect |
PlaceContext::Copy |
PlaceContext::Move |
PlaceContext::Validate =>
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) |
PlaceContext::NonUse(NonUseContext::AscribeUserTy) |
PlaceContext::NonUse(NonUseContext::Validate) =>
Some(DefUse::Use),
///////////////////////////////////////////////////////////////////////////
@ -208,7 +215,7 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>) -> Option<DefUse> {
// uses in drop are special because `#[may_dangle]`
// attributes can affect whether lifetimes must be live.
PlaceContext::Drop =>
PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
Some(DefUse::Drop),
}
}