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) => {
// see comment on lvalues in
// propagate_through_lvalue_components()
let succ = self.write_lvalue(&l, succ, ACC_WRITE);
let succ = self.propagate_through_lvalue_components(&l, succ);
// see comment on places in
// propagate_through_place_components()
let succ = self.write_place(&l, succ, ACC_WRITE);
let succ = self.propagate_through_place_components(&l, 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);
self.propagate_through_expr(&r, succ)
} else {
// see comment on lvalues in
// propagate_through_lvalue_components()
let succ = self.write_lvalue(&l, succ, ACC_WRITE|ACC_READ);
// see comment on places in
// propagate_through_place_components()
let succ = self.write_place(&l, succ, ACC_WRITE|ACC_READ);
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) => {
let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| {
// see comment on lvalues
// in propagate_through_lvalue_components()
// see comment on places
// in propagate_through_place_components()
if o.is_indirect {
self.propagate_through_expr(output, succ)
} else {
let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
let succ = self.write_lvalue(output, succ, acc);
self.propagate_through_lvalue_components(output, succ)
let succ = self.write_place(output, succ, acc);
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,
succ: LiveNode)
-> LiveNode {
// # Lvalues
// # Places
//
// In general, the full flow graph structure for an
// 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:
//
// Tracked lvalue Untracked lvalue
// Tracked place Untracked place
// ----------------------++-----------------------
// ||
// | || |
@ -1168,7 +1168,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
// (rvalue) || (rvalue)
// | || |
// v || v
// (write of lvalue) || (lvalue components)
// (write of place) || (place components)
// | || |
// v || v
// (succ) || (succ)
@ -1177,25 +1177,25 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
//
// 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
// 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
// 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
// reads for the components---`x` in these two examples. The
// 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
// 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()
fn write_lvalue(&mut self, expr: &Expr, succ: LiveNode, acc: u32)
// see comment on propagate_through_place()
fn write_place(&mut self, expr: &Expr, succ: LiveNode, acc: u32)
-> LiveNode {
match expr.node {
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
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
// non-lvalues occur here, because those are detected in the
// non-places occur here, because those are detected in the
// later pass borrowck.
_ => 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) {
match expr.node {
hir::ExprAssign(ref l, _) => {
this.check_lvalue(&l);
this.check_place(&l);
intravisit::walk_expr(this, expr);
}
hir::ExprAssignOp(_, ref l, _) => {
if !this.tables.is_method_call(expr) {
this.check_lvalue(&l);
this.check_place(&l);
}
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);
}
// Output operands must be lvalues
// Output operands must be places
for (o, output) in ia.outputs.iter().zip(outputs) {
if !o.is_indirect {
this.check_lvalue(output);
this.check_place(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> {
fn check_lvalue(&mut self, expr: &'tcx Expr) {
fn check_place(&mut self, expr: &'tcx Expr) {
match expr.node {
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
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
intravisit::walk_expr(self, expr);
}

View File

@ -26,8 +26,8 @@
//! | E.comp // access to an interior component
//!
//! 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
//! is the address of the lvalue. If Expr is an rvalue, this is the address of
//! address where the result is to be found. If Expr is a place, then this
//! 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.
//!
//! 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 span: Span, // span of same expr/pat
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 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,
// but what we want here is the type of the underlying value being borrowed.
// 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,
None => {
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 {
hir::ExprUnary(hir::UnDeref, ref e_base) => {
if self.tables.is_method_call(expr) {
self.cat_overloaded_lvalue(expr, e_base, false)
self.cat_overloaded_place(expr, e_base, false)
} else {
let base_cmt = self.cat_expr(&e_base)?;
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
// is an rvalue. That is what we will be
// dereferencing.
self.cat_overloaded_lvalue(expr, base, true)
self.cat_overloaded_place(expr, base, true)
} else {
let base_cmt = self.cat_expr(&base)?;
self.cat_index(expr, base_cmt, expr_ty, InteriorOffsetKind::Index)
@ -983,27 +983,27 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
ret
}
fn cat_overloaded_lvalue(&self,
fn cat_overloaded_place(&self,
expr: &hir::Expr,
base: &hir::Expr,
implicit: bool)
-> 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
// same region and mutability as the receiver. This holds for
// `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 (region, mutbl) = match base_ty.sty {
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 {
ty: lvalue_ty,
ty: place_ty,
mutbl,
});
@ -1019,7 +1019,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
debug!("cat_deref: base_cmt={:?}", base_cmt);
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,
None => {
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 {
// Maybe non-obvious: copied upvars can only be considered
// non-aliasable in once closures, since any other kind can be
@ -1453,7 +1453,7 @@ impl<'tcx> cmt_<'tcx> {
"static item".to_string()
}
Categorization::Rvalue(..) => {
"non-lvalue".to_string()
"non-place".to_string()
}
Categorization::Local(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
// 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`.
//
// A subexpression `<rvalue>` that appears in a let initializer
@ -1283,7 +1283,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
/// | (ET)
/// | <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>,
expr: &hir::Expr,
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
/// 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
/// elaboration:
///
/// ```
/// BB0 {
/// DropAndReplace(LV <- RV, goto BB1, unwind BB2)
/// DropAndReplace(P <- V, goto BB1, unwind BB2)
/// }
/// ```
///
@ -747,15 +747,15 @@ pub enum TerminatorKind<'tcx> {
///
/// ```
/// BB0 {
/// Drop(LV, goto BB1, unwind BB2)
/// Drop(P, goto BB1, unwind BB2)
/// }
/// BB1 {
/// // LV is now unitialized
/// LV <- RV
/// // P is now unitialized
/// P <- V
/// }
/// BB2 {
/// // LV is now unitialized -- its dtor panicked
/// LV <- RV
/// // P is now unitialized -- its dtor panicked
/// P <- V
/// }
/// ```
DropAndReplace {

View File

@ -52,7 +52,7 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
match *elem {
ProjectionElem::Deref => {
let ty = self.to_ty(tcx)
.builtin_deref(true, ty::LvaluePreference::NoPreference)
.builtin_deref(true)
.unwrap_or_else(|| {
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.
MutToConstPointer,
/// Dereference once, producing an lvalue.
/// Dereference once, producing a place.
Deref(Option<OverloadedDeref<'tcx>>),
/// 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::BorrowKind::*;
pub use self::IntVarValue::*;
pub use self::LvaluePreference::*;
pub use self::fold::TypeFoldable;
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 {
pub fn from_mutbl(m: hir::Mutability) -> BorrowKind {
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> {
self.associated_items(id)
.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.
/// Some types---notably unsafe ptrs---can only be dereferenced explicitly.
pub fn builtin_deref(&self, explicit: bool, pref: ty::LvaluePreference)
-> Option<TypeAndMut<'tcx>>
{
pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut<'tcx>> {
match self.sty {
TyAdt(def, _) if def.is_box() => {
Some(TypeAndMut {
ty: self.boxed_ty(),
mutbl: if pref == ty::PreferMutLvalue {
hir::MutMutable
} else {
hir::MutImmutable
},
mutbl: hir::MutImmutable,
})
},
TyRef(_, mt) => Some(mt),

View File

@ -43,14 +43,14 @@ it is safe with respect to the in-scope loans.
# Formal model
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
LV = x | LV.f | *LV
P = x | P.f | *P
```
Here `x` represents some variable, `LV.f` is a field reference,
and `*LV` is a pointer dereference. There is no auto-deref or other
Here `x` represents some variable, `P.f` is a field reference,
and `*P` is a pointer dereference. There is no auto-deref or other
niceties. This means that if you have a type like:
```rust
@ -58,7 +58,7 @@ struct S { f: i32 }
```
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:
@ -99,7 +99,7 @@ this sort of thing.
#### Loans and restrictions
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
expression, it computes a `Loan`, which is a data structure that
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:
```text
LOAN = (LV, LT, MQ, RESTRICTION*)
RESTRICTION = (LV, ACTION*)
LOAN = (P, LT, MQ, RESTRICTION*)
RESTRICTION = (P, ACTION*)
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
list of restrictions. The restrictions indicate actions which, if
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
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;
- `CLAIM` means that the `LV` cannot be borrowed mutably;
- `FREEZE` means that the `LV` cannot be borrowed immutably;
- `MUTATE` means that `P` cannot be assigned to;
- `CLAIM` means that the `P` cannot be borrowed mutably;
- `FREEZE` means that the `P` cannot be borrowed immutably;
Finally, it is never possible to move from an lvalue that appears in a
restriction. This implies that the "empty restriction" `(LV, [])`,
Finally, it is never possible to move from a place that appears in a
restriction. This implies that the "empty restriction" `(P, [])`,
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
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
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
validity tests:
1. `MUTABILITY(LV, MQ)`: The mutability of the reference is
compatible with the mutability of `LV` (i.e., not borrowing immutable
1. `MUTABILITY(P, MQ)`: The mutability of the reference is
compatible with the mutability of `P` (i.e., not borrowing immutable
data as mutable).
2. `ALIASABLE(LV, MQ)`: The aliasability of the reference is
compatible with the aliasability of `LV`. The goal is to prevent
2. `ALIASABLE(P, MQ)`: The aliasability of the reference is
compatible with the aliasability of `P`. The goal is to prevent
`&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.
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
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
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
`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
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
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
MUTABILITY(LV.f, MQ) // M-Field
MUTABILITY(LV, MQ)
MUTABILITY(P.f, MQ) // M-Field
MUTABILITY(P, MQ)
MUTABILITY(*LV, MQ) // M-Deref-Unique
TYPE(LV) = Box<Ty>
MUTABILITY(LV, MQ)
MUTABILITY(*P, MQ) // M-Deref-Unique
TYPE(P) = Box<Ty>
MUTABILITY(P, MQ)
```
### Checking mutability of immutable pointer types
@ -305,8 +305,8 @@ Immutable pointer types like `&T` can only
be borrowed if MQ is immutable:
```text
MUTABILITY(*LV, imm) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty
MUTABILITY(*P, imm) // M-Deref-Borrowed-Imm
TYPE(P) = &Ty
```
### 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:
```text
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty
MUTABILITY(*P, MQ) // M-Deref-Borrowed-Mut
TYPE(P) = &mut Ty
```
## Checking aliasability
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
aliasability of `LV` is compatible with a borrow of mutability `MQ`. The Rust
borrows of aliasable data. The judgement `ALIASABLE(P, MQ)` means the
aliasability of `P` is compatible with a borrow of mutability `MQ`. The Rust
code corresponding to this predicate is the function `check_aliasability()` in
`middle::borrowck::gather_loans`.
@ -340,11 +340,11 @@ the stack frame.
Owned content is aliasable if it is found in an aliasable location:
```text
ALIASABLE(LV.f, MQ) // M-Field
ALIASABLE(LV, MQ)
ALIASABLE(P.f, MQ) // M-Field
ALIASABLE(P, MQ)
ALIASABLE(*LV, MQ) // M-Deref-Unique
ALIASABLE(LV, MQ)
ALIASABLE(*P, MQ) // M-Deref-Unique
ALIASABLE(P, MQ)
```
### Checking aliasability of immutable pointer types
@ -353,8 +353,8 @@ Immutable pointer types like `&T` are aliasable, and hence can only be
borrowed immutably:
```text
ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
TYPE(LV) = &Ty
ALIASABLE(*P, imm) // M-Deref-Borrowed-Imm
TYPE(P) = &Ty
```
### 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:
```text
ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut
TYPE(LV) = &mut Ty
ALIASABLE(*P, MQ) // M-Deref-Borrowed-Mut
TYPE(P) = &mut Ty
```
## Checking lifetime
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.
Formally, we define a predicate `LIFETIME(LV, LT, MQ)`, which states that
"the lvalue `LV` can be safely borrowed for the lifetime `LT` with mutability
Formally, we define a predicate `LIFETIME(P, LT, MQ)`, which states that
"the place `P` can be safely borrowed for the lifetime `LT` with mutability
`MQ`". The Rust code corresponding to this predicate is the module
`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:
```text
LIFETIME(LV.f, LT, MQ) // L-Field
LIFETIME(LV, LT, MQ)
LIFETIME(P.f, LT, MQ) // L-Field
LIFETIME(P, LT, MQ)
LIFETIME(*LV, LT, MQ) // L-Deref-Send
TYPE(LV) = Box<Ty>
LIFETIME(LV, LT, MQ)
LIFETIME(*P, LT, MQ) // L-Deref-Send
TYPE(P) = Box<Ty>
LIFETIME(P, LT, MQ)
```
### Checking lifetime for derefs of references
@ -408,8 +408,8 @@ of the borrow is shorter than the lifetime `LT'` of the pointer
itself:
```text
LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed
TYPE(LV) = &LT' Ty OR &LT' mut Ty
LIFETIME(*P, LT, MQ) // L-Deref-Borrowed
TYPE(P) = &LT' Ty OR &LT' mut Ty
LT <= LT'
```
@ -417,17 +417,17 @@ LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed
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
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
occurring on `LV`, the restrictions `RESTRICTION*` must be respected
occurring on `P`, the restrictions `RESTRICTION*` must be respected
for the lifetime of the loan".
Note that there is an initial set of restrictions: these restrictions
are computed based on the kind of borrow:
```text
&mut LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE)
&LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM)
&mut P => RESTRICTIONS(P, LT, MUTATE|CLAIM|FREEZE)
&P => RESTRICTIONS(P, LT, MUTATE|CLAIM)
```
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:
```text
RESTRICTIONS(LV.f, LT, ACTIONS) = RS, (LV.f, ACTIONS) // R-Field
RESTRICTIONS(LV, LT, ACTIONS) = RS
RESTRICTIONS(P.f, LT, ACTIONS) = RS, (P.f, ACTIONS) // R-Field
RESTRICTIONS(P, LT, ACTIONS) = RS
```
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
owned referent is similar to restricting a field, in that it implies
restrictions on the pointer. However, boxes have an important
twist: if the owner `LV` is mutated, that causes the owned referent
`*LV` to be freed! So whenever an owned referent `*LV` is borrowed, we
must prevent the box `LV` from being mutated, which means
twist: if the owner `P` is mutated, that causes the owned referent
`*P` to be freed! So whenever an owned referent `*P` is borrowed, we
must prevent the box `P` from being mutated, which means
that we always add `MUTATE` and `CLAIM` to the restriction set imposed
on `LV`:
on `P`:
```text
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer
TYPE(LV) = Box<Ty>
RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS
RESTRICTIONS(*P, LT, ACTIONS) = RS, (*P, ACTIONS) // R-Deref-Send-Pointer
TYPE(P) = Box<Ty>
RESTRICTIONS(P, LT, ACTIONS|MUTATE|CLAIM) = RS
```
### 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
the compiler does not prevent you from copying the pointer. This
implies that issuing restrictions is useless. We might prevent the
user from acting on `*LV` itself, but there could be another path
`*LV1` that refers to the exact same memory, and we would not be
user from acting on `*P` itself, but there could be another path
`*P1` that refers to the exact same memory, and we would not be
restricting that path. Therefore, the rule for `&Ty` pointers
always returns an empty set of restrictions, and it only permits
restricting `MUTATE` and `CLAIM` actions:
```text
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed
TYPE(LV) = &LT' Ty
RESTRICTIONS(*P, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed
TYPE(P) = &LT' Ty
LT <= LT' // (1)
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
and valid for the lifetime `'b`. This is reflected in
`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`).
#### Why both `LIFETIME()` and `RESTRICTIONS()`?
@ -612,10 +612,10 @@ while the new claimant is live.
The rule for mutable borrowed pointers is as follows:
```text
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Mut-Borrowed
TYPE(LV) = &LT' mut Ty
RESTRICTIONS(*P, LT, ACTIONS) = RS, (*P, ACTIONS) // R-Deref-Mut-Borrowed
TYPE(P) = &LT' mut Ty
LT <= LT' // (1)
RESTRICTIONS(LV, LT, ACTIONS) = RS // (2)
RESTRICTIONS(P, LT, ACTIONS) = RS // (2)
```
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
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
path.
@ -906,7 +906,7 @@ results of a dataflow computation.
The `MovePath` tree tracks every path that is moved or assigned to.
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`
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
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
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

View File

@ -104,7 +104,7 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
fn scope(&self, cmt: &mc::cmt<'tcx>) -> ty::Region<'tcx> {
//! 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.
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() {
// large arrays of data inserted as constants can take a lot of
// 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.
debug!("skipping loan propagation for {:?} because of no loans", body_id);
return None;
@ -384,9 +384,9 @@ impl ToInteriorKind for mc::InteriorKind {
}
// 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
// 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
// a variant of an enum. For instance in:
// `enum E { X { foo: u32 }, Y { foo: u32 }}`

View File

@ -153,7 +153,7 @@ pub struct Assignment {
/// span of node where assignment occurs
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,
}

View File

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

View File

@ -35,8 +35,8 @@ use dataflow::{do_dataflow, DebugFormatted};
use dataflow::FlowAtLocation;
use dataflow::MoveDataParamEnv;
use dataflow::{DataflowAnalysis, DataflowResultsConsumer};
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
use dataflow::{EverInitializedLvals, MovingOutStatements};
use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use dataflow::{EverInitializedPlaces, MovingOutStatements};
use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex};
use dataflow::{ActiveBorrows, Reservations};
use dataflow::indexes::BorrowIndex;
@ -160,7 +160,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
id,
&attributes,
&dead_unwinds,
MaybeInitializedLvals::new(tcx, mir, &mdpe),
MaybeInitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
));
let flow_uninits = FlowAtLocation::new(do_dataflow(
@ -169,7 +169,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
id,
&attributes,
&dead_unwinds,
MaybeUninitializedLvals::new(tcx, mir, &mdpe),
MaybeUninitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
));
let flow_move_outs = FlowAtLocation::new(do_dataflow(
@ -187,7 +187,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
id,
&attributes,
&dead_unwinds,
EverInitializedLvals::new(tcx, mir, &mdpe),
EverInitializedPlaces::new(tcx, mir, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().inits[i]),
));
@ -607,7 +607,7 @@ enum ArtificialField {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ShallowOrDeep {
/// 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
/// that is shallow is an assignment like `x = ...;`, which would
/// be a *shallow write* of `x`."

View File

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

View File

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

View File

@ -15,7 +15,7 @@ use borrow_check::nll::region_infer::Cause;
use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
use borrow_check::nll::universal_regions::UniversalRegions;
use dataflow::FlowAtLocation;
use dataflow::MaybeInitializedLvals;
use dataflow::MaybeInitializedPlaces;
use dataflow::move_paths::MoveData;
use rustc::hir::def_id::DefId;
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
@ -100,7 +100,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
mir_def_id: DefId,
universal_regions: &UniversalRegions<'tcx>,
liveness: &LivenessResults,
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) -> MirTypeckRegionConstraints<'tcx> {
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);
match *pi {
ProjectionElem::Deref => {
let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference);
let deref_ty = base_ty.builtin_deref(true);
PlaceTy::Ty {
ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
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).
fn is_potential_use(context: PlaceContext) -> bool {
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,
// 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
// 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.
// so it's possible this should activate the place.
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::Store | PlaceContext::Call => false,
// reads of an place *do* activate it
// reads of a place *do* activate it
PlaceContext::Move |
PlaceContext::Copy |
PlaceContext::Drop |

View File

@ -36,7 +36,7 @@ pub use self::storage_liveness::*;
#[allow(dead_code)]
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
/// 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
/// between this data and the data from `MaybeUninitializedLvals` at the
/// between this data and the data from `MaybeUninitializedPlaces` at the
/// corresponding control-flow point.
///
/// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeUninitializedLvals` yields the set of
/// l-values that would require a dynamic drop-flag at that statement.
pub struct MaybeInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
/// between this data and `MaybeUninitializedPlaces` yields the set of
/// places that would require a dynamic drop-flag at that statement.
pub struct MaybeInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'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>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> 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 }
}
/// `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
/// 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
/// between this data and the data from `MaybeInitializedLvals` at the
/// between this data and the data from `MaybeInitializedPlaces` at the
/// corresponding control-flow point.
///
/// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeInitializedLvals` yields the set of
/// l-values that would require a dynamic drop-flag at that statement.
pub struct MaybeUninitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
/// between this data and `MaybeInitializedPlaces` yields the set of
/// places that would require a dynamic drop-flag at that statement.
pub struct MaybeUninitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'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>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> 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 }
}
/// `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
/// for a function.
///
/// 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
/// 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
/// 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
/// of this data.
///
/// 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.
pub struct DefinitelyInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
pub struct DefinitelyInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'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>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> 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 }
}
/// `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
/// 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
/// operation reinitializing that l-value.
/// operation reinitializing that place.
///
/// The resulting dataflow is a more enriched version of
/// `MaybeUninitializedLvals`. Both structures on their own only tell
/// you if an l-value *might* be uninitialized at a given point in the
/// `MaybeUninitializedPlaces`. Both structures on their own only tell
/// you if a place *might* be uninitialized at a given point in the
/// control flow. But `MovingOutStatements` also includes the added
/// data of *which* particular statement causing the deinitialization
/// 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 }
}
/// `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
/// 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 }
/// }
/// ```
pub struct EverInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
pub struct EverInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'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>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> 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 }
}
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,
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,
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,
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;
fn name() -> &'static str { "maybe_init" }
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;
fn name() -> &'static str { "maybe_uninit" }
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;
fn name() -> &'static str { "definite_init" }
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;
fn name() -> &'static str { "ever_init" }
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]
fn join(&self, pred1: usize, pred2: usize) -> usize {
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]
fn join(&self, pred1: usize, pred2: usize) -> usize {
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]
fn join(&self, pred1: usize, pred2: usize) -> usize {
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]
fn join(&self, pred1: usize, pred2: usize) -> usize {
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
// 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]
fn bottom_value() -> bool {
false // bottom = uninitialized
}
}
impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
#[inline]
fn bottom_value() -> bool {
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]
fn bottom_value() -> bool {
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]
fn bottom_value() -> bool {
false // bottom = no initialized variables by default

View File

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

View File

@ -86,7 +86,7 @@ impl MoveOutIndex {
/// It follows a tree structure.
///
/// 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,
/// 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)]
pub struct MovePathLookup<'tcx> {
locals: IndexVec<Local, MovePathIndex>,
@ -247,7 +247,7 @@ pub enum LookupResult {
impl<'tcx> MovePathLookup<'tcx> {
// Unlike the builder `fn move_path_for` below, this lookup
// 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.
pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
match *place {

View File

@ -9,9 +9,9 @@ use interpret::memory::HasMemory;
#[derive(Copy, Clone, Debug)]
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 {
/// 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
/// before ever being dereferenced.
ptr: Pointer,
@ -19,7 +19,7 @@ pub enum Place {
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.
Local { frame: usize, local: mir::Local },
}
@ -33,7 +33,7 @@ pub enum PlaceExtra {
}
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 {
Self::from_primval_ptr(PrimVal::Undef.into(), Align::from_bytes(1, 1).unwrap())
}

View File

@ -9,7 +9,7 @@
// except according to those terms.
use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult};
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use dataflow::{DataflowResults};
use dataflow::{on_all_children_bits, on_all_drop_children_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 flow_inits =
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]));
let flow_uninits =
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]));
ElaborateDropsCtxt {
@ -97,7 +97,7 @@ fn find_dead_unwinds<'a, 'tcx>(
let mut dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let flow_inits =
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]));
for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
let location = match bb_data.terminator().kind {
@ -300,8 +300,8 @@ struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &'a Mir<'tcx>,
env: &'a MoveDataParamEnv<'tcx, 'tcx>,
flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx, 'tcx>>,
flow_uninits: DataflowResults<MaybeUninitializedLvals<'a, 'tcx, 'tcx>>,
flow_inits: DataflowResults<MaybeInitializedPlaces<'a, 'tcx, 'tcx>>,
flow_uninits: DataflowResults<MaybeUninitializedPlaces<'a, 'tcx, 'tcx>>,
drop_flags: FxHashMap<MovePathIndex, Local>,
patch: MirPatch<'tcx>,
}

View File

@ -22,7 +22,7 @@ use dataflow::{do_dataflow, DebugFormatted};
use dataflow::MoveDataParamEnv;
use dataflow::BitDenotation;
use dataflow::DataflowResults;
use dataflow::{DefinitelyInitializedLvals, MaybeInitializedLvals, MaybeUninitializedLvals};
use dataflow::{DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces};
use dataflow::move_paths::{MovePathIndex, LookupResult};
use dataflow::move_paths::{HasMoveData, MoveData};
use dataflow;
@ -50,15 +50,15 @@ impl MirPass for SanityCheck {
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let flow_inits =
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]));
let flow_uninits =
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]));
let flow_def_inits =
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]));
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
/// cur = cur.offset(1)
/// } else {
/// ptr = &mut LV[cur]
/// ptr = &mut P[cur]
/// cur = cur + 1
/// }
/// drop(ptr)
@ -731,7 +731,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
if ptr_based {
let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place));
let tmp = Place::Local(self.new_temp(tmp_ty));
// tmp = &LV;
// tmp = &P;
// cur = tmp as *mut T;
// end = Offset(cur, len);
drop_block_stmts.push(self.assign(&tmp, Rvalue::Ref(

View File

@ -545,7 +545,7 @@ impl<'a, 'tcx> ArgType<'tcx> {
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`.
pub fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type {
self.layout.llvm_type(cx)
@ -674,7 +674,7 @@ impl<'a, 'tcx> FnType<'tcx> {
_ => 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(|| {
bug!("FnType::new_vtable: non-pointer self {:?}", self_arg)
}).ty;

View File

@ -212,7 +212,7 @@ enum Base {
Static(ValueRef)
}
/// An place as seen from a constant.
/// A place as seen from a constant.
#[derive(Copy, Clone)]
struct ConstPlace<'tcx> {
base: Base,
@ -743,7 +743,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
operand.llval
}
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;
let (base, old_info) = if !self.cx.type_is_sized(pointee_ty) {
// Normally, the source is a thin pointer and we are
@ -758,7 +758,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
(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;
let ptr_ty = self.cx.layout_of(unsized_ty).llvm_type(self.cx).ptr_to();
let base = consts::ptrcast(base, ptr_ty);

View File

@ -9,7 +9,6 @@
// except according to those terms.
use llvm::ValueRef;
use rustc::ty;
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
use rustc::mir;
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> {
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;
let (llptr, llextra) = match self.val {
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::type_variable::TypeVariableOrigin;
use rustc::traits::ObligationCauseCode;
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
use check::{FnCtxt, Expectation, Diverges};
use rustc::ty::{self, Ty, TypeFoldable};
use check::{FnCtxt, Expectation, Diverges, Needs};
use check::coercion::CoerceMany;
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 {
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 {
// This is "x = SomeTrait" being reduced from
// "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;
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 {
// ...but otherwise we want to use any supertype of the
// discriminant. This is sort of a workaround, see note (*) in

View File

@ -10,7 +10,7 @@
use astconv::AstConv;
use super::{FnCtxt, LvalueOp};
use super::{FnCtxt, PlaceOp, Needs};
use super::method::MethodCallee;
use rustc::infer::InferOk;
@ -18,7 +18,6 @@ use rustc::session::DiagnosticMessageId;
use rustc::traits;
use rustc::ty::{self, Ty, TraitRef};
use rustc::ty::{ToPredicate, TypeFoldable};
use rustc::ty::{LvaluePreference, NoPreference};
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
use syntax_pos::Span;
@ -85,7 +84,7 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
// Otherwise, deref if type is derefable:
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)
} else {
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.
pub fn adjust_steps(&self, pref: LvaluePreference)
pub fn adjust_steps(&self, needs: Needs)
-> 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>>> {
let mut obligations = vec![];
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty)
.chain(iter::once(self.cur_ty));
let steps: Vec<_> = self.steps.iter().map(|&(source, 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 }| {
obligations.extend(o);
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,
span: Span,
base_ty: Ty<'tcx>,
pref: LvaluePreference)
needs: Needs)
-> 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
// except according to those terms.
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
use super::autoderef::Autoderef;
use super::method::MethodCallee;
use hir::def::Def;
use hir::def_id::{DefId, LOCAL_CRATE};
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 syntax::abi;
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.
match adjusted_ty.sty {
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
let adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference);
let adjustments = autoderef.adjust_steps(Needs::None);
self.apply_adjustments(callee_expr, adjustments);
return Some(CallStep::Builtin(adjusted_ty));
}
@ -113,7 +113,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
infer::FnCall,
&closure_ty)
.0;
let adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference);
let adjustments = autoderef.adjust_steps(Needs::None);
self.record_deferred_call_resolution(def_id, DeferredCallResolution {
call_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)| {
let mut adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference);
let mut adjustments = autoderef.adjust_steps(Needs::None);
adjustments.extend(autoref);
self.apply_adjustments(callee_expr, adjustments);
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
//! we may want to adjust precisely when coercions occur.
use check::{Diverges, FnCtxt};
use check::{Diverges, FnCtxt, Needs};
use rustc::hir;
use rustc::hir::def_id::DefId;
@ -69,8 +69,7 @@ use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::lint;
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
use rustc::ty::{self, LvaluePreference, TypeAndMut,
Ty, ClosureSubsts};
use rustc::ty::{self, TypeAndMut, Ty, ClosureSubsts};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::error::TypeError;
use rustc::ty::relate::RelateResult;
@ -410,9 +409,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
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 }
= autoderef.adjust_steps_as_infer_ok(pref);
= autoderef.adjust_steps_as_infer_ok(needs);
obligations.extend(o);
obligations.extend(autoderef.into_obligations());

View File

@ -11,11 +11,11 @@
use super::{probe, MethodCallee};
use astconv::AstConv;
use check::{FnCtxt, LvalueOp, callee};
use check::{FnCtxt, PlaceOp, callee, Needs};
use hir::def_id::DefId;
use rustc::ty::subst::Substs;
use rustc::traits;
use rustc::ty::{self, LvaluePreference, NoPreference, PreferMutLvalue, Ty};
use rustc::ty::{self, Ty};
use rustc::ty::subst::Subst;
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, OverloadedDeref};
use rustc::ty::fold::TypeFoldable;
@ -136,7 +136,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
};
if let Some(hir::MutMutable) = pick.autoref {
self.convert_lvalue_derefs_to_mutable();
self.convert_place_derefs_to_mutable();
}
ConfirmResult { callee, illegal_sized_bound }
@ -155,7 +155,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
let (_, n) = autoderef.nth(pick.autoderefs).unwrap();
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();
@ -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
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
/// respectively.
fn convert_lvalue_derefs_to_mutable(&self) {
fn convert_place_derefs_to_mutable(&self) {
// Gather up expressions we want to munge.
let mut exprs = Vec::new();
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.
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
// 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.
let mut source = self.node_ty(expr.hir_id);
// Do not mutate adjustments in place, but rather take them,
@ -449,10 +449,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
.adjustments_mut()
.remove(expr.hir_id);
if let Some(mut adjustments) = previous_adjustments {
let pref = LvaluePreference::PreferMutLvalue;
let needs = Needs::MutPlace;
for adjustment in &mut adjustments {
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);
if let ty::TyRef(region, mt) = method.sig.output().sty {
*deref = OverloadedDeref {
@ -470,28 +470,28 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
match expr.node {
hir::ExprIndex(ref base_expr, ref index_expr) => {
let index_expr_ty = self.node_ty(index_expr.hir_id);
self.convert_lvalue_op_to_mutable(
LvalueOp::Index, expr, base_expr, &[index_expr_ty]);
self.convert_place_op_to_mutable(
PlaceOp::Index, expr, base_expr, &[index_expr_ty]);
}
hir::ExprUnary(hir::UnDeref, ref base_expr) => {
self.convert_lvalue_op_to_mutable(
LvalueOp::Deref, expr, base_expr, &[]);
self.convert_place_op_to_mutable(
PlaceOp::Deref, expr, base_expr, &[]);
}
_ => {}
}
}
}
fn convert_lvalue_op_to_mutable(&self,
op: LvalueOp,
fn convert_place_op_to_mutable(&self,
op: PlaceOp,
expr: &hir::Expr,
base_expr: &hir::Expr,
arg_tys: &[Ty<'tcx>])
{
debug!("convert_lvalue_op_to_mutable({:?}, {:?}, {:?}, {:?})",
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})",
op, expr, base_expr, arg_tys);
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
}
@ -499,24 +499,24 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
.map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target);
let base_ty = self.resolve_type_vars_if_possible(&base_ty);
// Need to deref because overloaded lvalue ops take self by-reference.
let base_ty = base_ty.builtin_deref(false, NoPreference)
.expect("lvalue op takes something that is not a ref")
// Need to deref because overloaded place ops take self by-reference.
let base_ty = base_ty.builtin_deref(false)
.expect("place op takes something that is not a ref")
.ty;
let method = self.try_overloaded_lvalue_op(
expr.span, base_ty, arg_tys, PreferMutLvalue, op);
let method = self.try_overloaded_place_op(
expr.span, base_ty, arg_tys, Needs::MutPlace, op);
let method = match method {
Some(ok) => self.register_infer_ok_obligations(ok),
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);
let (region, mutbl) = if let ty::TyRef(r, mt) = method.sig.inputs()[0].sty {
(r, mt.mutbl)
} 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
@ -529,7 +529,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
let mut source = base_expr_ty;
for adjustment in &mut adjustments[..] {
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.target = self.tcx.mk_ref(region, ty::TypeAndMut {
ty: source,

View File

@ -95,7 +95,6 @@ use rustc::infer::type_variable::{TypeVariableOrigin};
use rustc::middle::region;
use rustc::ty::subst::{Kind, Subst, Substs};
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::adjustment::{Adjust, Adjustment, AutoBorrow};
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)]
pub struct UnsafetyState {
pub def: ast::NodeId,
@ -410,7 +424,7 @@ impl UnsafetyState {
}
#[derive(Debug, Copy, Clone)]
pub enum LvalueOp {
pub enum PlaceOp {
Deref,
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 `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
/// if **either** the type of value being assigned is `!`, which
/// 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
/// *expression* is `T`. So this function just peels off the return
/// type by one layer to yield `T`.
fn make_overloaded_lvalue_return_type(&self,
fn make_overloaded_place_return_type(&self,
method: MethodCallee<'tcx>)
-> ty::TypeAndMut<'tcx>
{
@ -2219,7 +2287,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let ret_ty = method.sig.output();
// 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,
@ -2227,7 +2295,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
base_expr: &'gcx hir::Expr,
base_ty: Ty<'tcx>,
idx_ty: Ty<'tcx>,
lvalue_pref: LvaluePreference)
needs: Needs)
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
{
// 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 result = None;
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();
result
@ -2252,7 +2320,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr: &hir::Expr,
base_expr: &hir::Expr,
autoderef: &Autoderef<'a, 'gcx, 'tcx>,
lvalue_pref: LvaluePreference,
needs: Needs,
index_ty: 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.
// If some lookup succeeded, install method in table
let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_expr.span));
let method = self.try_overloaded_lvalue_op(
expr.span, self_ty, &[input_ty], lvalue_pref, LvalueOp::Index);
let method = self.try_overloaded_place_op(
expr.span, self_ty, &[input_ty], needs, PlaceOp::Index);
let result = method.map(|ok| {
debug!("try_index_step: success, using overloaded indexing");
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 {
adjustments.push(Adjustment {
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.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() {
return result;
@ -2315,45 +2383,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
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) {
(LvalueOp::Deref, false) =>
(PlaceOp::Deref, false) =>
(self.tcx.lang_items().deref_trait(), "deref"),
(LvalueOp::Deref, true) =>
(PlaceOp::Deref, true) =>
(self.tcx.lang_items().deref_mut_trait(), "deref_mut"),
(LvalueOp::Index, false) =>
(PlaceOp::Index, false) =>
(self.tcx.lang_items().index_trait(), "index"),
(LvalueOp::Index, true) =>
(PlaceOp::Index, true) =>
(self.tcx.lang_items().index_mut_trait(), "index_mut"),
};
(tr, Symbol::intern(name))
}
fn try_overloaded_lvalue_op(&self,
fn try_overloaded_place_op(&self,
span: Span,
base_ty: Ty<'tcx>,
arg_tys: &[Ty<'tcx>],
lvalue_pref: LvaluePreference,
op: LvalueOp)
needs: Needs,
op: PlaceOp)
-> Option<InferOk<'tcx, MethodCallee<'tcx>>>
{
debug!("try_overloaded_lvalue_op({:?},{:?},{:?},{:?})",
debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})",
span,
base_ty,
lvalue_pref,
needs,
op);
// Try Mut first, if preferred.
let (mut_tr, mut_op) = self.resolve_lvalue_op(op, true);
let method = match (lvalue_pref, mut_tr) {
(PreferMutLvalue, Some(trait_did)) => {
// Try Mut first, if needed.
let (mut_tr, mut_op) = self.resolve_place_op(op, true);
let method = match (needs, mut_tr) {
(Needs::MutPlace, Some(trait_did)) => {
self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys))
}
_ => None,
};
// 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) {
(None, Some(trait_did)) => {
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,
expr: &'gcx hir::Expr,
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,
expr: &'gcx hir::Expr,
expected: Ty<'tcx>,
lvalue_pref: LvaluePreference)
-> Ty<'tcx> {
let ty = self.check_expr_with_expectation_and_lvalue_pref(
fn check_expr_coercable_to_type_with_needs(&self,
expr: &'gcx hir::Expr,
expected: Ty<'tcx>,
needs: Needs)
-> Ty<'tcx> {
let ty = self.check_expr_with_expectation_and_needs(
expr,
ExpectHasType(expected),
lvalue_pref);
needs);
self.demand_coerce(expr, ty, expected)
}
@ -2761,16 +2829,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn check_expr_with_expectation(&self,
expr: &'gcx hir::Expr,
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> {
self.check_expr_with_expectation(expr, NoExpectation)
}
fn check_expr_with_lvalue_pref(&self, expr: &'gcx hir::Expr,
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
self.check_expr_with_expectation_and_lvalue_pref(expr, NoExpectation, lvalue_pref)
fn check_expr_with_needs(&self, expr: &'gcx hir::Expr, needs: Needs) -> Ty<'tcx> {
self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs)
}
// determine the `self` type, using fresh variables for all variables
@ -2853,9 +2920,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span,
args: &'gcx [hir::Expr],
expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
needs: Needs) -> Ty<'tcx> {
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
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
fn check_field(&self,
expr: &'gcx hir::Expr,
lvalue_pref: LvaluePreference,
needs: Needs,
base: &'gcx hir::Expr,
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,
expr_t);
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) {
let field_ty = self.field_ty(expr.span, field, substs);
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);
autoderef.finalize();
@ -3102,10 +3169,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Check tuple index expressions
fn check_tup_field(&self,
expr: &'gcx hir::Expr,
lvalue_pref: LvaluePreference,
needs: Needs,
base: &'gcx hir::Expr,
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,
expr_t);
let mut private_candidate = None;
@ -3146,7 +3213,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
};
if let Some(field_ty) = field {
let adjustments = autoderef.adjust_steps(lvalue_pref);
let adjustments = autoderef.adjust_steps(needs);
self.apply_adjustments(base, adjustments);
autoderef.finalize();
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
/// that there are actually multiple representations for `TyError`, so avoid
/// 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,
expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
needs: Needs) -> Ty<'tcx> {
debug!(">> typechecking: expr={:?} expected={:?}",
expr, expected);
@ -3492,7 +3559,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.diverges.set(Diverges::Maybe);
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.
match expr.node {
@ -3526,7 +3593,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn check_expr_kind(&self,
expr: &'gcx hir::Expr,
expected: Expectation<'tcx>,
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
needs: Needs) -> Ty<'tcx> {
let tcx = self.tcx;
let id = expr.id;
match expr.node {
@ -3560,22 +3627,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
NoExpectation
}
};
let lvalue_pref = match unop {
hir::UnDeref => lvalue_pref,
_ => NoPreference
let needs = match unop {
hir::UnDeref => needs,
_ => 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,
lvalue_pref);
needs);
if !oprnd_t.references_error() {
oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
match unop {
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;
} 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);
if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
self.apply_adjustments(oprnd, vec![Adjustment {
@ -3583,7 +3650,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
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);
} else {
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| {
match ty.sty {
ty::TyRef(_, ref mt) | ty::TyRawPtr(ref mt) => {
if self.tcx.expr_is_lval(&oprnd) {
// Lvalues may legitimately have unsized types.
if self.is_place_expr(&oprnd) {
// Places may legitimately have unsized types.
// For example, dereferences of a fat pointer and
// the last field of a struct can be unsized.
ExpectHasType(mt.ty)
@ -3626,8 +3693,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => NoExpectation
}
});
let lvalue_pref = LvaluePreference::from_mutbl(mutbl);
let ty = self.check_expr_with_expectation_and_lvalue_pref(&oprnd, hint, lvalue_pref);
let needs = Needs::maybe_mut_place(mutbl);
let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs);
let tm = ty::TypeAndMut { ty: ty, mutbl: mutbl };
if tm.ty.references_error() {
@ -3771,7 +3838,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
tcx.types.never
}
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);
@ -3783,7 +3850,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => {
// Only check this if not in an `if` condition, as the
// 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,
"invalid left-hand side expression")
.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)
}
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) => {
// 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)
}
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) => {
self.check_tup_field(expr, lvalue_pref, &base, idx)
self.check_tup_field(expr, needs, &base, 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);
if base_t.references_error() {
@ -4030,7 +4097,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
idx_t
} else {
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)) => {
self.demand_coerce(idx, idx_t, index_ty);
element_ty
@ -4178,9 +4245,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// ref mut, for soundness (issue #23116). In particular, in
// the latter case, we need to be clear that the type of 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.
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);
init_ty
} else {
@ -5023,7 +5090,7 @@ pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let lifetime_count = generics.lifetimes().count();
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);
tps_used[idx as usize - lifetime_count] = true;
} else if let ty::TyError = leaf_ty.sty {

View File

@ -10,9 +10,9 @@
//! Code related to processing overloaded binary and unary operators.
use super::FnCtxt;
use super::{FnCtxt, Needs};
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::adjustment::{Adjustment, Adjust, AutoBorrow};
use rustc::infer::type_variable::TypeVariableOrigin;
@ -40,10 +40,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
return_ty
};
let tcx = self.tcx;
if !tcx.expr_is_lval(lhs_expr) {
if !self.is_place_expr(lhs_expr) {
struct_span_err!(
tcx.sess, lhs_expr.span,
self.tcx.sess, lhs_expr.span,
E0067, "invalid left-hand side expression")
.span_label(
lhs_expr.span,
@ -166,18 +165,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
op,
is_assign);
let lhs_pref = match is_assign {
IsAssign::Yes => PreferMutLvalue,
IsAssign::No => NoPreference
let lhs_needs = match is_assign {
IsAssign::Yes => Needs::MutPlace,
IsAssign::No => Needs::None
};
// 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
// 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
// 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)),
lhs_pref);
lhs_needs);
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

View File

@ -732,8 +732,8 @@ and [RFC 809] for more details.
"##,
E0067: r##"
The left-hand side of a compound assignment expression must be an lvalue
expression. An lvalue expression represents a memory location and includes
The left-hand side of a compound assignment expression must be a place
expression. A place expression represents a memory location and includes
item paths (ie, namespaced variables), dereferences, indexing expressions,
and field references.
@ -742,7 +742,7 @@ Let's start with some erroneous code examples:
```compile_fail,E0067
use std::collections::LinkedList;
// Bad: assignment to non-lvalue expression
// Bad: assignment to non-place expression
LinkedList::new() += 1;
// ...
@ -783,14 +783,14 @@ function's return type and the value being returned.
"##,
E0070: r##"
The left-hand side of an assignment operator must be an lvalue expression. An
lvalue expression represents a memory location and can be a variable (with
The left-hand side of an assignment operator must be a place expression. An
place expression represents a memory location and can be a variable (with
optional namespacing), a dereference, an indexing expression or a field
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:
@ -806,7 +806,7 @@ fn some_other_func() {}
fn some_function() {
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!
SomeStruct.x = 12; // error : SomeStruct a structure name but it is used
// like a variable!

View File

@ -1439,7 +1439,7 @@ impl<'a> MethodDef<'a> {
&catch_all_substructure);
// 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
// expression; here add a layer of borrowing, turning
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
@ -1516,7 +1516,7 @@ impl<'a> MethodDef<'a> {
} else {
// 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
// expression; here add a layer of borrowing, turning
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// 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);

View File

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

View File

@ -10,7 +10,7 @@
// Test that we don't ICE when translating a generic impl method from
// 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
// impl.

View File

@ -11,7 +11,7 @@
// 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
// 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 {
match foo {

View File

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

View File

@ -41,7 +41,7 @@ fn main() {
// all borrows are extended - nothing has been dropped yet
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
// as far as scope-based borrowck goes).
assert_eq!(get(), vec![0, 2, 3, 1]);

View File

@ -40,6 +40,6 @@ fn main() {
assert_eq!(b, 1: u16);
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]);
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
macro_rules! not_an_lvalue {
macro_rules! not_a_place {
($thing:expr) => {
$thing = 42;
//~^ ERROR invalid left-hand side expression
@ -16,5 +16,5 @@ macro_rules! not_an_lvalue {
}
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;
| ^^^^^^^^^^^ left-hand of expression not valid
...
19 | not_an_lvalue!(99);
| ------------------- in this macro invocation
19 | not_a_place!(99);
| ----------------- in this macro invocation
error: aborting due to previous error