Auto merge of #47837 - eddyb:going-places, r=nikomatsakis

Replace "lvalue" terminology with "place".

See #46425 for the previous PR (which only changed MIR-related code).

r? @nikomatsakis
This commit is contained in:
bors 2018-01-29 19:47:48 +00:00
commit 90eb44a589
46 changed files with 470 additions and 483 deletions

View File

@ -1034,10 +1034,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
hir::ExprAssign(ref l, ref r) => { hir::ExprAssign(ref l, ref r) => {
// see comment on lvalues in // see comment on places in
// propagate_through_lvalue_components() // propagate_through_place_components()
let succ = self.write_lvalue(&l, succ, ACC_WRITE); let succ = self.write_place(&l, succ, ACC_WRITE);
let succ = self.propagate_through_lvalue_components(&l, succ); let succ = self.propagate_through_place_components(&l, succ);
self.propagate_through_expr(&r, succ) self.propagate_through_expr(&r, succ)
} }
@ -1047,11 +1047,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let succ = self.propagate_through_expr(&l, succ); let succ = self.propagate_through_expr(&l, succ);
self.propagate_through_expr(&r, succ) self.propagate_through_expr(&r, succ)
} else { } else {
// see comment on lvalues in // see comment on places in
// propagate_through_lvalue_components() // propagate_through_place_components()
let succ = self.write_lvalue(&l, succ, ACC_WRITE|ACC_READ); let succ = self.write_place(&l, succ, ACC_WRITE|ACC_READ);
let succ = self.propagate_through_expr(&r, succ); let succ = self.propagate_through_expr(&r, succ);
self.propagate_through_lvalue_components(&l, succ) self.propagate_through_place_components(&l, succ)
} }
} }
@ -1121,14 +1121,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprInlineAsm(ref ia, ref outputs, ref inputs) => { hir::ExprInlineAsm(ref ia, ref outputs, ref inputs) => {
let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| { let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| {
// see comment on lvalues // see comment on places
// in propagate_through_lvalue_components() // in propagate_through_place_components()
if o.is_indirect { if o.is_indirect {
self.propagate_through_expr(output, succ) self.propagate_through_expr(output, succ)
} else { } else {
let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE }; let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
let succ = self.write_lvalue(output, succ, acc); let succ = self.write_place(output, succ, acc);
self.propagate_through_lvalue_components(output, succ) self.propagate_through_place_components(output, succ)
} }
}); });
@ -1146,11 +1146,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
} }
fn propagate_through_lvalue_components(&mut self, fn propagate_through_place_components(&mut self,
expr: &Expr, expr: &Expr,
succ: LiveNode) succ: LiveNode)
-> LiveNode { -> LiveNode {
// # Lvalues // # Places
// //
// In general, the full flow graph structure for an // In general, the full flow graph structure for an
// assignment/move/etc can be handled in one of two ways, // assignment/move/etc can be handled in one of two ways,
@ -1160,7 +1160,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
// //
// The two kinds of graphs are: // The two kinds of graphs are:
// //
// Tracked lvalue Untracked lvalue // Tracked place Untracked place
// ----------------------++----------------------- // ----------------------++-----------------------
// || // ||
// | || | // | || |
@ -1168,7 +1168,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
// (rvalue) || (rvalue) // (rvalue) || (rvalue)
// | || | // | || |
// v || v // v || v
// (write of lvalue) || (lvalue components) // (write of place) || (place components)
// | || | // | || |
// v || v // v || v
// (succ) || (succ) // (succ) || (succ)
@ -1177,25 +1177,25 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
// //
// I will cover the two cases in turn: // I will cover the two cases in turn:
// //
// # Tracked lvalues // # Tracked places
// //
// A tracked lvalue is a local variable/argument `x`. In // A tracked place is a local variable/argument `x`. In
// these cases, the link_node where the write occurs is linked // these cases, the link_node where the write occurs is linked
// to node id of `x`. The `write_lvalue()` routine generates // to node id of `x`. The `write_place()` routine generates
// the contents of this node. There are no subcomponents to // the contents of this node. There are no subcomponents to
// consider. // consider.
// //
// # Non-tracked lvalues // # Non-tracked places
// //
// These are lvalues like `x[5]` or `x.f`. In that case, we // These are places like `x[5]` or `x.f`. In that case, we
// basically ignore the value which is written to but generate // basically ignore the value which is written to but generate
// reads for the components---`x` in these two examples. The // reads for the components---`x` in these two examples. The
// components reads are generated by // components reads are generated by
// `propagate_through_lvalue_components()` (this fn). // `propagate_through_place_components()` (this fn).
// //
// # Illegal lvalues // # Illegal places
// //
// It is still possible to observe assignments to non-lvalues; // It is still possible to observe assignments to non-places;
// these errors are detected in the later pass borrowck. We // these errors are detected in the later pass borrowck. We
// just ignore such cases and treat them as reads. // just ignore such cases and treat them as reads.
@ -1207,17 +1207,17 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
} }
// see comment on propagate_through_lvalue() // see comment on propagate_through_place()
fn write_lvalue(&mut self, expr: &Expr, succ: LiveNode, acc: u32) fn write_place(&mut self, expr: &Expr, succ: LiveNode, acc: u32)
-> LiveNode { -> LiveNode {
match expr.node { match expr.node {
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => { hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
self.access_path(expr.id, path, succ, acc) self.access_path(expr.id, path, succ, acc)
} }
// We do not track other lvalues, so just propagate through // We do not track other places, so just propagate through
// to their subcomponents. Also, it may happen that // to their subcomponents. Also, it may happen that
// non-lvalues occur here, because those are detected in the // non-places occur here, because those are detected in the
// later pass borrowck. // later pass borrowck.
_ => succ _ => succ
} }
@ -1363,14 +1363,14 @@ fn check_arm<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, arm: &'tcx hir::Arm) {
fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) { fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
match expr.node { match expr.node {
hir::ExprAssign(ref l, _) => { hir::ExprAssign(ref l, _) => {
this.check_lvalue(&l); this.check_place(&l);
intravisit::walk_expr(this, expr); intravisit::walk_expr(this, expr);
} }
hir::ExprAssignOp(_, ref l, _) => { hir::ExprAssignOp(_, ref l, _) => {
if !this.tables.is_method_call(expr) { if !this.tables.is_method_call(expr) {
this.check_lvalue(&l); this.check_place(&l);
} }
intravisit::walk_expr(this, expr); intravisit::walk_expr(this, expr);
@ -1381,10 +1381,10 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
this.visit_expr(input); this.visit_expr(input);
} }
// Output operands must be lvalues // Output operands must be places
for (o, output) in ia.outputs.iter().zip(outputs) { for (o, output) in ia.outputs.iter().zip(outputs) {
if !o.is_indirect { if !o.is_indirect {
this.check_lvalue(output); this.check_place(output);
} }
this.visit_expr(output); this.visit_expr(output);
} }
@ -1409,7 +1409,7 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
} }
impl<'a, 'tcx> Liveness<'a, 'tcx> { impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn check_lvalue(&mut self, expr: &'tcx Expr) { fn check_place(&mut self, expr: &'tcx Expr) {
match expr.node { match expr.node {
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => { hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
if let Def::Local(nid) = path.def { if let Def::Local(nid) = path.def {
@ -1423,7 +1423,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
} }
_ => { _ => {
// For other kinds of lvalues, no checks are required, // For other kinds of places, no checks are required,
// and any embedded expressions are actually rvalues // and any embedded expressions are actually rvalues
intravisit::walk_expr(self, expr); intravisit::walk_expr(self, expr);
} }

View File

@ -26,8 +26,8 @@
//! | E.comp // access to an interior component //! | E.comp // access to an interior component
//! //!
//! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an //! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
//! address where the result is to be found. If Expr is an lvalue, then this //! address where the result is to be found. If Expr is a place, then this
//! is the address of the lvalue. If Expr is an rvalue, this is the address of //! is the address of the place. If Expr is an rvalue, this is the address of
//! some temporary spot in memory where the result is stored. //! some temporary spot in memory where the result is stored.
//! //!
//! Now, cat_expr() classifies the expression Expr and the address A=ToAddr(Expr) //! Now, cat_expr() classifies the expression Expr and the address A=ToAddr(Expr)
@ -182,7 +182,7 @@ pub struct cmt_<'tcx> {
pub id: ast::NodeId, // id of expr/pat producing this value pub id: ast::NodeId, // id of expr/pat producing this value
pub span: Span, // span of same expr/pat pub span: Span, // span of same expr/pat
pub cat: Categorization<'tcx>, // categorization of expr pub cat: Categorization<'tcx>, // categorization of expr
pub mutbl: MutabilityCategory, // mutability of expr as lvalue pub mutbl: MutabilityCategory, // mutability of expr as place
pub ty: Ty<'tcx>, // type of the expr (*see WARNING above*) pub ty: Ty<'tcx>, // type of the expr (*see WARNING above*)
pub note: Note, // Note about the provenance of this cmt pub note: Note, // Note about the provenance of this cmt
} }
@ -517,7 +517,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
// a bind-by-ref means that the base_ty will be the type of the ident itself, // a bind-by-ref means that the base_ty will be the type of the ident itself,
// but what we want here is the type of the underlying value being borrowed. // but what we want here is the type of the underlying value being borrowed.
// So peel off one-level, turning the &T into T. // So peel off one-level, turning the &T into T.
match base_ty.builtin_deref(false, ty::NoPreference) { match base_ty.builtin_deref(false) {
Some(t) => t.ty, Some(t) => t.ty,
None => { None => {
debug!("By-ref binding of non-derefable type {:?}", base_ty); debug!("By-ref binding of non-derefable type {:?}", base_ty);
@ -603,7 +603,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
match expr.node { match expr.node {
hir::ExprUnary(hir::UnDeref, ref e_base) => { hir::ExprUnary(hir::UnDeref, ref e_base) => {
if self.tables.is_method_call(expr) { if self.tables.is_method_call(expr) {
self.cat_overloaded_lvalue(expr, e_base, false) self.cat_overloaded_place(expr, e_base, false)
} else { } else {
let base_cmt = self.cat_expr(&e_base)?; let base_cmt = self.cat_expr(&e_base)?;
self.cat_deref(expr, base_cmt, false) self.cat_deref(expr, base_cmt, false)
@ -631,7 +631,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
// The call to index() returns a `&T` value, which // The call to index() returns a `&T` value, which
// is an rvalue. That is what we will be // is an rvalue. That is what we will be
// dereferencing. // dereferencing.
self.cat_overloaded_lvalue(expr, base, true) self.cat_overloaded_place(expr, base, true)
} else { } else {
let base_cmt = self.cat_expr(&base)?; let base_cmt = self.cat_expr(&base)?;
self.cat_index(expr, base_cmt, expr_ty, InteriorOffsetKind::Index) self.cat_index(expr, base_cmt, expr_ty, InteriorOffsetKind::Index)
@ -983,27 +983,27 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
ret ret
} }
fn cat_overloaded_lvalue(&self, fn cat_overloaded_place(&self,
expr: &hir::Expr, expr: &hir::Expr,
base: &hir::Expr, base: &hir::Expr,
implicit: bool) implicit: bool)
-> McResult<cmt<'tcx>> { -> McResult<cmt<'tcx>> {
debug!("cat_overloaded_lvalue: implicit={}", implicit); debug!("cat_overloaded_place: implicit={}", implicit);
// Reconstruct the output assuming it's a reference with the // Reconstruct the output assuming it's a reference with the
// same region and mutability as the receiver. This holds for // same region and mutability as the receiver. This holds for
// `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
let lvalue_ty = self.expr_ty(expr)?; let place_ty = self.expr_ty(expr)?;
let base_ty = self.expr_ty_adjusted(base)?; let base_ty = self.expr_ty_adjusted(base)?;
let (region, mutbl) = match base_ty.sty { let (region, mutbl) = match base_ty.sty {
ty::TyRef(region, mt) => (region, mt.mutbl), ty::TyRef(region, mt) => (region, mt.mutbl),
_ => { _ => {
span_bug!(expr.span, "cat_overloaded_lvalue: base is not a reference") span_bug!(expr.span, "cat_overloaded_place: base is not a reference")
} }
}; };
let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut { let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut {
ty: lvalue_ty, ty: place_ty,
mutbl, mutbl,
}); });
@ -1019,7 +1019,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
debug!("cat_deref: base_cmt={:?}", base_cmt); debug!("cat_deref: base_cmt={:?}", base_cmt);
let base_cmt_ty = base_cmt.ty; let base_cmt_ty = base_cmt.ty;
let deref_ty = match base_cmt_ty.builtin_deref(true, ty::NoPreference) { let deref_ty = match base_cmt_ty.builtin_deref(true) {
Some(mt) => mt.ty, Some(mt) => mt.ty,
None => { None => {
debug!("Explicit deref of non-derefable type: {:?}", debug!("Explicit deref of non-derefable type: {:?}",
@ -1386,7 +1386,7 @@ impl<'tcx> cmt_<'tcx> {
} }
} }
/// Returns `FreelyAliasable(_)` if this lvalue represents a freely aliasable pointer type. /// Returns `FreelyAliasable(_)` if this place represents a freely aliasable pointer type.
pub fn freely_aliasable(&self) -> Aliasability { pub fn freely_aliasable(&self) -> Aliasability {
// Maybe non-obvious: copied upvars can only be considered // Maybe non-obvious: copied upvars can only be considered
// non-aliasable in once closures, since any other kind can be // non-aliasable in once closures, since any other kind can be
@ -1453,7 +1453,7 @@ impl<'tcx> cmt_<'tcx> {
"static item".to_string() "static item".to_string()
} }
Categorization::Rvalue(..) => { Categorization::Rvalue(..) => {
"non-lvalue".to_string() "non-place".to_string()
} }
Categorization::Local(vid) => { Categorization::Local(vid) => {
if tcx.hir.is_argument(vid) { if tcx.hir.is_argument(vid) {

View File

@ -1112,7 +1112,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
// I mean that creating a binding into a ref-counted or managed value // I mean that creating a binding into a ref-counted or managed value
// would still count.) // would still count.)
// //
// 3. `ET`, which matches both rvalues like `foo()` as well as lvalues // 3. `ET`, which matches both rvalues like `foo()` as well as places
// based on rvalues like `foo().x[2].y`. // based on rvalues like `foo().x[2].y`.
// //
// A subexpression `<rvalue>` that appears in a let initializer // A subexpression `<rvalue>` that appears in a let initializer
@ -1283,7 +1283,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
/// | (ET) /// | (ET)
/// | <rvalue> /// | <rvalue>
/// ///
/// Note: ET is intended to match "rvalues or lvalues based on rvalues". /// Note: ET is intended to match "rvalues or places based on rvalues".
fn record_rvalue_scope<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, fn record_rvalue_scope<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
expr: &hir::Expr, expr: &hir::Expr,
blk_scope: Option<Scope>) { blk_scope: Option<Scope>) {

View File

@ -733,13 +733,13 @@ pub enum TerminatorKind<'tcx> {
}, },
/// Drop the Place and assign the new value over it. This ensures /// Drop the Place and assign the new value over it. This ensures
/// that the assignment to LV occurs *even if* the destructor for /// that the assignment to `P` occurs *even if* the destructor for
/// place unwinds. Its semantics are best explained by by the /// place unwinds. Its semantics are best explained by by the
/// elaboration: /// elaboration:
/// ///
/// ``` /// ```
/// BB0 { /// BB0 {
/// DropAndReplace(LV <- RV, goto BB1, unwind BB2) /// DropAndReplace(P <- V, goto BB1, unwind BB2)
/// } /// }
/// ``` /// ```
/// ///
@ -747,15 +747,15 @@ pub enum TerminatorKind<'tcx> {
/// ///
/// ``` /// ```
/// BB0 { /// BB0 {
/// Drop(LV, goto BB1, unwind BB2) /// Drop(P, goto BB1, unwind BB2)
/// } /// }
/// BB1 { /// BB1 {
/// // LV is now unitialized /// // P is now unitialized
/// LV <- RV /// P <- V
/// } /// }
/// BB2 { /// BB2 {
/// // LV is now unitialized -- its dtor panicked /// // P is now unitialized -- its dtor panicked
/// LV <- RV /// P <- V
/// } /// }
/// ``` /// ```
DropAndReplace { DropAndReplace {

View File

@ -52,7 +52,7 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
match *elem { match *elem {
ProjectionElem::Deref => { ProjectionElem::Deref => {
let ty = self.to_ty(tcx) let ty = self.to_ty(tcx)
.builtin_deref(true, ty::LvaluePreference::NoPreference) .builtin_deref(true)
.unwrap_or_else(|| { .unwrap_or_else(|| {
bug!("deref projection of non-dereferencable ty {:?}", self) bug!("deref projection of non-dereferencable ty {:?}", self)
}) })

View File

@ -77,7 +77,7 @@ pub enum Adjust<'tcx> {
/// Go from a mut raw pointer to a const raw pointer. /// Go from a mut raw pointer to a const raw pointer.
MutToConstPointer, MutToConstPointer,
/// Dereference once, producing an lvalue. /// Dereference once, producing a place.
Deref(Option<OverloadedDeref<'tcx>>), Deref(Option<OverloadedDeref<'tcx>>),
/// Take the address and produce either a `&` or `*` pointer. /// Take the address and produce either a `&` or `*` pointer.

View File

@ -12,7 +12,6 @@ pub use self::Variance::*;
pub use self::AssociatedItemContainer::*; pub use self::AssociatedItemContainer::*;
pub use self::BorrowKind::*; pub use self::BorrowKind::*;
pub use self::IntVarValue::*; pub use self::IntVarValue::*;
pub use self::LvaluePreference::*;
pub use self::fold::TypeFoldable; pub use self::fold::TypeFoldable;
use hir::{map as hir_map, FreevarMap, TraitMap}; use hir::{map as hir_map, FreevarMap, TraitMap};
@ -2099,21 +2098,6 @@ impl<'tcx> TyS<'tcx> {
} }
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LvaluePreference {
PreferMutLvalue,
NoPreference
}
impl LvaluePreference {
pub fn from_mutbl(m: hir::Mutability) -> Self {
match m {
hir::MutMutable => PreferMutLvalue,
hir::MutImmutable => NoPreference,
}
}
}
impl BorrowKind { impl BorrowKind {
pub fn from_mutbl(m: hir::Mutability) -> BorrowKind { pub fn from_mutbl(m: hir::Mutability) -> BorrowKind {
match m { match m {
@ -2193,60 +2177,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
} }
} }
pub fn expr_is_lval(self, expr: &hir::Expr) -> bool {
match expr.node {
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
match path.def {
Def::Local(..) | Def::Upvar(..) | Def::Static(..) | Def::Err => true,
_ => false,
}
}
hir::ExprType(ref e, _) => {
self.expr_is_lval(e)
}
hir::ExprUnary(hir::UnDeref, _) |
hir::ExprField(..) |
hir::ExprTupField(..) |
hir::ExprIndex(..) => {
true
}
// Partially qualified paths in expressions can only legally
// refer to associated items which are always rvalues.
hir::ExprPath(hir::QPath::TypeRelative(..)) |
hir::ExprCall(..) |
hir::ExprMethodCall(..) |
hir::ExprStruct(..) |
hir::ExprTup(..) |
hir::ExprIf(..) |
hir::ExprMatch(..) |
hir::ExprClosure(..) |
hir::ExprBlock(..) |
hir::ExprRepeat(..) |
hir::ExprArray(..) |
hir::ExprBreak(..) |
hir::ExprAgain(..) |
hir::ExprRet(..) |
hir::ExprWhile(..) |
hir::ExprLoop(..) |
hir::ExprAssign(..) |
hir::ExprInlineAsm(..) |
hir::ExprAssignOp(..) |
hir::ExprLit(_) |
hir::ExprUnary(..) |
hir::ExprBox(..) |
hir::ExprAddrOf(..) |
hir::ExprBinary(..) |
hir::ExprYield(..) |
hir::ExprCast(..) => {
false
}
}
}
pub fn provided_trait_methods(self, id: DefId) -> Vec<AssociatedItem> { pub fn provided_trait_methods(self, id: DefId) -> Vec<AssociatedItem> {
self.associated_items(id) self.associated_items(id)
.filter(|item| item.kind == AssociatedKind::Method && item.defaultness.has_value()) .filter(|item| item.kind == AssociatedKind::Method && item.defaultness.has_value())

View File

@ -1514,18 +1514,12 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
/// ///
/// The parameter `explicit` indicates if this is an *explicit* dereference. /// The parameter `explicit` indicates if this is an *explicit* dereference.
/// Some types---notably unsafe ptrs---can only be dereferenced explicitly. /// Some types---notably unsafe ptrs---can only be dereferenced explicitly.
pub fn builtin_deref(&self, explicit: bool, pref: ty::LvaluePreference) pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut<'tcx>> {
-> Option<TypeAndMut<'tcx>>
{
match self.sty { match self.sty {
TyAdt(def, _) if def.is_box() => { TyAdt(def, _) if def.is_box() => {
Some(TypeAndMut { Some(TypeAndMut {
ty: self.boxed_ty(), ty: self.boxed_ty(),
mutbl: if pref == ty::PreferMutLvalue { mutbl: hir::MutImmutable,
hir::MutMutable
} else {
hir::MutImmutable
},
}) })
}, },
TyRef(_, mt) => Some(mt), TyRef(_, mt) => Some(mt),

View File

@ -43,14 +43,14 @@ it is safe with respect to the in-scope loans.
# Formal model # Formal model
Throughout the docs we'll consider a simple subset of Rust in which Throughout the docs we'll consider a simple subset of Rust in which
you can only borrow from lvalues, defined like so: you can only borrow from places, defined like so:
```text ```text
LV = x | LV.f | *LV P = x | P.f | *P
``` ```
Here `x` represents some variable, `LV.f` is a field reference, Here `x` represents some variable, `P.f` is a field reference,
and `*LV` is a pointer dereference. There is no auto-deref or other and `*P` is a pointer dereference. There is no auto-deref or other
niceties. This means that if you have a type like: niceties. This means that if you have a type like:
```rust ```rust
@ -58,7 +58,7 @@ struct S { f: i32 }
``` ```
and a variable `a: Box<S>`, then the rust expression `a.f` would correspond and a variable `a: Box<S>`, then the rust expression `a.f` would correspond
to an `LV` of `(*a).f`. to an `P` of `(*a).f`.
Here is the formal grammar for the types we'll consider: Here is the formal grammar for the types we'll consider:
@ -99,7 +99,7 @@ this sort of thing.
#### Loans and restrictions #### Loans and restrictions
The way the borrow checker works is that it analyzes each borrow The way the borrow checker works is that it analyzes each borrow
expression (in our simple model, that's stuff like `&LV`, though in expression (in our simple model, that's stuff like `&P`, though in
real life there are a few other cases to consider). For each borrow real life there are a few other cases to consider). For each borrow
expression, it computes a `Loan`, which is a data structure that expression, it computes a `Loan`, which is a data structure that
records (1) the value being borrowed, (2) the mutability and scope of records (1) the value being borrowed, (2) the mutability and scope of
@ -108,29 +108,29 @@ struct defined in `middle::borrowck`. Formally, we define `LOAN` as
follows: follows:
```text ```text
LOAN = (LV, LT, MQ, RESTRICTION*) LOAN = (P, LT, MQ, RESTRICTION*)
RESTRICTION = (LV, ACTION*) RESTRICTION = (P, ACTION*)
ACTION = MUTATE | CLAIM | FREEZE ACTION = MUTATE | CLAIM | FREEZE
``` ```
Here the `LOAN` tuple defines the lvalue `LV` being borrowed; the Here the `LOAN` tuple defines the place `P` being borrowed; the
lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a
list of restrictions. The restrictions indicate actions which, if list of restrictions. The restrictions indicate actions which, if
taken, could invalidate the loan and lead to type safety violations. taken, could invalidate the loan and lead to type safety violations.
Each `RESTRICTION` is a pair of a restrictive lvalue `LV` (which will Each `RESTRICTION` is a pair of a restrictive place `P` (which will
either be the path that was borrowed or some prefix of the path that either be the path that was borrowed or some prefix of the path that
was borrowed) and a set of restricted actions. There are three kinds was borrowed) and a set of restricted actions. There are three kinds
of actions that may be restricted for the path `LV`: of actions that may be restricted for the path `P`:
- `MUTATE` means that `LV` cannot be assigned to; - `MUTATE` means that `P` cannot be assigned to;
- `CLAIM` means that the `LV` cannot be borrowed mutably; - `CLAIM` means that the `P` cannot be borrowed mutably;
- `FREEZE` means that the `LV` cannot be borrowed immutably; - `FREEZE` means that the `P` cannot be borrowed immutably;
Finally, it is never possible to move from an lvalue that appears in a Finally, it is never possible to move from a place that appears in a
restriction. This implies that the "empty restriction" `(LV, [])`, restriction. This implies that the "empty restriction" `(P, [])`,
which contains an empty set of actions, still has a purpose---it which contains an empty set of actions, still has a purpose---it
prevents moves from `LV`. I chose not to make `MOVE` a fourth kind of prevents moves from `P`. I chose not to make `MOVE` a fourth kind of
action because that would imply that sometimes moves are permitted action because that would imply that sometimes moves are permitted
from restricted values, which is not the case. from restricted values, which is not the case.
@ -239,22 +239,22 @@ live. (This is done via restrictions, read on.)
We start with the `gather_loans` pass, which walks the AST looking for We start with the `gather_loans` pass, which walks the AST looking for
borrows. For each borrow, there are three bits of information: the borrows. For each borrow, there are three bits of information: the
lvalue `LV` being borrowed and the mutability `MQ` and lifetime `LT` place `P` being borrowed and the mutability `MQ` and lifetime `LT`
of the resulting pointer. Given those, `gather_loans` applies four of the resulting pointer. Given those, `gather_loans` applies four
validity tests: validity tests:
1. `MUTABILITY(LV, MQ)`: The mutability of the reference is 1. `MUTABILITY(P, MQ)`: The mutability of the reference is
compatible with the mutability of `LV` (i.e., not borrowing immutable compatible with the mutability of `P` (i.e., not borrowing immutable
data as mutable). data as mutable).
2. `ALIASABLE(LV, MQ)`: The aliasability of the reference is 2. `ALIASABLE(P, MQ)`: The aliasability of the reference is
compatible with the aliasability of `LV`. The goal is to prevent compatible with the aliasability of `P`. The goal is to prevent
`&mut` borrows of aliasability data. `&mut` borrows of aliasability data.
3. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed 3. `LIFETIME(P, LT, MQ)`: The lifetime of the borrow does not exceed
the lifetime of the value being borrowed. the lifetime of the value being borrowed.
4. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the 4. `RESTRICTIONS(P, LT, ACTIONS) = RS`: This pass checks and computes the
restrictions to maintain memory safety. These are the restrictions restrictions to maintain memory safety. These are the restrictions
that will go into the final loan. We'll discuss in more detail below. that will go into the final loan. We'll discuss in more detail below.
@ -263,7 +263,7 @@ that will go into the final loan. We'll discuss in more detail below.
Checking mutability is fairly straightforward. We just want to prevent Checking mutability is fairly straightforward. We just want to prevent
immutable data from being borrowed as mutable. Note that it is ok to borrow immutable data from being borrowed as mutable. Note that it is ok to borrow
mutable data as immutable, since that is simply a freeze. The judgement mutable data as immutable, since that is simply a freeze. The judgement
`MUTABILITY(LV, MQ)` means the mutability of `LV` is compatible with a borrow `MUTABILITY(P, MQ)` means the mutability of `P` is compatible with a borrow
of mutability `MQ`. The Rust code corresponding to this predicate is the of mutability `MQ`. The Rust code corresponding to this predicate is the
function `check_mutability` in `middle::borrowck::gather_loans`. function `check_mutability` in `middle::borrowck::gather_loans`.
@ -288,15 +288,15 @@ MUTABILITY(X, imm) // M-Var-Imm
Fields and boxes inherit their mutability from Fields and boxes inherit their mutability from
their base expressions, so both of their rules basically their base expressions, so both of their rules basically
delegate the check to the base expression `LV`: delegate the check to the base expression `P`:
```text ```text
MUTABILITY(LV.f, MQ) // M-Field MUTABILITY(P.f, MQ) // M-Field
MUTABILITY(LV, MQ) MUTABILITY(P, MQ)
MUTABILITY(*LV, MQ) // M-Deref-Unique MUTABILITY(*P, MQ) // M-Deref-Unique
TYPE(LV) = Box<Ty> TYPE(P) = Box<Ty>
MUTABILITY(LV, MQ) MUTABILITY(P, MQ)
``` ```
### Checking mutability of immutable pointer types ### Checking mutability of immutable pointer types
@ -305,8 +305,8 @@ Immutable pointer types like `&T` can only
be borrowed if MQ is immutable: be borrowed if MQ is immutable:
```text ```text
MUTABILITY(*LV, imm) // M-Deref-Borrowed-Imm MUTABILITY(*P, imm) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty TYPE(P) = &Ty
``` ```
### Checking mutability of mutable pointer types ### Checking mutability of mutable pointer types
@ -314,15 +314,15 @@ MUTABILITY(*LV, imm) // M-Deref-Borrowed-Imm
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut: `&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
```text ```text
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut MUTABILITY(*P, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty TYPE(P) = &mut Ty
``` ```
## Checking aliasability ## Checking aliasability
The goal of the aliasability check is to ensure that we never permit `&mut` The goal of the aliasability check is to ensure that we never permit `&mut`
borrows of aliasable data. The judgement `ALIASABLE(LV, MQ)` means the borrows of aliasable data. The judgement `ALIASABLE(P, MQ)` means the
aliasability of `LV` is compatible with a borrow of mutability `MQ`. The Rust aliasability of `P` is compatible with a borrow of mutability `MQ`. The Rust
code corresponding to this predicate is the function `check_aliasability()` in code corresponding to this predicate is the function `check_aliasability()` in
`middle::borrowck::gather_loans`. `middle::borrowck::gather_loans`.
@ -340,11 +340,11 @@ the stack frame.
Owned content is aliasable if it is found in an aliasable location: Owned content is aliasable if it is found in an aliasable location:
```text ```text
ALIASABLE(LV.f, MQ) // M-Field ALIASABLE(P.f, MQ) // M-Field
ALIASABLE(LV, MQ) ALIASABLE(P, MQ)
ALIASABLE(*LV, MQ) // M-Deref-Unique ALIASABLE(*P, MQ) // M-Deref-Unique
ALIASABLE(LV, MQ) ALIASABLE(P, MQ)
``` ```
### Checking aliasability of immutable pointer types ### Checking aliasability of immutable pointer types
@ -353,8 +353,8 @@ Immutable pointer types like `&T` are aliasable, and hence can only be
borrowed immutably: borrowed immutably:
```text ```text
ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm ALIASABLE(*P, imm) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty TYPE(P) = &Ty
``` ```
### Checking aliasability of mutable pointer types ### Checking aliasability of mutable pointer types
@ -362,16 +362,16 @@ ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut: `&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
```text ```text
ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut ALIASABLE(*P, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty TYPE(P) = &mut Ty
``` ```
## Checking lifetime ## Checking lifetime
These rules aim to ensure that no data is borrowed for a scope that exceeds These rules aim to ensure that no data is borrowed for a scope that exceeds
its lifetime. These two computations wind up being intimately related. its lifetime. These two computations wind up being intimately related.
Formally, we define a predicate `LIFETIME(LV, LT, MQ)`, which states that Formally, we define a predicate `LIFETIME(P, LT, MQ)`, which states that
"the lvalue `LV` can be safely borrowed for the lifetime `LT` with mutability "the place `P` can be safely borrowed for the lifetime `LT` with mutability
`MQ`". The Rust code corresponding to this predicate is the module `MQ`". The Rust code corresponding to this predicate is the module
`middle::borrowck::gather_loans::lifetime`. `middle::borrowck::gather_loans::lifetime`.
@ -391,12 +391,12 @@ The lifetime of a field or box is the same as the lifetime
of its owner: of its owner:
```text ```text
LIFETIME(LV.f, LT, MQ) // L-Field LIFETIME(P.f, LT, MQ) // L-Field
LIFETIME(LV, LT, MQ) LIFETIME(P, LT, MQ)
LIFETIME(*LV, LT, MQ) // L-Deref-Send LIFETIME(*P, LT, MQ) // L-Deref-Send
TYPE(LV) = Box<Ty> TYPE(P) = Box<Ty>
LIFETIME(LV, LT, MQ) LIFETIME(P, LT, MQ)
``` ```
### Checking lifetime for derefs of references ### Checking lifetime for derefs of references
@ -408,8 +408,8 @@ of the borrow is shorter than the lifetime `LT'` of the pointer
itself: itself:
```text ```text
LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed LIFETIME(*P, LT, MQ) // L-Deref-Borrowed
TYPE(LV) = &LT' Ty OR &LT' mut Ty TYPE(P) = &LT' Ty OR &LT' mut Ty
LT <= LT' LT <= LT'
``` ```
@ -417,17 +417,17 @@ LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed
The final rules govern the computation of *restrictions*, meaning that The final rules govern the computation of *restrictions*, meaning that
we compute the set of actions that will be illegal for the life of the we compute the set of actions that will be illegal for the life of the
loan. The predicate is written `RESTRICTIONS(LV, LT, ACTIONS) = loan. The predicate is written `RESTRICTIONS(P, LT, ACTIONS) =
RESTRICTION*`, which can be read "in order to prevent `ACTIONS` from RESTRICTION*`, which can be read "in order to prevent `ACTIONS` from
occurring on `LV`, the restrictions `RESTRICTION*` must be respected occurring on `P`, the restrictions `RESTRICTION*` must be respected
for the lifetime of the loan". for the lifetime of the loan".
Note that there is an initial set of restrictions: these restrictions Note that there is an initial set of restrictions: these restrictions
are computed based on the kind of borrow: are computed based on the kind of borrow:
```text ```text
&mut LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE) &mut P => RESTRICTIONS(P, LT, MUTATE|CLAIM|FREEZE)
&LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM) &P => RESTRICTIONS(P, LT, MUTATE|CLAIM)
``` ```
The reasoning here is that a mutable borrow must be the only writer, The reasoning here is that a mutable borrow must be the only writer,
@ -451,8 +451,8 @@ Restricting a field is the same as restricting the owner of that
field: field:
```text ```text
RESTRICTIONS(LV.f, LT, ACTIONS) = RS, (LV.f, ACTIONS) // R-Field RESTRICTIONS(P.f, LT, ACTIONS) = RS, (P.f, ACTIONS) // R-Field
RESTRICTIONS(LV, LT, ACTIONS) = RS RESTRICTIONS(P, LT, ACTIONS) = RS
``` ```
The reasoning here is as follows. If the field must not be mutated, The reasoning here is as follows. If the field must not be mutated,
@ -467,16 +467,16 @@ origin of inherited mutability.
Because the mutability of owned referents is inherited, restricting an Because the mutability of owned referents is inherited, restricting an
owned referent is similar to restricting a field, in that it implies owned referent is similar to restricting a field, in that it implies
restrictions on the pointer. However, boxes have an important restrictions on the pointer. However, boxes have an important
twist: if the owner `LV` is mutated, that causes the owned referent twist: if the owner `P` is mutated, that causes the owned referent
`*LV` to be freed! So whenever an owned referent `*LV` is borrowed, we `*P` to be freed! So whenever an owned referent `*P` is borrowed, we
must prevent the box `LV` from being mutated, which means must prevent the box `P` from being mutated, which means
that we always add `MUTATE` and `CLAIM` to the restriction set imposed that we always add `MUTATE` and `CLAIM` to the restriction set imposed
on `LV`: on `P`:
```text ```text
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer RESTRICTIONS(*P, LT, ACTIONS) = RS, (*P, ACTIONS) // R-Deref-Send-Pointer
TYPE(LV) = Box<Ty> TYPE(P) = Box<Ty>
RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS RESTRICTIONS(P, LT, ACTIONS|MUTATE|CLAIM) = RS
``` ```
### Restrictions for loans of immutable borrowed referents ### Restrictions for loans of immutable borrowed referents
@ -484,15 +484,15 @@ RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer
Immutable borrowed referents are freely aliasable, meaning that Immutable borrowed referents are freely aliasable, meaning that
the compiler does not prevent you from copying the pointer. This the compiler does not prevent you from copying the pointer. This
implies that issuing restrictions is useless. We might prevent the implies that issuing restrictions is useless. We might prevent the
user from acting on `*LV` itself, but there could be another path user from acting on `*P` itself, but there could be another path
`*LV1` that refers to the exact same memory, and we would not be `*P1` that refers to the exact same memory, and we would not be
restricting that path. Therefore, the rule for `&Ty` pointers restricting that path. Therefore, the rule for `&Ty` pointers
always returns an empty set of restrictions, and it only permits always returns an empty set of restrictions, and it only permits
restricting `MUTATE` and `CLAIM` actions: restricting `MUTATE` and `CLAIM` actions:
```text ```text
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed RESTRICTIONS(*P, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed
TYPE(LV) = &LT' Ty TYPE(P) = &LT' Ty
LT <= LT' // (1) LT <= LT' // (1)
ACTIONS subset of [MUTATE, CLAIM] ACTIONS subset of [MUTATE, CLAIM]
``` ```
@ -546,7 +546,7 @@ This function is legal. The reason for this is that the inner pointer
(`*point : &'b Point`) is enough to guarantee the memory is immutable (`*point : &'b Point`) is enough to guarantee the memory is immutable
and valid for the lifetime `'b`. This is reflected in and valid for the lifetime `'b`. This is reflected in
`RESTRICTIONS()` by the fact that we do not recurse (i.e., we impose `RESTRICTIONS()` by the fact that we do not recurse (i.e., we impose
no restrictions on `LV`, which in this particular case is the pointer no restrictions on `P`, which in this particular case is the pointer
`point : &'a &'b Point`). `point : &'a &'b Point`).
#### Why both `LIFETIME()` and `RESTRICTIONS()`? #### Why both `LIFETIME()` and `RESTRICTIONS()`?
@ -612,10 +612,10 @@ while the new claimant is live.
The rule for mutable borrowed pointers is as follows: The rule for mutable borrowed pointers is as follows:
```text ```text
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Mut-Borrowed RESTRICTIONS(*P, LT, ACTIONS) = RS, (*P, ACTIONS) // R-Deref-Mut-Borrowed
TYPE(LV) = &LT' mut Ty TYPE(P) = &LT' mut Ty
LT <= LT' // (1) LT <= LT' // (1)
RESTRICTIONS(LV, LT, ACTIONS) = RS // (2) RESTRICTIONS(P, LT, ACTIONS) = RS // (2)
``` ```
Let's examine the two numbered clauses: Let's examine the two numbered clauses:
@ -670,7 +670,7 @@ fn foo(t0: &mut i32) {
Remember that `&mut` pointers are linear, and hence `let t1 = t0` is a Remember that `&mut` pointers are linear, and hence `let t1 = t0` is a
move of `t0` -- or would be, if it were legal. Instead, we get an move of `t0` -- or would be, if it were legal. Instead, we get an
error, because clause (2) imposes restrictions on `LV` (`t0`, here), error, because clause (2) imposes restrictions on `P` (`t0`, here),
and any restrictions on a path make it impossible to move from that and any restrictions on a path make it impossible to move from that
path. path.
@ -906,7 +906,7 @@ results of a dataflow computation.
The `MovePath` tree tracks every path that is moved or assigned to. The `MovePath` tree tracks every path that is moved or assigned to.
These paths have the same form as the `LoanPath` data structure, which These paths have the same form as the `LoanPath` data structure, which
in turn is the "real world version of the lvalues `LV` that we in turn is the "real world version of the places `P` that we
introduced earlier. The difference between a `MovePath` and a `LoanPath` introduced earlier. The difference between a `MovePath` and a `LoanPath`
is that move paths are: is that move paths are:
@ -1132,7 +1132,7 @@ is implied by the relevant moves.
While writing up these docs, I encountered some rules I believe to be While writing up these docs, I encountered some rules I believe to be
stricter than necessary: stricter than necessary:
- I think restricting the `&mut` LV against moves and `ALIAS` is sufficient, - I think restricting the `&mut` P against moves and `ALIAS` is sufficient,
`MUTATE` and `CLAIM` are overkill. `MUTATE` was necessary when swap was `MUTATE` and `CLAIM` are overkill. `MUTATE` was necessary when swap was
a built-in operator, but as it is not, it is implied by `CLAIM`, a built-in operator, but as it is not, it is implied by `CLAIM`,
and `CLAIM` is implied by `ALIAS`. The only net effect of this is an and `CLAIM` is implied by `ALIAS`. The only net effect of this is an

View File

@ -104,7 +104,7 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
fn scope(&self, cmt: &mc::cmt<'tcx>) -> ty::Region<'tcx> { fn scope(&self, cmt: &mc::cmt<'tcx>) -> ty::Region<'tcx> {
//! Returns the maximal region scope for the which the //! Returns the maximal region scope for the which the
//! lvalue `cmt` is guaranteed to be valid without any //! place `cmt` is guaranteed to be valid without any
//! rooting etc, and presuming `cmt` is not mutated. //! rooting etc, and presuming `cmt` is not mutated.
match cmt.cat { match cmt.cat {

View File

@ -170,7 +170,7 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
if !force_analysis && move_data.is_empty() && all_loans.is_empty() { if !force_analysis && move_data.is_empty() && all_loans.is_empty() {
// large arrays of data inserted as constants can take a lot of // large arrays of data inserted as constants can take a lot of
// time and memory to borrow-check - see issue #36799. However, // time and memory to borrow-check - see issue #36799. However,
// they don't have lvalues, so no borrow-check is actually needed. // they don't have places, so no borrow-check is actually needed.
// Recognize that case and skip borrow-checking. // Recognize that case and skip borrow-checking.
debug!("skipping loan propagation for {:?} because of no loans", body_id); debug!("skipping loan propagation for {:?} because of no loans", body_id);
return None; return None;
@ -384,9 +384,9 @@ impl ToInteriorKind for mc::InteriorKind {
} }
// This can be: // This can be:
// - a pointer dereference (`*LV` in README.md) // - a pointer dereference (`*P` in README.md)
// - a field reference, with an optional definition of the containing // - a field reference, with an optional definition of the containing
// enum variant (`LV.f` in README.md) // enum variant (`P.f` in README.md)
// `DefId` is present when the field is part of struct that is in // `DefId` is present when the field is part of struct that is in
// a variant of an enum. For instance in: // a variant of an enum. For instance in:
// `enum E { X { foo: u32 }, Y { foo: u32 }}` // `enum E { X { foo: u32 }, Y { foo: u32 }}`

View File

@ -153,7 +153,7 @@ pub struct Assignment {
/// span of node where assignment occurs /// span of node where assignment occurs
pub span: Span, pub span: Span,
/// id for l-value expression on lhs of assignment /// id for place expression on lhs of assignment
pub assignee_id: hir::ItemLocalId, pub assignee_id: hir::ItemLocalId,
} }

View File

@ -15,8 +15,8 @@
use rustc::mir::{BasicBlock, Location}; use rustc::mir::{BasicBlock, Location};
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use dataflow::{EverInitializedLvals, MovingOutStatements}; use dataflow::{EverInitializedPlaces, MovingOutStatements};
use dataflow::{ActiveBorrows, FlowAtLocation, FlowsAtLocation}; use dataflow::{ActiveBorrows, FlowAtLocation, FlowsAtLocation};
use dataflow::move_paths::HasMoveData; use dataflow::move_paths::HasMoveData;
use std::fmt; use std::fmt;
@ -24,19 +24,19 @@ use std::fmt;
// (forced to be `pub` due to its use as an associated type below.) // (forced to be `pub` due to its use as an associated type below.)
pub(crate) struct Flows<'b, 'gcx: 'tcx, 'tcx: 'b> { pub(crate) struct Flows<'b, 'gcx: 'tcx, 'tcx: 'b> {
pub borrows: FlowAtLocation<ActiveBorrows<'b, 'gcx, 'tcx>>, pub borrows: FlowAtLocation<ActiveBorrows<'b, 'gcx, 'tcx>>,
pub inits: FlowAtLocation<MaybeInitializedLvals<'b, 'gcx, 'tcx>>, pub inits: FlowAtLocation<MaybeInitializedPlaces<'b, 'gcx, 'tcx>>,
pub uninits: FlowAtLocation<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>, pub uninits: FlowAtLocation<MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
pub move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>, pub move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>,
pub ever_inits: FlowAtLocation<EverInitializedLvals<'b, 'gcx, 'tcx>>, pub ever_inits: FlowAtLocation<EverInitializedPlaces<'b, 'gcx, 'tcx>>,
} }
impl<'b, 'gcx, 'tcx> Flows<'b, 'gcx, 'tcx> { impl<'b, 'gcx, 'tcx> Flows<'b, 'gcx, 'tcx> {
pub fn new( pub fn new(
borrows: FlowAtLocation<ActiveBorrows<'b, 'gcx, 'tcx>>, borrows: FlowAtLocation<ActiveBorrows<'b, 'gcx, 'tcx>>,
inits: FlowAtLocation<MaybeInitializedLvals<'b, 'gcx, 'tcx>>, inits: FlowAtLocation<MaybeInitializedPlaces<'b, 'gcx, 'tcx>>,
uninits: FlowAtLocation<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>, uninits: FlowAtLocation<MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>, move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>,
ever_inits: FlowAtLocation<EverInitializedLvals<'b, 'gcx, 'tcx>>, ever_inits: FlowAtLocation<EverInitializedPlaces<'b, 'gcx, 'tcx>>,
) -> Self { ) -> Self {
Flows { Flows {
borrows, borrows,

View File

@ -35,8 +35,8 @@ use dataflow::{do_dataflow, DebugFormatted};
use dataflow::FlowAtLocation; use dataflow::FlowAtLocation;
use dataflow::MoveDataParamEnv; use dataflow::MoveDataParamEnv;
use dataflow::{DataflowAnalysis, DataflowResultsConsumer}; use dataflow::{DataflowAnalysis, DataflowResultsConsumer};
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use dataflow::{EverInitializedLvals, MovingOutStatements}; use dataflow::{EverInitializedPlaces, MovingOutStatements};
use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex}; use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex};
use dataflow::{ActiveBorrows, Reservations}; use dataflow::{ActiveBorrows, Reservations};
use dataflow::indexes::BorrowIndex; use dataflow::indexes::BorrowIndex;
@ -160,7 +160,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
id, id,
&attributes, &attributes,
&dead_unwinds, &dead_unwinds,
MaybeInitializedLvals::new(tcx, mir, &mdpe), MaybeInitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]), |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
)); ));
let flow_uninits = FlowAtLocation::new(do_dataflow( let flow_uninits = FlowAtLocation::new(do_dataflow(
@ -169,7 +169,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
id, id,
&attributes, &attributes,
&dead_unwinds, &dead_unwinds,
MaybeUninitializedLvals::new(tcx, mir, &mdpe), MaybeUninitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]), |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
)); ));
let flow_move_outs = FlowAtLocation::new(do_dataflow( let flow_move_outs = FlowAtLocation::new(do_dataflow(
@ -187,7 +187,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
id, id,
&attributes, &attributes,
&dead_unwinds, &dead_unwinds,
EverInitializedLvals::new(tcx, mir, &mdpe), EverInitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().inits[i]), |bd, i| DebugFormatted::new(&bd.move_data().inits[i]),
)); ));
@ -607,7 +607,7 @@ enum ArtificialField {
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ShallowOrDeep { enum ShallowOrDeep {
/// From the RFC: "A *shallow* access means that the immediate /// From the RFC: "A *shallow* access means that the immediate
/// fields reached at LV are accessed, but references or pointers /// fields reached at P are accessed, but references or pointers
/// found within are not dereferenced. Right now, the only access /// found within are not dereferenced. Right now, the only access
/// that is shallow is an assignment like `x = ...;`, which would /// that is shallow is an assignment like `x = ...;`, which would
/// be a *shallow write* of `x`." /// be a *shallow write* of `x`."

View File

@ -19,7 +19,7 @@ use std::io;
use transform::MirSource; use transform::MirSource;
use util::liveness::{LivenessResults, LocalSet}; use util::liveness::{LivenessResults, LocalSet};
use dataflow::FlowAtLocation; use dataflow::FlowAtLocation;
use dataflow::MaybeInitializedLvals; use dataflow::MaybeInitializedPlaces;
use dataflow::move_paths::MoveData; use dataflow::move_paths::MoveData;
use util as mir_util; use util as mir_util;
@ -71,7 +71,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
universal_regions: UniversalRegions<'tcx>, universal_regions: UniversalRegions<'tcx>,
mir: &Mir<'tcx>, mir: &Mir<'tcx>,
param_env: ty::ParamEnv<'gcx>, param_env: ty::ParamEnv<'gcx>,
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>, flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'cx, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>, move_data: &MoveData<'tcx>,
) -> ( ) -> (
RegionInferenceContext<'tcx>, RegionInferenceContext<'tcx>,

View File

@ -10,7 +10,7 @@
use dataflow::{FlowAtLocation, FlowsAtLocation}; use dataflow::{FlowAtLocation, FlowsAtLocation};
use borrow_check::nll::region_infer::Cause; use borrow_check::nll::region_infer::Cause;
use dataflow::MaybeInitializedLvals; use dataflow::MaybeInitializedPlaces;
use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::move_paths::{HasMoveData, MoveData};
use rustc::mir::{BasicBlock, Location, Mir}; use rustc::mir::{BasicBlock, Location, Mir};
use rustc::mir::Local; use rustc::mir::Local;
@ -34,7 +34,7 @@ pub(super) fn generate<'gcx, 'tcx>(
cx: &mut TypeChecker<'_, 'gcx, 'tcx>, cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>, mir: &Mir<'tcx>,
liveness: &LivenessResults, liveness: &LivenessResults,
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>, flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>, move_data: &MoveData<'tcx>,
) { ) {
let tcx = cx.tcx(); let tcx = cx.tcx();
@ -63,7 +63,7 @@ where
tcx: TyCtxt<'typeck, 'gcx, 'tcx>, tcx: TyCtxt<'typeck, 'gcx, 'tcx>,
mir: &'gen Mir<'tcx>, mir: &'gen Mir<'tcx>,
liveness: &'gen LivenessResults, liveness: &'gen LivenessResults,
flow_inits: &'gen mut FlowAtLocation<MaybeInitializedLvals<'flow, 'gcx, 'tcx>>, flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
move_data: &'gen MoveData<'tcx>, move_data: &'gen MoveData<'tcx>,
} }

View File

@ -15,7 +15,7 @@ use borrow_check::nll::region_infer::Cause;
use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::universal_regions::UniversalRegions;
use dataflow::FlowAtLocation; use dataflow::FlowAtLocation;
use dataflow::MaybeInitializedLvals; use dataflow::MaybeInitializedPlaces;
use dataflow::move_paths::MoveData; use dataflow::move_paths::MoveData;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
@ -100,7 +100,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
mir_def_id: DefId, mir_def_id: DefId,
universal_regions: &UniversalRegions<'tcx>, universal_regions: &UniversalRegions<'tcx>,
liveness: &LivenessResults, liveness: &LivenessResults,
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>, flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>, move_data: &MoveData<'tcx>,
) -> MirTypeckRegionConstraints<'tcx> { ) -> MirTypeckRegionConstraints<'tcx> {
let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap(); let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap();
@ -397,7 +397,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
let base_ty = base.to_ty(tcx); let base_ty = base.to_ty(tcx);
match *pi { match *pi {
ProjectionElem::Deref => { ProjectionElem::Deref => {
let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); let deref_ty = base_ty.builtin_deref(true);
PlaceTy::Ty { PlaceTy::Ty {
ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| { ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty) span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty)

View File

@ -575,10 +575,10 @@ impl<'a, 'b, 'tcx> FindPlaceUses<'a, 'b, 'tcx> {
/// has a reservation at the time). /// has a reservation at the time).
fn is_potential_use(context: PlaceContext) -> bool { fn is_potential_use(context: PlaceContext) -> bool {
match context { match context {
// storage effects on an place do not activate it // storage effects on a place do not activate it
PlaceContext::StorageLive | PlaceContext::StorageDead => false, PlaceContext::StorageLive | PlaceContext::StorageDead => false,
// validation effects do not activate an place // validation effects do not activate a place
// //
// FIXME: Should they? Is it just another read? Or can we // FIXME: Should they? Is it just another read? Or can we
// guarantee it won't dereference the stored address? How // guarantee it won't dereference the stored address? How
@ -589,11 +589,11 @@ impl<'a, 'b, 'tcx> FindPlaceUses<'a, 'b, 'tcx> {
// AsmOutput existed, but it's not necessarily a pure overwrite. // AsmOutput existed, but it's not necessarily a pure overwrite.
// so it's possible this should activate the place. // so it's possible this should activate the place.
PlaceContext::AsmOutput | PlaceContext::AsmOutput |
// pure overwrites of an place do not activate it. (note // pure overwrites of a place do not activate it. (note
// PlaceContext::Call is solely about dest place) // PlaceContext::Call is solely about dest place)
PlaceContext::Store | PlaceContext::Call => false, PlaceContext::Store | PlaceContext::Call => false,
// reads of an place *do* activate it // reads of a place *do* activate it
PlaceContext::Move | PlaceContext::Move |
PlaceContext::Copy | PlaceContext::Copy |
PlaceContext::Drop | PlaceContext::Drop |

View File

@ -36,7 +36,7 @@ pub use self::storage_liveness::*;
#[allow(dead_code)] #[allow(dead_code)]
pub(super) mod borrows; pub(super) mod borrows;
/// `MaybeInitializedLvals` tracks all l-values that might be /// `MaybeInitializedPlaces` tracks all places that might be
/// initialized upon reaching a particular point in the control flow /// initialized upon reaching a particular point in the control flow
/// for a function. /// for a function.
/// ///
@ -63,35 +63,35 @@ pub(super) mod borrows;
/// } /// }
/// ``` /// ```
/// ///
/// To determine whether an l-value *must* be initialized at a /// To determine whether a place *must* be initialized at a
/// particular control-flow point, one can take the set-difference /// particular control-flow point, one can take the set-difference
/// between this data and the data from `MaybeUninitializedLvals` at the /// between this data and the data from `MaybeUninitializedPlaces` at the
/// corresponding control-flow point. /// corresponding control-flow point.
/// ///
/// Similarly, at a given `drop` statement, the set-intersection /// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeUninitializedLvals` yields the set of /// between this data and `MaybeUninitializedPlaces` yields the set of
/// l-values that would require a dynamic drop-flag at that statement. /// places that would require a dynamic drop-flag at that statement.
pub struct MaybeInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { pub struct MaybeInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
} }
impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self -> Self
{ {
MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } MaybeInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
} }
} }
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
} }
/// `MaybeUninitializedLvals` tracks all l-values that might be /// `MaybeUninitializedPlaces` tracks all places that might be
/// uninitialized upon reaching a particular point in the control flow /// uninitialized upon reaching a particular point in the control flow
/// for a function. /// for a function.
/// ///
@ -118,42 +118,42 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx>
/// } /// }
/// ``` /// ```
/// ///
/// To determine whether an l-value *must* be uninitialized at a /// To determine whether a place *must* be uninitialized at a
/// particular control-flow point, one can take the set-difference /// particular control-flow point, one can take the set-difference
/// between this data and the data from `MaybeInitializedLvals` at the /// between this data and the data from `MaybeInitializedPlaces` at the
/// corresponding control-flow point. /// corresponding control-flow point.
/// ///
/// Similarly, at a given `drop` statement, the set-intersection /// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeInitializedLvals` yields the set of /// between this data and `MaybeInitializedPlaces` yields the set of
/// l-values that would require a dynamic drop-flag at that statement. /// places that would require a dynamic drop-flag at that statement.
pub struct MaybeUninitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { pub struct MaybeUninitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
} }
impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self -> Self
{ {
MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } MaybeUninitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
} }
} }
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
} }
/// `DefinitelyInitializedLvals` tracks all l-values that are definitely /// `DefinitelyInitializedPlaces` tracks all places that are definitely
/// initialized upon reaching a particular point in the control flow /// initialized upon reaching a particular point in the control flow
/// for a function. /// for a function.
/// ///
/// FIXME: Note that once flow-analysis is complete, this should be /// FIXME: Note that once flow-analysis is complete, this should be
/// the set-complement of MaybeUninitializedLvals; thus we can get rid /// the set-complement of MaybeUninitializedPlaces; thus we can get rid
/// of one or the other of these two. I'm inclined to get rid of /// of one or the other of these two. I'm inclined to get rid of
/// MaybeUninitializedLvals, simply because the sets will tend to be /// MaybeUninitializedPlaces, simply because the sets will tend to be
/// smaller in this analysis and thus easier for humans to process /// smaller in this analysis and thus easier for humans to process
/// when debugging. /// when debugging.
/// ///
@ -180,43 +180,43 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tc
/// } /// }
/// ``` /// ```
/// ///
/// To determine whether an l-value *may* be uninitialized at a /// To determine whether a place *may* be uninitialized at a
/// particular control-flow point, one can take the set-complement /// particular control-flow point, one can take the set-complement
/// of this data. /// of this data.
/// ///
/// Similarly, at a given `drop` statement, the set-difference between /// Similarly, at a given `drop` statement, the set-difference between
/// this data and `MaybeInitializedLvals` yields the set of l-values /// this data and `MaybeInitializedPlaces` yields the set of places
/// that would require a dynamic drop-flag at that statement. /// that would require a dynamic drop-flag at that statement.
pub struct DefinitelyInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { pub struct DefinitelyInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
} }
impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self -> Self
{ {
DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } DefinitelyInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
} }
} }
impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
} }
/// `MovingOutStatements` tracks the statements that perform moves out /// `MovingOutStatements` tracks the statements that perform moves out
/// of particular l-values. More precisely, it tracks whether the /// of particular places. More precisely, it tracks whether the
/// *effect* of such moves (namely, the uninitialization of the /// *effect* of such moves (namely, the uninitialization of the
/// l-value in question) can reach some point in the control-flow of /// place in question) can reach some point in the control-flow of
/// the function, or if that effect is "killed" by some intervening /// the function, or if that effect is "killed" by some intervening
/// operation reinitializing that l-value. /// operation reinitializing that place.
/// ///
/// The resulting dataflow is a more enriched version of /// The resulting dataflow is a more enriched version of
/// `MaybeUninitializedLvals`. Both structures on their own only tell /// `MaybeUninitializedPlaces`. Both structures on their own only tell
/// you if an l-value *might* be uninitialized at a given point in the /// you if a place *might* be uninitialized at a given point in the
/// control flow. But `MovingOutStatements` also includes the added /// control flow. But `MovingOutStatements` also includes the added
/// data of *which* particular statement causing the deinitialization /// data of *which* particular statement causing the deinitialization
/// that the borrow checker's error message may need to report. /// that the borrow checker's error message may need to report.
@ -241,7 +241,7 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
} }
/// `EverInitializedLvals` tracks all l-values that might have ever been /// `EverInitializedPlaces` tracks all places that might have ever been
/// initialized upon reaching a particular point in the control flow /// initialized upon reaching a particular point in the control flow
/// for a function, without an intervening `Storage Dead`. /// for a function, without an intervening `Storage Dead`.
/// ///
@ -270,28 +270,28 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> {
/// c = S; // {a, b, c, d } /// c = S; // {a, b, c, d }
/// } /// }
/// ``` /// ```
pub struct EverInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> { pub struct EverInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>, mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
} }
impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedPlaces<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>) mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self -> Self
{ {
EverInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe } EverInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
} }
} }
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
} }
impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex, fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
state: DropFlagState) state: DropFlagState)
{ {
@ -302,7 +302,7 @@ impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex, fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
state: DropFlagState) state: DropFlagState)
{ {
@ -313,7 +313,7 @@ impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex, fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
state: DropFlagState) state: DropFlagState)
{ {
@ -324,7 +324,7 @@ impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex; type Idx = MovePathIndex;
fn name() -> &'static str { "maybe_init" } fn name() -> &'static str { "maybe_init" }
fn bits_per_block(&self) -> usize { fn bits_per_block(&self) -> usize {
@ -375,7 +375,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex; type Idx = MovePathIndex;
fn name() -> &'static str { "maybe_uninit" } fn name() -> &'static str { "maybe_uninit" }
fn bits_per_block(&self) -> usize { fn bits_per_block(&self) -> usize {
@ -430,7 +430,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex; type Idx = MovePathIndex;
fn name() -> &'static str { "definite_init" } fn name() -> &'static str { "definite_init" }
fn bits_per_block(&self) -> usize { fn bits_per_block(&self) -> usize {
@ -561,7 +561,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = InitIndex; type Idx = InitIndex;
fn name() -> &'static str { "ever_init" } fn name() -> &'static str { "ever_init" }
fn bits_per_block(&self) -> usize { fn bits_per_block(&self) -> usize {
@ -657,21 +657,21 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
#[inline] #[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize { fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // "maybe" means we union effects of both preds pred1 | pred2 // "maybe" means we union effects of both preds
} }
} }
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
#[inline] #[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize { fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // "maybe" means we union effects of both preds pred1 | pred2 // "maybe" means we union effects of both preds
} }
} }
impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
#[inline] #[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize { fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 & pred2 // "definitely" means we intersect effects of both preds pred1 & pred2 // "definitely" means we intersect effects of both preds
@ -685,7 +685,7 @@ impl<'a, 'gcx, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedPlaces<'a, 'gcx, 'tcx> {
#[inline] #[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize { fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // inits from both preds are in scope pred1 | pred2 // inits from both preds are in scope
@ -702,21 +702,21 @@ impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedLvals<'a, 'gcx, 'tcx> {
// propagating, or you start at all-ones and then use Intersect as // propagating, or you start at all-ones and then use Intersect as
// your merge when propagating. // your merge when propagating.
impl<'a, 'gcx, 'tcx> InitialFlow for MaybeInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> InitialFlow for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
#[inline] #[inline]
fn bottom_value() -> bool { fn bottom_value() -> bool {
false // bottom = uninitialized false // bottom = uninitialized
} }
} }
impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
#[inline] #[inline]
fn bottom_value() -> bool { fn bottom_value() -> bool {
false // bottom = initialized (start_block_effect counters this at outset) false // bottom = initialized (start_block_effect counters this at outset)
} }
} }
impl<'a, 'gcx, 'tcx> InitialFlow for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> InitialFlow for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
#[inline] #[inline]
fn bottom_value() -> bool { fn bottom_value() -> bool {
true // bottom = initialized (start_block_effect counters this at outset) true // bottom = initialized (start_block_effect counters this at outset)
@ -730,7 +730,7 @@ impl<'a, 'gcx, 'tcx> InitialFlow for MovingOutStatements<'a, 'gcx, 'tcx> {
} }
} }
impl<'a, 'gcx, 'tcx> InitialFlow for EverInitializedLvals<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> InitialFlow for EverInitializedPlaces<'a, 'gcx, 'tcx> {
#[inline] #[inline]
fn bottom_value() -> bool { fn bottom_value() -> bool {
false // bottom = no initialized variables by default false // bottom = no initialized variables by default

View File

@ -26,9 +26,9 @@ use std::path::PathBuf;
use std::usize; use std::usize;
pub use self::impls::{MaybeStorageLive}; pub use self::impls::{MaybeStorageLive};
pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals}; pub use self::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements}; pub use self::impls::{DefinitelyInitializedPlaces, MovingOutStatements};
pub use self::impls::EverInitializedLvals; pub use self::impls::EverInitializedPlaces;
pub use self::impls::borrows::{Borrows, BorrowData}; pub use self::impls::borrows::{Borrows, BorrowData};
pub(crate) use self::impls::borrows::{ActiveBorrows, Reservations, ReserveOrActivateIndex}; pub(crate) use self::impls::borrows::{ActiveBorrows, Reservations, ReserveOrActivateIndex};
pub use self::at_location::{FlowAtLocation, FlowsAtLocation}; pub use self::at_location::{FlowAtLocation, FlowsAtLocation};

View File

@ -86,7 +86,7 @@ impl MoveOutIndex {
/// It follows a tree structure. /// It follows a tree structure.
/// ///
/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;` /// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
/// move *out* of the l-value `x.m`. /// move *out* of the place `x.m`.
/// ///
/// The MovePaths representing `x.m` and `x.n` are siblings (that is, /// The MovePaths representing `x.m` and `x.n` are siblings (that is,
/// one of them will link to the other via the `next_sibling` field, /// one of them will link to the other via the `next_sibling` field,
@ -222,7 +222,7 @@ impl fmt::Debug for Init {
} }
} }
/// Tables mapping from an l-value to its MovePathIndex. /// Tables mapping from a place to its MovePathIndex.
#[derive(Debug)] #[derive(Debug)]
pub struct MovePathLookup<'tcx> { pub struct MovePathLookup<'tcx> {
locals: IndexVec<Local, MovePathIndex>, locals: IndexVec<Local, MovePathIndex>,
@ -247,7 +247,7 @@ pub enum LookupResult {
impl<'tcx> MovePathLookup<'tcx> { impl<'tcx> MovePathLookup<'tcx> {
// Unlike the builder `fn move_path_for` below, this lookup // Unlike the builder `fn move_path_for` below, this lookup
// alternative will *not* create a MovePath on the fly for an // alternative will *not* create a MovePath on the fly for an
// unknown l-value, but will rather return the nearest available // unknown place, but will rather return the nearest available
// parent. // parent.
pub fn find(&self, place: &Place<'tcx>) -> LookupResult { pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
match *place { match *place {

View File

@ -9,9 +9,9 @@ use interpret::memory::HasMemory;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum Place { pub enum Place {
/// An place referring to a value allocated in the `Memory` system. /// A place referring to a value allocated in the `Memory` system.
Ptr { Ptr {
/// An place may have an invalid (integral or undef) pointer, /// A place may have an invalid (integral or undef) pointer,
/// since it might be turned back into a reference /// since it might be turned back into a reference
/// before ever being dereferenced. /// before ever being dereferenced.
ptr: Pointer, ptr: Pointer,
@ -19,7 +19,7 @@ pub enum Place {
extra: PlaceExtra, extra: PlaceExtra,
}, },
/// An place referring to a value on the stack. Represented by a stack frame index paired with /// A place referring to a value on the stack. Represented by a stack frame index paired with
/// a Mir local index. /// a Mir local index.
Local { frame: usize, local: mir::Local }, Local { frame: usize, local: mir::Local },
} }
@ -33,7 +33,7 @@ pub enum PlaceExtra {
} }
impl<'tcx> Place { impl<'tcx> Place {
/// Produces an Place that will error if attempted to be read from /// Produces a Place that will error if attempted to be read from
pub fn undef() -> Self { pub fn undef() -> Self {
Self::from_primval_ptr(PrimVal::Undef.into(), Align::from_bytes(1, 1).unwrap()) Self::from_primval_ptr(PrimVal::Undef.into(), Align::from_bytes(1, 1).unwrap())
} }

View File

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult}; use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult};
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use dataflow::{DataflowResults}; use dataflow::{DataflowResults};
use dataflow::{on_all_children_bits, on_all_drop_children_bits}; use dataflow::{on_all_children_bits, on_all_drop_children_bits};
use dataflow::{drop_flag_effects_for_location, on_lookup_result_bits}; use dataflow::{drop_flag_effects_for_location, on_lookup_result_bits};
@ -60,11 +60,11 @@ impl MirPass for ElaborateDrops {
let dead_unwinds = find_dead_unwinds(tcx, mir, id, &env); let dead_unwinds = find_dead_unwinds(tcx, mir, id, &env);
let flow_inits = let flow_inits =
do_dataflow(tcx, mir, id, &[], &dead_unwinds, do_dataflow(tcx, mir, id, &[], &dead_unwinds,
MaybeInitializedLvals::new(tcx, mir, &env), MaybeInitializedPlaces::new(tcx, mir, &env),
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p])); |bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
let flow_uninits = let flow_uninits =
do_dataflow(tcx, mir, id, &[], &dead_unwinds, do_dataflow(tcx, mir, id, &[], &dead_unwinds,
MaybeUninitializedLvals::new(tcx, mir, &env), MaybeUninitializedPlaces::new(tcx, mir, &env),
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p])); |bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
ElaborateDropsCtxt { ElaborateDropsCtxt {
@ -97,7 +97,7 @@ fn find_dead_unwinds<'a, 'tcx>(
let mut dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let mut dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let flow_inits = let flow_inits =
do_dataflow(tcx, mir, id, &[], &dead_unwinds, do_dataflow(tcx, mir, id, &[], &dead_unwinds,
MaybeInitializedLvals::new(tcx, mir, &env), MaybeInitializedPlaces::new(tcx, mir, &env),
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p])); |bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
for (bb, bb_data) in mir.basic_blocks().iter_enumerated() { for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
let location = match bb_data.terminator().kind { let location = match bb_data.terminator().kind {
@ -300,8 +300,8 @@ struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
env: &'a MoveDataParamEnv<'tcx, 'tcx>, env: &'a MoveDataParamEnv<'tcx, 'tcx>,
flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx, 'tcx>>, flow_inits: DataflowResults<MaybeInitializedPlaces<'a, 'tcx, 'tcx>>,
flow_uninits: DataflowResults<MaybeUninitializedLvals<'a, 'tcx, 'tcx>>, flow_uninits: DataflowResults<MaybeUninitializedPlaces<'a, 'tcx, 'tcx>>,
drop_flags: FxHashMap<MovePathIndex, Local>, drop_flags: FxHashMap<MovePathIndex, Local>,
patch: MirPatch<'tcx>, patch: MirPatch<'tcx>,
} }

View File

@ -22,7 +22,7 @@ use dataflow::{do_dataflow, DebugFormatted};
use dataflow::MoveDataParamEnv; use dataflow::MoveDataParamEnv;
use dataflow::BitDenotation; use dataflow::BitDenotation;
use dataflow::DataflowResults; use dataflow::DataflowResults;
use dataflow::{DefinitelyInitializedLvals, MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces};
use dataflow::move_paths::{MovePathIndex, LookupResult}; use dataflow::move_paths::{MovePathIndex, LookupResult};
use dataflow::move_paths::{HasMoveData, MoveData}; use dataflow::move_paths::{HasMoveData, MoveData};
use dataflow; use dataflow;
@ -50,15 +50,15 @@ impl MirPass for SanityCheck {
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let flow_inits = let flow_inits =
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
MaybeInitializedLvals::new(tcx, mir, &mdpe), MaybeInitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i])); |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
let flow_uninits = let flow_uninits =
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
MaybeUninitializedLvals::new(tcx, mir, &mdpe), MaybeUninitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i])); |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
let flow_def_inits = let flow_def_inits =
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
DefinitelyInitializedLvals::new(tcx, mir, &mdpe), DefinitelyInitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i])); |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
if has_rustc_mir_with(&attributes, "rustc_peek_maybe_init").is_some() { if has_rustc_mir_with(&attributes, "rustc_peek_maybe_init").is_some() {

View File

@ -560,7 +560,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
/// ptr = cur /// ptr = cur
/// cur = cur.offset(1) /// cur = cur.offset(1)
/// } else { /// } else {
/// ptr = &mut LV[cur] /// ptr = &mut P[cur]
/// cur = cur + 1 /// cur = cur + 1
/// } /// }
/// drop(ptr) /// drop(ptr)
@ -731,7 +731,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
if ptr_based { if ptr_based {
let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place)); let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place));
let tmp = Place::Local(self.new_temp(tmp_ty)); let tmp = Place::Local(self.new_temp(tmp_ty));
// tmp = &LV; // tmp = &P;
// cur = tmp as *mut T; // cur = tmp as *mut T;
// end = Offset(cur, len); // end = Offset(cur, len);
drop_block_stmts.push(self.assign(&tmp, Rvalue::Ref( drop_block_stmts.push(self.assign(&tmp, Rvalue::Ref(

View File

@ -545,7 +545,7 @@ impl<'a, 'tcx> ArgType<'tcx> {
self.mode == PassMode::Ignore self.mode == PassMode::Ignore
} }
/// Get the LLVM type for an place of the original Rust type of /// Get the LLVM type for a place of the original Rust type of
/// this argument/return, i.e. the result of `type_of::type_of`. /// this argument/return, i.e. the result of `type_of::type_of`.
pub fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { pub fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type {
self.layout.llvm_type(cx) self.layout.llvm_type(cx)
@ -674,7 +674,7 @@ impl<'a, 'tcx> FnType<'tcx> {
_ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg) _ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg)
} }
let pointee = self_arg.layout.ty.builtin_deref(true, ty::NoPreference) let pointee = self_arg.layout.ty.builtin_deref(true)
.unwrap_or_else(|| { .unwrap_or_else(|| {
bug!("FnType::new_vtable: non-pointer self {:?}", self_arg) bug!("FnType::new_vtable: non-pointer self {:?}", self_arg)
}).ty; }).ty;

View File

@ -212,7 +212,7 @@ enum Base {
Static(ValueRef) Static(ValueRef)
} }
/// An place as seen from a constant. /// A place as seen from a constant.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct ConstPlace<'tcx> { struct ConstPlace<'tcx> {
base: Base, base: Base,
@ -743,7 +743,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
operand.llval operand.llval
} }
mir::CastKind::Unsize => { mir::CastKind::Unsize => {
let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference) let pointee_ty = operand.ty.builtin_deref(true)
.expect("consts: unsizing got non-pointer type").ty; .expect("consts: unsizing got non-pointer type").ty;
let (base, old_info) = if !self.cx.type_is_sized(pointee_ty) { let (base, old_info) = if !self.cx.type_is_sized(pointee_ty) {
// Normally, the source is a thin pointer and we are // Normally, the source is a thin pointer and we are
@ -758,7 +758,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
(operand.llval, None) (operand.llval, None)
}; };
let unsized_ty = cast_ty.builtin_deref(true, ty::NoPreference) let unsized_ty = cast_ty.builtin_deref(true)
.expect("consts: unsizing got non-pointer target type").ty; .expect("consts: unsizing got non-pointer target type").ty;
let ptr_ty = self.cx.layout_of(unsized_ty).llvm_type(self.cx).ptr_to(); let ptr_ty = self.cx.layout_of(unsized_ty).llvm_type(self.cx).ptr_to();
let base = consts::ptrcast(base, ptr_ty); let base = consts::ptrcast(base, ptr_ty);

View File

@ -9,7 +9,6 @@
// except according to those terms. // except according to those terms.
use llvm::ValueRef; use llvm::ValueRef;
use rustc::ty;
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
use rustc::mir; use rustc::mir;
use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::Idx;
@ -100,7 +99,7 @@ impl<'a, 'tcx> OperandRef<'tcx> {
} }
pub fn deref(self, cx: &CodegenCx<'a, 'tcx>) -> PlaceRef<'tcx> { pub fn deref(self, cx: &CodegenCx<'a, 'tcx>) -> PlaceRef<'tcx> {
let projected_ty = self.layout.ty.builtin_deref(true, ty::NoPreference) let projected_ty = self.layout.ty.builtin_deref(true)
.unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty; .unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty;
let (llptr, llextra) = match self.val { let (llptr, llextra) = match self.val {
OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()),

View File

@ -14,8 +14,8 @@ use rustc::hir::pat_util::EnumerateAndAdjustIterator;
use rustc::infer; use rustc::infer;
use rustc::infer::type_variable::TypeVariableOrigin; use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::traits::ObligationCauseCode; use rustc::traits::ObligationCauseCode;
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference}; use rustc::ty::{self, Ty, TypeFoldable};
use check::{FnCtxt, Expectation, Diverges}; use check::{FnCtxt, Expectation, Diverges, Needs};
use check::coercion::CoerceMany; use check::coercion::CoerceMany;
use util::nodemap::FxHashMap; use util::nodemap::FxHashMap;
@ -500,7 +500,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool { pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool {
if let PatKind::Binding(..) = inner.node { if let PatKind::Binding(..) = inner.node {
if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true, ty::NoPreference) { if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) {
if let ty::TyDynamic(..) = mt.ty.sty { if let ty::TyDynamic(..) = mt.ty.sty {
// This is "x = SomeTrait" being reduced from // This is "x = SomeTrait" being reduced from
// "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error. // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
@ -584,7 +584,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}); });
let discrim_ty; let discrim_ty;
if let Some(m) = contains_ref_bindings { if let Some(m) = contains_ref_bindings {
discrim_ty = self.check_expr_with_lvalue_pref(discrim, LvaluePreference::from_mutbl(m)); discrim_ty = self.check_expr_with_needs(discrim, Needs::maybe_mut_place(m));
} else { } else {
// ...but otherwise we want to use any supertype of the // ...but otherwise we want to use any supertype of the
// discriminant. This is sort of a workaround, see note (*) in // discriminant. This is sort of a workaround, see note (*) in

View File

@ -10,7 +10,7 @@
use astconv::AstConv; use astconv::AstConv;
use super::{FnCtxt, LvalueOp}; use super::{FnCtxt, PlaceOp, Needs};
use super::method::MethodCallee; use super::method::MethodCallee;
use rustc::infer::InferOk; use rustc::infer::InferOk;
@ -18,7 +18,6 @@ use rustc::session::DiagnosticMessageId;
use rustc::traits; use rustc::traits;
use rustc::ty::{self, Ty, TraitRef}; use rustc::ty::{self, Ty, TraitRef};
use rustc::ty::{ToPredicate, TypeFoldable}; use rustc::ty::{ToPredicate, TypeFoldable};
use rustc::ty::{LvaluePreference, NoPreference};
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref}; use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
use syntax_pos::Span; use syntax_pos::Span;
@ -85,7 +84,7 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
// Otherwise, deref if type is derefable: // Otherwise, deref if type is derefable:
let (kind, new_ty) = let (kind, new_ty) =
if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers, NoPreference) { if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers) {
(AutoderefKind::Builtin, mt.ty) (AutoderefKind::Builtin, mt.ty)
} else { } else {
let ty = self.overloaded_deref_ty(self.cur_ty)?; let ty = self.overloaded_deref_ty(self.cur_ty)?;
@ -163,19 +162,19 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
} }
/// Returns the adjustment steps. /// Returns the adjustment steps.
pub fn adjust_steps(&self, pref: LvaluePreference) pub fn adjust_steps(&self, needs: Needs)
-> Vec<Adjustment<'tcx>> { -> Vec<Adjustment<'tcx>> {
self.fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(pref)) self.fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(needs))
} }
pub fn adjust_steps_as_infer_ok(&self, pref: LvaluePreference) pub fn adjust_steps_as_infer_ok(&self, needs: Needs)
-> InferOk<'tcx, Vec<Adjustment<'tcx>>> { -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
let mut obligations = vec![]; let mut obligations = vec![];
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty) let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty)
.chain(iter::once(self.cur_ty)); .chain(iter::once(self.cur_ty));
let steps: Vec<_> = self.steps.iter().map(|&(source, kind)| { let steps: Vec<_> = self.steps.iter().map(|&(source, kind)| {
if let AutoderefKind::Overloaded = kind { if let AutoderefKind::Overloaded = kind {
self.fcx.try_overloaded_deref(self.span, source, pref) self.fcx.try_overloaded_deref(self.span, source, needs)
.and_then(|InferOk { value: method, obligations: o }| { .and_then(|InferOk { value: method, obligations: o }| {
obligations.extend(o); obligations.extend(o);
if let ty::TyRef(region, mt) = method.sig.output().sty { if let ty::TyRef(region, mt) = method.sig.output().sty {
@ -238,8 +237,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn try_overloaded_deref(&self, pub fn try_overloaded_deref(&self,
span: Span, span: Span,
base_ty: Ty<'tcx>, base_ty: Ty<'tcx>,
pref: LvaluePreference) needs: Needs)
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> { -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
self.try_overloaded_lvalue_op(span, base_ty, &[], pref, LvalueOp::Deref) self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref)
} }
} }

View File

@ -8,14 +8,14 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
use super::autoderef::Autoderef; use super::autoderef::Autoderef;
use super::method::MethodCallee; use super::method::MethodCallee;
use hir::def::Def; use hir::def::Def;
use hir::def_id::{DefId, LOCAL_CRATE}; use hir::def_id::{DefId, LOCAL_CRATE};
use rustc::{infer, traits}; use rustc::{infer, traits};
use rustc::ty::{self, TyCtxt, TypeFoldable, LvaluePreference, Ty}; use rustc::ty::{self, TyCtxt, TypeFoldable, Ty};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
use syntax::abi; use syntax::abi;
use syntax::symbol::Symbol; use syntax::symbol::Symbol;
@ -96,7 +96,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// If the callee is a bare function or a closure, then we're all set. // If the callee is a bare function or a closure, then we're all set.
match adjusted_ty.sty { match adjusted_ty.sty {
ty::TyFnDef(..) | ty::TyFnPtr(_) => { ty::TyFnDef(..) | ty::TyFnPtr(_) => {
let adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference); let adjustments = autoderef.adjust_steps(Needs::None);
self.apply_adjustments(callee_expr, adjustments); self.apply_adjustments(callee_expr, adjustments);
return Some(CallStep::Builtin(adjusted_ty)); return Some(CallStep::Builtin(adjusted_ty));
} }
@ -113,7 +113,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
infer::FnCall, infer::FnCall,
&closure_ty) &closure_ty)
.0; .0;
let adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference); let adjustments = autoderef.adjust_steps(Needs::None);
self.record_deferred_call_resolution(def_id, DeferredCallResolution { self.record_deferred_call_resolution(def_id, DeferredCallResolution {
call_expr, call_expr,
callee_expr, callee_expr,
@ -143,7 +143,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| { self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| {
let mut adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference); let mut adjustments = autoderef.adjust_steps(Needs::None);
adjustments.extend(autoref); adjustments.extend(autoref);
self.apply_adjustments(callee_expr, adjustments); self.apply_adjustments(callee_expr, adjustments);
CallStep::Overloaded(method) CallStep::Overloaded(method)

View File

@ -60,7 +60,7 @@
//! sort of a minor point so I've opted to leave it for later---after all //! sort of a minor point so I've opted to leave it for later---after all
//! we may want to adjust precisely when coercions occur. //! we may want to adjust precisely when coercions occur.
use check::{Diverges, FnCtxt}; use check::{Diverges, FnCtxt, Needs};
use rustc::hir; use rustc::hir;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
@ -69,8 +69,7 @@ use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::lint; use rustc::lint;
use rustc::traits::{self, ObligationCause, ObligationCauseCode}; use rustc::traits::{self, ObligationCause, ObligationCauseCode};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
use rustc::ty::{self, LvaluePreference, TypeAndMut, use rustc::ty::{self, TypeAndMut, Ty, ClosureSubsts};
Ty, ClosureSubsts};
use rustc::ty::fold::TypeFoldable; use rustc::ty::fold::TypeFoldable;
use rustc::ty::error::TypeError; use rustc::ty::error::TypeError;
use rustc::ty::relate::RelateResult; use rustc::ty::relate::RelateResult;
@ -410,9 +409,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
return success(vec![], ty, obligations); return success(vec![], ty, obligations);
} }
let pref = LvaluePreference::from_mutbl(mt_b.mutbl); let needs = Needs::maybe_mut_place(mt_b.mutbl);
let InferOk { value: mut adjustments, obligations: o } let InferOk { value: mut adjustments, obligations: o }
= autoderef.adjust_steps_as_infer_ok(pref); = autoderef.adjust_steps_as_infer_ok(needs);
obligations.extend(o); obligations.extend(o);
obligations.extend(autoderef.into_obligations()); obligations.extend(autoderef.into_obligations());

View File

@ -11,11 +11,11 @@
use super::{probe, MethodCallee}; use super::{probe, MethodCallee};
use astconv::AstConv; use astconv::AstConv;
use check::{FnCtxt, LvalueOp, callee}; use check::{FnCtxt, PlaceOp, callee, Needs};
use hir::def_id::DefId; use hir::def_id::DefId;
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use rustc::traits; use rustc::traits;
use rustc::ty::{self, LvaluePreference, NoPreference, PreferMutLvalue, Ty}; use rustc::ty::{self, Ty};
use rustc::ty::subst::Subst; use rustc::ty::subst::Subst;
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, OverloadedDeref}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, OverloadedDeref};
use rustc::ty::fold::TypeFoldable; use rustc::ty::fold::TypeFoldable;
@ -136,7 +136,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
}; };
if let Some(hir::MutMutable) = pick.autoref { if let Some(hir::MutMutable) = pick.autoref {
self.convert_lvalue_derefs_to_mutable(); self.convert_place_derefs_to_mutable();
} }
ConfirmResult { callee, illegal_sized_bound } ConfirmResult { callee, illegal_sized_bound }
@ -155,7 +155,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
let (_, n) = autoderef.nth(pick.autoderefs).unwrap(); let (_, n) = autoderef.nth(pick.autoderefs).unwrap();
assert_eq!(n, pick.autoderefs); assert_eq!(n, pick.autoderefs);
let mut adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference); let mut adjustments = autoderef.adjust_steps(Needs::None);
let mut target = autoderef.unambiguous_final_ty(); let mut target = autoderef.unambiguous_final_ty();
@ -416,7 +416,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
/// When we select a method with a mutable autoref, we have to go convert any /// When we select a method with a mutable autoref, we have to go convert any
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut` /// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
/// respectively. /// respectively.
fn convert_lvalue_derefs_to_mutable(&self) { fn convert_place_derefs_to_mutable(&self) {
// Gather up expressions we want to munge. // Gather up expressions we want to munge.
let mut exprs = Vec::new(); let mut exprs = Vec::new();
exprs.push(self.self_expr); exprs.push(self.self_expr);
@ -431,14 +431,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
} }
} }
debug!("convert_lvalue_derefs_to_mutable: exprs={:?}", exprs); debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
// Fix up autoderefs and derefs. // Fix up autoderefs and derefs.
for (i, &expr) in exprs.iter().rev().enumerate() { for (i, &expr) in exprs.iter().rev().enumerate() {
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?}", i, expr); debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
// Fix up the autoderefs. Autorefs can only occur immediately preceding // Fix up the autoderefs. Autorefs can only occur immediately preceding
// overloaded lvalue ops, and will be fixed by them in order to get // overloaded place ops, and will be fixed by them in order to get
// the correct region. // the correct region.
let mut source = self.node_ty(expr.hir_id); let mut source = self.node_ty(expr.hir_id);
// Do not mutate adjustments in place, but rather take them, // Do not mutate adjustments in place, but rather take them,
@ -449,10 +449,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
.adjustments_mut() .adjustments_mut()
.remove(expr.hir_id); .remove(expr.hir_id);
if let Some(mut adjustments) = previous_adjustments { if let Some(mut adjustments) = previous_adjustments {
let pref = LvaluePreference::PreferMutLvalue; let needs = Needs::MutPlace;
for adjustment in &mut adjustments { for adjustment in &mut adjustments {
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
if let Some(ok) = self.try_overloaded_deref(expr.span, source, pref) { if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) {
let method = self.register_infer_ok_obligations(ok); let method = self.register_infer_ok_obligations(ok);
if let ty::TyRef(region, mt) = method.sig.output().sty { if let ty::TyRef(region, mt) = method.sig.output().sty {
*deref = OverloadedDeref { *deref = OverloadedDeref {
@ -470,28 +470,28 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
match expr.node { match expr.node {
hir::ExprIndex(ref base_expr, ref index_expr) => { hir::ExprIndex(ref base_expr, ref index_expr) => {
let index_expr_ty = self.node_ty(index_expr.hir_id); let index_expr_ty = self.node_ty(index_expr.hir_id);
self.convert_lvalue_op_to_mutable( self.convert_place_op_to_mutable(
LvalueOp::Index, expr, base_expr, &[index_expr_ty]); PlaceOp::Index, expr, base_expr, &[index_expr_ty]);
} }
hir::ExprUnary(hir::UnDeref, ref base_expr) => { hir::ExprUnary(hir::UnDeref, ref base_expr) => {
self.convert_lvalue_op_to_mutable( self.convert_place_op_to_mutable(
LvalueOp::Deref, expr, base_expr, &[]); PlaceOp::Deref, expr, base_expr, &[]);
} }
_ => {} _ => {}
} }
} }
} }
fn convert_lvalue_op_to_mutable(&self, fn convert_place_op_to_mutable(&self,
op: LvalueOp, op: PlaceOp,
expr: &hir::Expr, expr: &hir::Expr,
base_expr: &hir::Expr, base_expr: &hir::Expr,
arg_tys: &[Ty<'tcx>]) arg_tys: &[Ty<'tcx>])
{ {
debug!("convert_lvalue_op_to_mutable({:?}, {:?}, {:?}, {:?})", debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})",
op, expr, base_expr, arg_tys); op, expr, base_expr, arg_tys);
if !self.tables.borrow().is_method_call(expr) { if !self.tables.borrow().is_method_call(expr) {
debug!("convert_lvalue_op_to_mutable - builtin, nothing to do"); debug!("convert_place_op_to_mutable - builtin, nothing to do");
return return
} }
@ -499,24 +499,24 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
.map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target); .map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target);
let base_ty = self.resolve_type_vars_if_possible(&base_ty); let base_ty = self.resolve_type_vars_if_possible(&base_ty);
// Need to deref because overloaded lvalue ops take self by-reference. // Need to deref because overloaded place ops take self by-reference.
let base_ty = base_ty.builtin_deref(false, NoPreference) let base_ty = base_ty.builtin_deref(false)
.expect("lvalue op takes something that is not a ref") .expect("place op takes something that is not a ref")
.ty; .ty;
let method = self.try_overloaded_lvalue_op( let method = self.try_overloaded_place_op(
expr.span, base_ty, arg_tys, PreferMutLvalue, op); expr.span, base_ty, arg_tys, Needs::MutPlace, op);
let method = match method { let method = match method {
Some(ok) => self.register_infer_ok_obligations(ok), Some(ok) => self.register_infer_ok_obligations(ok),
None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed") None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed")
}; };
debug!("convert_lvalue_op_to_mutable: method={:?}", method); debug!("convert_place_op_to_mutable: method={:?}", method);
self.write_method_call(expr.hir_id, method); self.write_method_call(expr.hir_id, method);
let (region, mutbl) = if let ty::TyRef(r, mt) = method.sig.inputs()[0].sty { let (region, mutbl) = if let ty::TyRef(r, mt) = method.sig.inputs()[0].sty {
(r, mt.mutbl) (r, mt.mutbl)
} else { } else {
span_bug!(expr.span, "input to lvalue op is not a ref?"); span_bug!(expr.span, "input to place op is not a ref?");
}; };
// Convert the autoref in the base expr to mutable with the correct // Convert the autoref in the base expr to mutable with the correct
@ -529,7 +529,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
let mut source = base_expr_ty; let mut source = base_expr_ty;
for adjustment in &mut adjustments[..] { for adjustment in &mut adjustments[..] {
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
debug!("convert_lvalue_op_to_mutable: converting autoref {:?}", adjustment); debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
adjustment.target = self.tcx.mk_ref(region, ty::TypeAndMut { adjustment.target = self.tcx.mk_ref(region, ty::TypeAndMut {
ty: source, ty: source,

View File

@ -95,7 +95,6 @@ use rustc::infer::type_variable::{TypeVariableOrigin};
use rustc::middle::region; use rustc::middle::region;
use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode}; use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate}; use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate};
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc::ty::fold::TypeFoldable; use rustc::ty::fold::TypeFoldable;
@ -368,6 +367,21 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
} }
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Needs {
MutPlace,
None
}
impl Needs {
fn maybe_mut_place(m: hir::Mutability) -> Self {
match m {
hir::MutMutable => Needs::MutPlace,
hir::MutImmutable => Needs::None,
}
}
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct UnsafetyState { pub struct UnsafetyState {
pub def: ast::NodeId, pub def: ast::NodeId,
@ -410,7 +424,7 @@ impl UnsafetyState {
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum LvalueOp { pub enum PlaceOp {
Deref, Deref,
Index Index
} }
@ -543,7 +557,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
/// foo();}` or `{return; 22}`, where we would warn on the /// foo();}` or `{return; 22}`, where we would warn on the
/// `foo()` or `22`. /// `foo()` or `22`.
/// ///
/// - To permit assignment into a local variable or other lvalue /// - To permit assignment into a local variable or other place
/// (including the "return slot") of type `!`. This is allowed /// (including the "return slot") of type `!`. This is allowed
/// if **either** the type of value being assigned is `!`, which /// if **either** the type of value being assigned is `!`, which
/// means the current code is dead, **or** the expression's /// means the current code is dead, **or** the expression's
@ -2207,11 +2221,65 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
} }
/// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait fn is_place_expr(&self, expr: &hir::Expr) -> bool {
match expr.node {
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
match path.def {
Def::Local(..) | Def::Upvar(..) | Def::Static(..) | Def::Err => true,
_ => false,
}
}
hir::ExprType(ref e, _) => {
self.is_place_expr(e)
}
hir::ExprUnary(hir::UnDeref, _) |
hir::ExprField(..) |
hir::ExprTupField(..) |
hir::ExprIndex(..) => {
true
}
// Partially qualified paths in expressions can only legally
// refer to associated items which are always rvalues.
hir::ExprPath(hir::QPath::TypeRelative(..)) |
hir::ExprCall(..) |
hir::ExprMethodCall(..) |
hir::ExprStruct(..) |
hir::ExprTup(..) |
hir::ExprIf(..) |
hir::ExprMatch(..) |
hir::ExprClosure(..) |
hir::ExprBlock(..) |
hir::ExprRepeat(..) |
hir::ExprArray(..) |
hir::ExprBreak(..) |
hir::ExprAgain(..) |
hir::ExprRet(..) |
hir::ExprWhile(..) |
hir::ExprLoop(..) |
hir::ExprAssign(..) |
hir::ExprInlineAsm(..) |
hir::ExprAssignOp(..) |
hir::ExprLit(_) |
hir::ExprUnary(..) |
hir::ExprBox(..) |
hir::ExprAddrOf(..) |
hir::ExprBinary(..) |
hir::ExprYield(..) |
hir::ExprCast(..) => {
false
}
}
}
/// For the overloaded place expressions (`*x`, `x[3]`), the trait
/// returns a type of `&T`, but the actual type we assign to the /// returns a type of `&T`, but the actual type we assign to the
/// *expression* is `T`. So this function just peels off the return /// *expression* is `T`. So this function just peels off the return
/// type by one layer to yield `T`. /// type by one layer to yield `T`.
fn make_overloaded_lvalue_return_type(&self, fn make_overloaded_place_return_type(&self,
method: MethodCallee<'tcx>) method: MethodCallee<'tcx>)
-> ty::TypeAndMut<'tcx> -> ty::TypeAndMut<'tcx>
{ {
@ -2219,7 +2287,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let ret_ty = method.sig.output(); let ret_ty = method.sig.output();
// method returns &T, but the type as visible to user is T, so deref // method returns &T, but the type as visible to user is T, so deref
ret_ty.builtin_deref(true, NoPreference).unwrap() ret_ty.builtin_deref(true).unwrap()
} }
fn lookup_indexing(&self, fn lookup_indexing(&self,
@ -2227,7 +2295,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
base_expr: &'gcx hir::Expr, base_expr: &'gcx hir::Expr,
base_ty: Ty<'tcx>, base_ty: Ty<'tcx>,
idx_ty: Ty<'tcx>, idx_ty: Ty<'tcx>,
lvalue_pref: LvaluePreference) needs: Needs)
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
{ {
// FIXME(#18741) -- this is almost but not quite the same as the // FIXME(#18741) -- this is almost but not quite the same as the
@ -2237,7 +2305,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mut autoderef = self.autoderef(base_expr.span, base_ty); let mut autoderef = self.autoderef(base_expr.span, base_ty);
let mut result = None; let mut result = None;
while result.is_none() && autoderef.next().is_some() { while result.is_none() && autoderef.next().is_some() {
result = self.try_index_step(expr, base_expr, &autoderef, lvalue_pref, idx_ty); result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty);
} }
autoderef.finalize(); autoderef.finalize();
result result
@ -2252,7 +2320,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr: &hir::Expr, expr: &hir::Expr,
base_expr: &hir::Expr, base_expr: &hir::Expr,
autoderef: &Autoderef<'a, 'gcx, 'tcx>, autoderef: &Autoderef<'a, 'gcx, 'tcx>,
lvalue_pref: LvaluePreference, needs: Needs,
index_ty: Ty<'tcx>) index_ty: Ty<'tcx>)
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
{ {
@ -2279,14 +2347,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// type from the method signature. // type from the method signature.
// If some lookup succeeded, install method in table // If some lookup succeeded, install method in table
let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_expr.span)); let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_expr.span));
let method = self.try_overloaded_lvalue_op( let method = self.try_overloaded_place_op(
expr.span, self_ty, &[input_ty], lvalue_pref, LvalueOp::Index); expr.span, self_ty, &[input_ty], needs, PlaceOp::Index);
let result = method.map(|ok| { let result = method.map(|ok| {
debug!("try_index_step: success, using overloaded indexing"); debug!("try_index_step: success, using overloaded indexing");
let method = self.register_infer_ok_obligations(ok); let method = self.register_infer_ok_obligations(ok);
let mut adjustments = autoderef.adjust_steps(lvalue_pref); let mut adjustments = autoderef.adjust_steps(needs);
if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty { if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
adjustments.push(Adjustment { adjustments.push(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)), kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)),
@ -2305,7 +2373,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.apply_adjustments(base_expr, adjustments); self.apply_adjustments(base_expr, adjustments);
self.write_method_call(expr.hir_id, method); self.write_method_call(expr.hir_id, method);
(input_ty, self.make_overloaded_lvalue_return_type(method).ty) (input_ty, self.make_overloaded_place_return_type(method).ty)
}); });
if result.is_some() { if result.is_some() {
return result; return result;
@ -2315,45 +2383,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
None None
} }
fn resolve_lvalue_op(&self, op: LvalueOp, is_mut: bool) -> (Option<DefId>, Symbol) { fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option<DefId>, Symbol) {
let (tr, name) = match (op, is_mut) { let (tr, name) = match (op, is_mut) {
(LvalueOp::Deref, false) => (PlaceOp::Deref, false) =>
(self.tcx.lang_items().deref_trait(), "deref"), (self.tcx.lang_items().deref_trait(), "deref"),
(LvalueOp::Deref, true) => (PlaceOp::Deref, true) =>
(self.tcx.lang_items().deref_mut_trait(), "deref_mut"), (self.tcx.lang_items().deref_mut_trait(), "deref_mut"),
(LvalueOp::Index, false) => (PlaceOp::Index, false) =>
(self.tcx.lang_items().index_trait(), "index"), (self.tcx.lang_items().index_trait(), "index"),
(LvalueOp::Index, true) => (PlaceOp::Index, true) =>
(self.tcx.lang_items().index_mut_trait(), "index_mut"), (self.tcx.lang_items().index_mut_trait(), "index_mut"),
}; };
(tr, Symbol::intern(name)) (tr, Symbol::intern(name))
} }
fn try_overloaded_lvalue_op(&self, fn try_overloaded_place_op(&self,
span: Span, span: Span,
base_ty: Ty<'tcx>, base_ty: Ty<'tcx>,
arg_tys: &[Ty<'tcx>], arg_tys: &[Ty<'tcx>],
lvalue_pref: LvaluePreference, needs: Needs,
op: LvalueOp) op: PlaceOp)
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> -> Option<InferOk<'tcx, MethodCallee<'tcx>>>
{ {
debug!("try_overloaded_lvalue_op({:?},{:?},{:?},{:?})", debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})",
span, span,
base_ty, base_ty,
lvalue_pref, needs,
op); op);
// Try Mut first, if preferred. // Try Mut first, if needed.
let (mut_tr, mut_op) = self.resolve_lvalue_op(op, true); let (mut_tr, mut_op) = self.resolve_place_op(op, true);
let method = match (lvalue_pref, mut_tr) { let method = match (needs, mut_tr) {
(PreferMutLvalue, Some(trait_did)) => { (Needs::MutPlace, Some(trait_did)) => {
self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys)) self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys))
} }
_ => None, _ => None,
}; };
// Otherwise, fall back to the immutable version. // Otherwise, fall back to the immutable version.
let (imm_tr, imm_op) = self.resolve_lvalue_op(op, false); let (imm_tr, imm_op) = self.resolve_place_op(op, false);
let method = match (method, imm_tr) { let method = match (method, imm_tr) {
(None, Some(trait_did)) => { (None, Some(trait_did)) => {
self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys)) self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys))
@ -2738,18 +2806,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn check_expr_coercable_to_type(&self, fn check_expr_coercable_to_type(&self,
expr: &'gcx hir::Expr, expr: &'gcx hir::Expr,
expected: Ty<'tcx>) -> Ty<'tcx> { expected: Ty<'tcx>) -> Ty<'tcx> {
self.check_expr_coercable_to_type_with_lvalue_pref(expr, expected, NoPreference) self.check_expr_coercable_to_type_with_needs(expr, expected, Needs::None)
} }
fn check_expr_coercable_to_type_with_lvalue_pref(&self, fn check_expr_coercable_to_type_with_needs(&self,
expr: &'gcx hir::Expr, expr: &'gcx hir::Expr,
expected: Ty<'tcx>, expected: Ty<'tcx>,
lvalue_pref: LvaluePreference) needs: Needs)
-> Ty<'tcx> { -> Ty<'tcx> {
let ty = self.check_expr_with_expectation_and_lvalue_pref( let ty = self.check_expr_with_expectation_and_needs(
expr, expr,
ExpectHasType(expected), ExpectHasType(expected),
lvalue_pref); needs);
self.demand_coerce(expr, ty, expected) self.demand_coerce(expr, ty, expected)
} }
@ -2761,16 +2829,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn check_expr_with_expectation(&self, fn check_expr_with_expectation(&self,
expr: &'gcx hir::Expr, expr: &'gcx hir::Expr,
expected: Expectation<'tcx>) -> Ty<'tcx> { expected: Expectation<'tcx>) -> Ty<'tcx> {
self.check_expr_with_expectation_and_lvalue_pref(expr, expected, NoPreference) self.check_expr_with_expectation_and_needs(expr, expected, Needs::None)
} }
fn check_expr(&self, expr: &'gcx hir::Expr) -> Ty<'tcx> { fn check_expr(&self, expr: &'gcx hir::Expr) -> Ty<'tcx> {
self.check_expr_with_expectation(expr, NoExpectation) self.check_expr_with_expectation(expr, NoExpectation)
} }
fn check_expr_with_lvalue_pref(&self, expr: &'gcx hir::Expr, fn check_expr_with_needs(&self, expr: &'gcx hir::Expr, needs: Needs) -> Ty<'tcx> {
lvalue_pref: LvaluePreference) -> Ty<'tcx> { self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs)
self.check_expr_with_expectation_and_lvalue_pref(expr, NoExpectation, lvalue_pref)
} }
// determine the `self` type, using fresh variables for all variables // determine the `self` type, using fresh variables for all variables
@ -2853,9 +2920,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span, span: Span,
args: &'gcx [hir::Expr], args: &'gcx [hir::Expr],
expected: Expectation<'tcx>, expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference) -> Ty<'tcx> { needs: Needs) -> Ty<'tcx> {
let rcvr = &args[0]; let rcvr = &args[0];
let rcvr_t = self.check_expr_with_lvalue_pref(&rcvr, lvalue_pref); let rcvr_t = self.check_expr_with_needs(&rcvr, needs);
// no need to check for bot/err -- callee does that // no need to check for bot/err -- callee does that
let rcvr_t = self.structurally_resolved_type(expr.span, rcvr_t); let rcvr_t = self.structurally_resolved_type(expr.span, rcvr_t);
@ -2965,10 +3032,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Check field access expressions // Check field access expressions
fn check_field(&self, fn check_field(&self,
expr: &'gcx hir::Expr, expr: &'gcx hir::Expr,
lvalue_pref: LvaluePreference, needs: Needs,
base: &'gcx hir::Expr, base: &'gcx hir::Expr,
field: &Spanned<ast::Name>) -> Ty<'tcx> { field: &Spanned<ast::Name>) -> Ty<'tcx> {
let expr_t = self.check_expr_with_lvalue_pref(base, lvalue_pref); let expr_t = self.check_expr_with_needs(base, needs);
let expr_t = self.structurally_resolved_type(expr.span, let expr_t = self.structurally_resolved_type(expr.span,
expr_t); expr_t);
let mut private_candidate = None; let mut private_candidate = None;
@ -2983,7 +3050,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) { if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) {
let field_ty = self.field_ty(expr.span, field, substs); let field_ty = self.field_ty(expr.span, field, substs);
if field.vis.is_accessible_from(def_scope, self.tcx) { if field.vis.is_accessible_from(def_scope, self.tcx) {
let adjustments = autoderef.adjust_steps(lvalue_pref); let adjustments = autoderef.adjust_steps(needs);
self.apply_adjustments(base, adjustments); self.apply_adjustments(base, adjustments);
autoderef.finalize(); autoderef.finalize();
@ -3102,10 +3169,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Check tuple index expressions // Check tuple index expressions
fn check_tup_field(&self, fn check_tup_field(&self,
expr: &'gcx hir::Expr, expr: &'gcx hir::Expr,
lvalue_pref: LvaluePreference, needs: Needs,
base: &'gcx hir::Expr, base: &'gcx hir::Expr,
idx: codemap::Spanned<usize>) -> Ty<'tcx> { idx: codemap::Spanned<usize>) -> Ty<'tcx> {
let expr_t = self.check_expr_with_lvalue_pref(base, lvalue_pref); let expr_t = self.check_expr_with_needs(base, needs);
let expr_t = self.structurally_resolved_type(expr.span, let expr_t = self.structurally_resolved_type(expr.span,
expr_t); expr_t);
let mut private_candidate = None; let mut private_candidate = None;
@ -3146,7 +3213,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}; };
if let Some(field_ty) = field { if let Some(field_ty) = field {
let adjustments = autoderef.adjust_steps(lvalue_pref); let adjustments = autoderef.adjust_steps(needs);
self.apply_adjustments(base, adjustments); self.apply_adjustments(base, adjustments);
autoderef.finalize(); autoderef.finalize();
return field_ty; return field_ty;
@ -3476,10 +3543,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// Note that inspecting a type's structure *directly* may expose the fact /// Note that inspecting a type's structure *directly* may expose the fact
/// that there are actually multiple representations for `TyError`, so avoid /// that there are actually multiple representations for `TyError`, so avoid
/// that when err needs to be handled differently. /// that when err needs to be handled differently.
fn check_expr_with_expectation_and_lvalue_pref(&self, fn check_expr_with_expectation_and_needs(&self,
expr: &'gcx hir::Expr, expr: &'gcx hir::Expr,
expected: Expectation<'tcx>, expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference) -> Ty<'tcx> { needs: Needs) -> Ty<'tcx> {
debug!(">> typechecking: expr={:?} expected={:?}", debug!(">> typechecking: expr={:?} expected={:?}",
expr, expected); expr, expected);
@ -3492,7 +3559,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.diverges.set(Diverges::Maybe); self.diverges.set(Diverges::Maybe);
self.has_errors.set(false); self.has_errors.set(false);
let ty = self.check_expr_kind(expr, expected, lvalue_pref); let ty = self.check_expr_kind(expr, expected, needs);
// Warn for non-block expressions with diverging children. // Warn for non-block expressions with diverging children.
match expr.node { match expr.node {
@ -3526,7 +3593,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn check_expr_kind(&self, fn check_expr_kind(&self,
expr: &'gcx hir::Expr, expr: &'gcx hir::Expr,
expected: Expectation<'tcx>, expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference) -> Ty<'tcx> { needs: Needs) -> Ty<'tcx> {
let tcx = self.tcx; let tcx = self.tcx;
let id = expr.id; let id = expr.id;
match expr.node { match expr.node {
@ -3560,22 +3627,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
NoExpectation NoExpectation
} }
}; };
let lvalue_pref = match unop { let needs = match unop {
hir::UnDeref => lvalue_pref, hir::UnDeref => needs,
_ => NoPreference _ => Needs::None
}; };
let mut oprnd_t = self.check_expr_with_expectation_and_lvalue_pref(&oprnd, let mut oprnd_t = self.check_expr_with_expectation_and_needs(&oprnd,
expected_inner, expected_inner,
lvalue_pref); needs);
if !oprnd_t.references_error() { if !oprnd_t.references_error() {
oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t); oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
match unop { match unop {
hir::UnDeref => { hir::UnDeref => {
if let Some(mt) = oprnd_t.builtin_deref(true, NoPreference) { if let Some(mt) = oprnd_t.builtin_deref(true) {
oprnd_t = mt.ty; oprnd_t = mt.ty;
} else if let Some(ok) = self.try_overloaded_deref( } else if let Some(ok) = self.try_overloaded_deref(
expr.span, oprnd_t, lvalue_pref) { expr.span, oprnd_t, needs) {
let method = self.register_infer_ok_obligations(ok); let method = self.register_infer_ok_obligations(ok);
if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty { if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
self.apply_adjustments(oprnd, vec![Adjustment { self.apply_adjustments(oprnd, vec![Adjustment {
@ -3583,7 +3650,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
target: method.sig.inputs()[0] target: method.sig.inputs()[0]
}]); }]);
} }
oprnd_t = self.make_overloaded_lvalue_return_type(method).ty; oprnd_t = self.make_overloaded_place_return_type(method).ty;
self.write_method_call(expr.hir_id, method); self.write_method_call(expr.hir_id, method);
} else { } else {
type_error_struct!(tcx.sess, expr.span, oprnd_t, E0614, type_error_struct!(tcx.sess, expr.span, oprnd_t, E0614,
@ -3614,8 +3681,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| {
match ty.sty { match ty.sty {
ty::TyRef(_, ref mt) | ty::TyRawPtr(ref mt) => { ty::TyRef(_, ref mt) | ty::TyRawPtr(ref mt) => {
if self.tcx.expr_is_lval(&oprnd) { if self.is_place_expr(&oprnd) {
// Lvalues may legitimately have unsized types. // Places may legitimately have unsized types.
// For example, dereferences of a fat pointer and // For example, dereferences of a fat pointer and
// the last field of a struct can be unsized. // the last field of a struct can be unsized.
ExpectHasType(mt.ty) ExpectHasType(mt.ty)
@ -3626,8 +3693,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => NoExpectation _ => NoExpectation
} }
}); });
let lvalue_pref = LvaluePreference::from_mutbl(mutbl); let needs = Needs::maybe_mut_place(mutbl);
let ty = self.check_expr_with_expectation_and_lvalue_pref(&oprnd, hint, lvalue_pref); let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs);
let tm = ty::TypeAndMut { ty: ty, mutbl: mutbl }; let tm = ty::TypeAndMut { ty: ty, mutbl: mutbl };
if tm.ty.references_error() { if tm.ty.references_error() {
@ -3771,7 +3838,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
tcx.types.never tcx.types.never
} }
hir::ExprAssign(ref lhs, ref rhs) => { hir::ExprAssign(ref lhs, ref rhs) => {
let lhs_ty = self.check_expr_with_lvalue_pref(&lhs, PreferMutLvalue); let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty); let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty);
@ -3783,7 +3850,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => { _ => {
// Only check this if not in an `if` condition, as the // Only check this if not in an `if` condition, as the
// mistyped comparison help is more appropriate. // mistyped comparison help is more appropriate.
if !self.tcx.expr_is_lval(&lhs) { if !self.is_place_expr(&lhs) {
struct_span_err!(self.tcx.sess, expr.span, E0070, struct_span_err!(self.tcx.sess, expr.span, E0070,
"invalid left-hand side expression") "invalid left-hand side expression")
.span_label(expr.span, "left-hand of expression not valid") .span_label(expr.span, "left-hand of expression not valid")
@ -3872,7 +3939,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.check_call(expr, &callee, args, expected) self.check_call(expr, &callee, args, expected)
} }
hir::ExprMethodCall(ref segment, span, ref args) => { hir::ExprMethodCall(ref segment, span, ref args) => {
self.check_method_call(expr, segment, span, args, expected, lvalue_pref) self.check_method_call(expr, segment, span, args, expected, needs)
} }
hir::ExprCast(ref e, ref t) => { hir::ExprCast(ref e, ref t) => {
// Find the type of `e`. Supply hints based on the type we are casting to, // Find the type of `e`. Supply hints based on the type we are casting to,
@ -4015,13 +4082,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.check_expr_struct(expr, expected, qpath, fields, base_expr) self.check_expr_struct(expr, expected, qpath, fields, base_expr)
} }
hir::ExprField(ref base, ref field) => { hir::ExprField(ref base, ref field) => {
self.check_field(expr, lvalue_pref, &base, field) self.check_field(expr, needs, &base, field)
} }
hir::ExprTupField(ref base, idx) => { hir::ExprTupField(ref base, idx) => {
self.check_tup_field(expr, lvalue_pref, &base, idx) self.check_tup_field(expr, needs, &base, idx)
} }
hir::ExprIndex(ref base, ref idx) => { hir::ExprIndex(ref base, ref idx) => {
let base_t = self.check_expr_with_lvalue_pref(&base, lvalue_pref); let base_t = self.check_expr_with_needs(&base, needs);
let idx_t = self.check_expr(&idx); let idx_t = self.check_expr(&idx);
if base_t.references_error() { if base_t.references_error() {
@ -4030,7 +4097,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
idx_t idx_t
} else { } else {
let base_t = self.structurally_resolved_type(expr.span, base_t); let base_t = self.structurally_resolved_type(expr.span, base_t);
match self.lookup_indexing(expr, base, base_t, idx_t, lvalue_pref) { match self.lookup_indexing(expr, base, base_t, idx_t, needs) {
Some((index_ty, element_ty)) => { Some((index_ty, element_ty)) => {
self.demand_coerce(idx, idx_t, index_ty); self.demand_coerce(idx, idx_t, index_ty);
element_ty element_ty
@ -4178,9 +4245,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// ref mut, for soundness (issue #23116). In particular, in // ref mut, for soundness (issue #23116). In particular, in
// the latter case, we need to be clear that the type of the // the latter case, we need to be clear that the type of the
// referent for the reference that results is *equal to* the // referent for the reference that results is *equal to* the
// type of the lvalue it is referencing, and not some // type of the place it is referencing, and not some
// supertype thereof. // supertype thereof.
let init_ty = self.check_expr_with_lvalue_pref(init, LvaluePreference::from_mutbl(m)); let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
self.demand_eqtype(init.span, local_ty, init_ty); self.demand_eqtype(init.span, local_ty, init_ty);
init_ty init_ty
} else { } else {
@ -5023,7 +5090,7 @@ pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let lifetime_count = generics.lifetimes().count(); let lifetime_count = generics.lifetimes().count();
for leaf_ty in ty.walk() { for leaf_ty in ty.walk() {
if let ty::TyParam(ParamTy {idx, ..}) = leaf_ty.sty { if let ty::TyParam(ty::ParamTy {idx, ..}) = leaf_ty.sty {
debug!("Found use of ty param num {}", idx); debug!("Found use of ty param num {}", idx);
tps_used[idx as usize - lifetime_count] = true; tps_used[idx as usize - lifetime_count] = true;
} else if let ty::TyError = leaf_ty.sty { } else if let ty::TyError = leaf_ty.sty {

View File

@ -10,9 +10,9 @@
//! Code related to processing overloaded binary and unary operators. //! Code related to processing overloaded binary and unary operators.
use super::FnCtxt; use super::{FnCtxt, Needs};
use super::method::MethodCallee; use super::method::MethodCallee;
use rustc::ty::{self, Ty, TypeFoldable, NoPreference, PreferMutLvalue, TypeVariants}; use rustc::ty::{self, Ty, TypeFoldable, TypeVariants};
use rustc::ty::TypeVariants::{TyStr, TyRef}; use rustc::ty::TypeVariants::{TyStr, TyRef};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
use rustc::infer::type_variable::TypeVariableOrigin; use rustc::infer::type_variable::TypeVariableOrigin;
@ -40,10 +40,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
return_ty return_ty
}; };
let tcx = self.tcx; if !self.is_place_expr(lhs_expr) {
if !tcx.expr_is_lval(lhs_expr) {
struct_span_err!( struct_span_err!(
tcx.sess, lhs_expr.span, self.tcx.sess, lhs_expr.span,
E0067, "invalid left-hand side expression") E0067, "invalid left-hand side expression")
.span_label( .span_label(
lhs_expr.span, lhs_expr.span,
@ -166,18 +165,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
op, op,
is_assign); is_assign);
let lhs_pref = match is_assign { let lhs_needs = match is_assign {
IsAssign::Yes => PreferMutLvalue, IsAssign::Yes => Needs::MutPlace,
IsAssign::No => NoPreference IsAssign::No => Needs::None
}; };
// Find a suitable supertype of the LHS expression's type, by coercing to // Find a suitable supertype of the LHS expression's type, by coercing to
// a type variable, to pass as the `Self` to the trait, avoiding invariant // a type variable, to pass as the `Self` to the trait, avoiding invariant
// trait matching creating lifetime constraints that are too strict. // trait matching creating lifetime constraints that are too strict.
// E.g. adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result // E.g. adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
// in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
let lhs_ty = self.check_expr_coercable_to_type_with_lvalue_pref(lhs_expr, let lhs_ty = self.check_expr_coercable_to_type_with_needs(lhs_expr,
self.next_ty_var(TypeVariableOrigin::MiscVariable(lhs_expr.span)), self.next_ty_var(TypeVariableOrigin::MiscVariable(lhs_expr.span)),
lhs_pref); lhs_needs);
let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty); let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty);
// NB: As we have not yet type-checked the RHS, we don't have the // NB: As we have not yet type-checked the RHS, we don't have the

View File

@ -732,8 +732,8 @@ and [RFC 809] for more details.
"##, "##,
E0067: r##" E0067: r##"
The left-hand side of a compound assignment expression must be an lvalue The left-hand side of a compound assignment expression must be a place
expression. An lvalue expression represents a memory location and includes expression. A place expression represents a memory location and includes
item paths (ie, namespaced variables), dereferences, indexing expressions, item paths (ie, namespaced variables), dereferences, indexing expressions,
and field references. and field references.
@ -742,7 +742,7 @@ Let's start with some erroneous code examples:
```compile_fail,E0067 ```compile_fail,E0067
use std::collections::LinkedList; use std::collections::LinkedList;
// Bad: assignment to non-lvalue expression // Bad: assignment to non-place expression
LinkedList::new() += 1; LinkedList::new() += 1;
// ... // ...
@ -783,14 +783,14 @@ function's return type and the value being returned.
"##, "##,
E0070: r##" E0070: r##"
The left-hand side of an assignment operator must be an lvalue expression. An The left-hand side of an assignment operator must be a place expression. An
lvalue expression represents a memory location and can be a variable (with place expression represents a memory location and can be a variable (with
optional namespacing), a dereference, an indexing expression or a field optional namespacing), a dereference, an indexing expression or a field
reference. reference.
More details can be found in the [Expressions] section of the Reference. More details can be found in the [Expressions] section of the Reference.
[Expressions]: https://doc.rust-lang.org/reference/expressions.html#lvalues-rvalues-and-temporaries [Expressions]: https://doc.rust-lang.org/reference/expressions.html#places-rvalues-and-temporaries
Now, we can go further. Here are some erroneous code examples: Now, we can go further. Here are some erroneous code examples:
@ -806,7 +806,7 @@ fn some_other_func() {}
fn some_function() { fn some_function() {
SOME_CONST = 14; // error : a constant value cannot be changed! SOME_CONST = 14; // error : a constant value cannot be changed!
1 = 3; // error : 1 isn't a valid lvalue! 1 = 3; // error : 1 isn't a valid place!
some_other_func() = 4; // error : we can't assign value to a function! some_other_func() = 4; // error : we can't assign value to a function!
SomeStruct.x = 12; // error : SomeStruct a structure name but it is used SomeStruct.x = 12; // error : SomeStruct a structure name but it is used
// like a variable! // like a variable!

View File

@ -1439,7 +1439,7 @@ impl<'a> MethodDef<'a> {
&catch_all_substructure); &catch_all_substructure);
// Final wrinkle: the self_args are expressions that deref // Final wrinkle: the self_args are expressions that deref
// down to desired l-values, but we cannot actually deref // down to desired places, but we cannot actually deref
// them when they are fed as r-values into a tuple // them when they are fed as r-values into a tuple
// expression; here add a layer of borrowing, turning // expression; here add a layer of borrowing, turning
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
@ -1516,7 +1516,7 @@ impl<'a> MethodDef<'a> {
} else { } else {
// Final wrinkle: the self_args are expressions that deref // Final wrinkle: the self_args are expressions that deref
// down to desired l-values, but we cannot actually deref // down to desired places, but we cannot actually deref
// them when they are fed as r-values into a tuple // them when they are fed as r-values into a tuple
// expression; here add a layer of borrowing, turning // expression; here add a layer of borrowing, turning
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// check that we link regions in mutable lvalue ops correctly - issue #41774 // check that we link regions in mutable place ops correctly - issue #41774
struct Data(i32); struct Data(i32);

View File

@ -404,9 +404,9 @@ pub fn value_cast(a: u32) -> i32 {
// Change l-value in assignment ------------------------------------------------ // Change place in assignment --------------------------------------------------
#[cfg(cfail1)] #[cfg(cfail1)]
pub fn lvalue() -> i32 { pub fn place() -> i32 {
let mut x = 10; let mut x = 10;
let mut y = 11; let mut y = 11;
x = 9; x = 9;
@ -416,7 +416,7 @@ pub fn lvalue() -> i32 {
#[cfg(not(cfail1))] #[cfg(not(cfail1))]
#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")] #[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")]
#[rustc_clean(cfg="cfail3")] #[rustc_clean(cfg="cfail3")]
pub fn lvalue() -> i32 { pub fn place() -> i32 {
let mut x = 10; let mut x = 10;
let mut y = 11; let mut y = 11;
y = 9; y = 9;

View File

@ -10,7 +10,7 @@
// Test that we don't ICE when translating a generic impl method from // Test that we don't ICE when translating a generic impl method from
// an extern crate that contains a match expression on a local // an extern crate that contains a match expression on a local
// variable lvalue where one of the match case bodies contains an // variable place where one of the match case bodies contains an
// expression that autoderefs through an overloaded generic deref // expression that autoderefs through an overloaded generic deref
// impl. // impl.

View File

@ -11,7 +11,7 @@
// This used to generate invalid IR in that even if we took the // This used to generate invalid IR in that even if we took the
// `false` branch we'd still try to free the Box from the other // `false` branch we'd still try to free the Box from the other
// arm. This was due to treating `*Box::new(9)` as an rvalue datum // arm. This was due to treating `*Box::new(9)` as an rvalue datum
// instead of as an lvalue. // instead of as a place.
fn test(foo: bool) -> u8 { fn test(foo: bool) -> u8 {
match foo { match foo {

View File

@ -8,8 +8,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// Test that an `&mut self` method, when invoked on an lvalue whose // Test that an `&mut self` method, when invoked on a place whose
// type is `&mut [u8]`, passes in a pointer to the lvalue and not a // type is `&mut [u8]`, passes in a pointer to the place and not a
// temporary. Issue #19147. // temporary. Issue #19147.
use std::slice; use std::slice;

View File

@ -41,7 +41,7 @@ fn main() {
// all borrows are extended - nothing has been dropped yet // all borrows are extended - nothing has been dropped yet
assert_eq!(get(), vec![]); assert_eq!(get(), vec![]);
} }
// in a let-statement, extended lvalues are dropped // in a let-statement, extended places are dropped
// *after* the let result (tho they have the same scope // *after* the let result (tho they have the same scope
// as far as scope-based borrowck goes). // as far as scope-based borrowck goes).
assert_eq!(get(), vec![0, 2, 3, 1]); assert_eq!(get(), vec![0, 2, 3, 1]);

View File

@ -40,6 +40,6 @@ fn main() {
assert_eq!(b, 1: u16); assert_eq!(b, 1: u16);
let mut v = Vec::new(); let mut v = Vec::new();
v: Vec<u8> = vec![1, 2, 3]; // Lvalue type ascription v: Vec<u8> = vec![1, 2, 3]; // Place expression type ascription
assert_eq!(v, [1u8, 2, 3]); assert_eq!(v, [1u8, 2, 3]);
} }

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
macro_rules! not_an_lvalue { macro_rules! not_a_place {
($thing:expr) => { ($thing:expr) => {
$thing = 42; $thing = 42;
//~^ ERROR invalid left-hand side expression //~^ ERROR invalid left-hand side expression
@ -16,5 +16,5 @@ macro_rules! not_an_lvalue {
} }
fn main() { fn main() {
not_an_lvalue!(99); not_a_place!(99);
} }

View File

@ -4,8 +4,8 @@ error[E0070]: invalid left-hand side expression
13 | $thing = 42; 13 | $thing = 42;
| ^^^^^^^^^^^ left-hand of expression not valid | ^^^^^^^^^^^ left-hand of expression not valid
... ...
19 | not_an_lvalue!(99); 19 | not_a_place!(99);
| ------------------- in this macro invocation | ----------------- in this macro invocation
error: aborting due to previous error error: aborting due to previous error